MPM Documentation Kit v1.3 (CW-034)
MPM Document Standard 1.3, mpmdoc build engine, templates for TN, INS, SOP, BOM, SVY, TAP, DWG, TSB, PRO, SOW and WO (Chinese editions for TN and DWG), glossary, MPM fonts and logos, and the mpm-technical-documents plugin 1.3.0. Worked examples ship in the kit zip on Drive only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWatTMmqmGatgHjDZvWs5b
This commit is contained in:
143 files changed
+4097
No files matched your search
Binary file not shown.
@@ -0,0 +1,44 @@
|
||||
"""TEMPLATE — Bill of Materials (BOM). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
A list of parts to pull, check and pack. Marked INTERNAL USE automatically. 1–2 pages.
|
||||
See MPM_Document_Standard.md §9.4. Reference: BOM-OMT-HW-01 (Omnitrans).
|
||||
|
||||
Order: masthead (automatic) → item sections (doc.bom) → Draft items to confirm (while < 1.0)
|
||||
→ sign-off → Revision history (automatic)
|
||||
Unknown values are TBC (red), never guessed; every TBC appears in the 'items to confirm' note.
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc, TBC
|
||||
|
||||
doc = Doc(
|
||||
number="BOM-MPM-SUBJ-01", rev="0.1", date="2026-11-06",
|
||||
customer=None, subject="Project hardware",
|
||||
title="What Is Being Packed",
|
||||
lede="What the list covers and what to do with it (pull, check off, pack).",
|
||||
meta=[("Customer", "Customer"), ("Prepared by", "Jason · MPM")],
|
||||
slug="Customer_Short-Title_BOM",
|
||||
history=[("0.1", "2026-11-06", "Initial draft.")],
|
||||
)
|
||||
|
||||
doc.add(
|
||||
# (item, specification, qty, uom[, part no.]) · group by category · subtotal per section
|
||||
doc.bom([
|
||||
("Fasteners", [
|
||||
("Hex bolt, M8 × 30 mm", f"M8 × 1.25 · 30 mm L · hex head<br>Material/grade {TBC}", 150, "ea"),
|
||||
("Flange nut, M8", "M8 × 1.25 · serrated flange", 150, "ea", "PN-0000"),
|
||||
], "Fastener subtotal"),
|
||||
("Consumables", [
|
||||
("Sealing tape", f"Foil tape<br>Width × length {TBC}", 6, "roll"),
|
||||
], "Consumables subtotal"),
|
||||
]),
|
||||
doc.note("Draft · items to confirm before release", [
|
||||
"<b>Material and grade</b> for all fasteners.",
|
||||
"<b>Tape size</b> (width × roll length).",
|
||||
], tone="warn"),
|
||||
doc.signoff(("Pulled by", "Checked by", "Date")),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,50 @@
|
||||
"""TEMPLATE — Drawing sheet (DWG), 11×17 landscape. Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
Mechanical, mounting, cable or fit-out drawings someone will fabricate or approve from.
|
||||
See MPM_Document_Standard.md §9.7. Each dwg.sheet(...) is one page; the title block, sheet numbers,
|
||||
zone grid, revision history and confidentiality strip are automatic.
|
||||
|
||||
View area = 6 columns × 4 rows. Place each view with area="row-start / col-start / row-end / col-end"
|
||||
(e.g. "1 / 1 / 5 / 3" = full height, left third).
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Drawing
|
||||
from figs import FIGS
|
||||
|
||||
F = {k: f() for k, f in FIGS.items()}
|
||||
|
||||
dwg = Drawing(
|
||||
number="DWG-MPM-SUBJ-01", rev="0.1", date="2026-11-06",
|
||||
title="Wall-Plate Sign Bracket",
|
||||
subtitle="Mounting bracket, general arrangement",
|
||||
project="Project name", client="Customer", location="City, ST",
|
||||
discipline="Mechanical drawing · Mounting",
|
||||
drawn="JS", checked="—", approved="—",
|
||||
slug="Customer_Sign-Bracket_GA",
|
||||
history=[("0.1", "2026-11-06", "Initial issue for review.")],
|
||||
)
|
||||
|
||||
dwg.sheet(
|
||||
views=[
|
||||
dwg.view(F["elevation"], 1, "Elevation · wall plate", "1:4", area="1 / 1 / 5 / 3"),
|
||||
dwg.view(F["section"], 2, "Section A · side", "1:4", area="1 / 3 / 4 / 7"),
|
||||
dwg.view(F["detail"], 3, "Detail B · anchor", "1:1", area="4 / 3 / 5 / 5"),
|
||||
],
|
||||
schedule=[ # (tag, designation, description, qty)
|
||||
("B-1", "Wall plate", "6 mm HDG steel, 200 × 300", 1),
|
||||
("A-1", "Arm", "60 × 60 × 4 SHS, HDG, 400 long", 1),
|
||||
("F-1", "Anchor", "M12 wedge anchor, A4 stainless", 4),
|
||||
],
|
||||
notes=[
|
||||
"All dimensions in mm unless noted. Do not scale from drawing.",
|
||||
"Hot-dip galvanize all steel after fabrication.",
|
||||
"Confirm wall / post construction before drilling.",
|
||||
"Sign and its hanging hardware supplied by others.",
|
||||
],
|
||||
scale="As shown",
|
||||
)
|
||||
|
||||
dwg.build()
|
||||
@@ -0,0 +1,62 @@
|
||||
"""TEMPLATE — Drawing sheet, Simplified Chinese edition (图纸 · 中文版).
|
||||
Copy next to the English build.py in <PROJECT>/source/. See MPM_Document_Standard.md §12.
|
||||
|
||||
Rules:
|
||||
* Same number, slug, revision, sheets and views as build.py; figs.py is shared. Build English first:
|
||||
the engine refuses to build the _ZH file without the English PDF at this revision.
|
||||
* Dimensions mm first, imperial in [ ]: if figs.py formats dimensions through a function F, replace it
|
||||
here before drawing the figures (figs functions read F at call time).
|
||||
* Figure text goes through i18n.Translator (TERMS, then _STANDARDS/glossary_zh.md); tr.check() fails the
|
||||
build on any English left. Tags (B-1), part numbers, standards and place names stay in Latin script.
|
||||
* The frame (title block, schedule and notes headings, dates) is translated by the engine; the
|
||||
"英文版为准" note is added to each sheet's notes unless you place it yourself.
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
sys.path.insert(0, str(HERE))
|
||||
|
||||
from mpmdoc import Drawing, i18n
|
||||
from figs import FIGS
|
||||
|
||||
TERMS = { # this document's figure phrases (single terms: glossary)
|
||||
"WALL / POST": "墙体 / 立柱",
|
||||
"SIGN (BY OTHERS)": "显示屏(他方供货)",
|
||||
"M12 ANCHOR · SEE SCHEDULE": "M12 锚栓 · 见部件表",
|
||||
}
|
||||
tr = i18n.Translator(TERMS)
|
||||
F = {k: tr.svg(f()) for k, f in FIGS.items()}
|
||||
tr.check()
|
||||
|
||||
dwg = Drawing(
|
||||
number="DWG-MPM-SUBJ-01", rev="0.1", date="2026-11-06", lang="zh",
|
||||
title="墙板式显示屏支架",
|
||||
subtitle="安装支架总图",
|
||||
project="项目名称", client="Customer", location="城市, 州",
|
||||
discipline="机械图纸 · 安装",
|
||||
drawn="JS", checked="—", approved="—",
|
||||
slug="Customer_Sign-Bracket_GA", # identical to the English slug
|
||||
history=[("0.1", "2026-11-06", "首次发布,供审核。")],
|
||||
)
|
||||
|
||||
dwg.sheet(
|
||||
views=[
|
||||
dwg.view(F["elevation"], 1, "立面 · 墙板", "1:4", area="1 / 1 / 5 / 3"),
|
||||
dwg.view(F["section"], 2, "剖面 A · 侧视", "1:4", area="1 / 3 / 4 / 7"),
|
||||
dwg.view(F["detail"], 3, "详图 B · 锚栓", "1:1", area="4 / 3 / 5 / 5"),
|
||||
],
|
||||
schedule=[
|
||||
("B-1", "墙板", "6 mm 热镀锌钢板,200 × 300", 1),
|
||||
("A-1", "悬臂", "60 × 60 × 4 方钢管,热镀锌,长 400", 1),
|
||||
("F-1", "锚栓", "M12 膨胀锚栓,A4 不锈钢", 4),
|
||||
],
|
||||
notes=[
|
||||
"除注明外尺寸单位为 mm。请勿从图纸量取尺寸。",
|
||||
"所有钢件加工完成后热镀锌。",
|
||||
"钻孔前确认墙体 / 立柱结构。",
|
||||
"显示屏及其吊挂件由他方供货。",
|
||||
],
|
||||
scale="As shown",
|
||||
)
|
||||
|
||||
dwg.build()
|
||||
@@ -0,0 +1,78 @@
|
||||
"""TEMPLATE figures for a DWG sheet: a generic wall-plate sign bracket in three views.
|
||||
Drafting conventions: ink linework, centrelines in Dark Gold (long-dash-dot), hidden lines dashed,
|
||||
dimensions in mm (JetBrains Mono), schedule tags as gold bubbles. One mm = SCALE px."""
|
||||
from mpmdoc.figkit import T, svg_open, dim_h, dim_v, tag, INK, KICK, MUTE, CREAM
|
||||
|
||||
S = 0.9 # px per mm in these views
|
||||
PLATE_W, PLATE_H, T_PL = 200, 300, 10
|
||||
HOLE_X, HOLE_Y, HOLE_D = 150, 250, 14
|
||||
ARM_L, ARM_H = 400, 60
|
||||
|
||||
|
||||
def _cl(x1, y1, x2, y2):
|
||||
return f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{KICK}" stroke-width=".8" stroke-dasharray="14 3 2 3"/>'
|
||||
|
||||
|
||||
def elevation():
|
||||
"""Front view of the wall plate with the four anchor holes."""
|
||||
W, H = 360, 420
|
||||
ox, oy = 80, 50
|
||||
pw, ph = PLATE_W * S, PLATE_H * S
|
||||
o = [svg_open(W, H, grid=True)]
|
||||
o.append(f'<rect x="{ox}" y="{oy}" width="{pw}" height="{ph}" fill="{CREAM}" stroke="{INK}" stroke-width="1.4"/>')
|
||||
cx, cy = ox + pw / 2, oy + ph / 2
|
||||
for dx in (-HOLE_X / 2, HOLE_X / 2):
|
||||
for dy in (-HOLE_Y / 2, HOLE_Y / 2):
|
||||
o.append(f'<circle cx="{cx + dx * S}" cy="{cy + dy * S}" r="{HOLE_D / 2 * S}" fill="#fff" stroke="{INK}" stroke-width="1.1"/>')
|
||||
o.append(_cl(cx, oy - 14, cx, oy + ph + 14) + _cl(ox - 14, cy, ox + pw + 14, cy))
|
||||
o.append(f'<rect x="{cx - ARM_H / 2 * S}" y="{cy - ARM_H / 2 * S}" width="{ARM_H * S}" height="{ARM_H * S}" fill="none" stroke="{INK}" stroke-width="1" stroke-dasharray="4 3"/>')
|
||||
o.append(dim_h(ox, ox + pw, oy + ph + 34, f"{PLATE_W}", ext_from=oy + ph))
|
||||
o.append(dim_h(cx - HOLE_X / 2 * S, cx + HOLE_X / 2 * S, oy - 22, f"{HOLE_X}", ext_from=oy + (PLATE_H - HOLE_Y) / 2 * S))
|
||||
o.append(dim_v(ox - 30, oy, oy + ph, f"{PLATE_H}", ext_from=ox))
|
||||
o.append(dim_v(ox + pw + 30, cy - HOLE_Y / 2 * S, cy + HOLE_Y / 2 * S, f"{HOLE_Y}", ext_from=cx + HOLE_X / 2 * S))
|
||||
o.append(tag(ox + pw + 52, oy + 18, "B-1"))
|
||||
o.append(T(ox + pw - 4, oy + ph - 8, f"4× Ø{HOLE_D} THRU", size=7.5, w=700, anchor="end", ls=.6))
|
||||
o.append("</svg>")
|
||||
return "".join(o)
|
||||
|
||||
|
||||
def section():
|
||||
"""Side section: plate, arm and sign hanging point."""
|
||||
W, H = 640, 420
|
||||
ox, oy = 70, 50
|
||||
ph = PLATE_H * S
|
||||
o = [svg_open(W, H, grid=True)]
|
||||
o.append(f'<rect x="{ox - 18}" y="{oy - 30}" width="18" height="{ph + 60}" fill="url(#fh)" stroke="{INK}" stroke-width="1"/>')
|
||||
o.append(T(ox - 26, oy - 36, "WALL / POST", size=7, w=700, fill=MUTE, ls=1))
|
||||
o.append(f'<rect x="{ox}" y="{oy}" width="{T_PL * S}" height="{ph}" fill="{CREAM}" stroke="{INK}" stroke-width="1.4"/>')
|
||||
ay = oy + ph / 2 - ARM_H / 2 * S
|
||||
o.append(f'<rect x="{ox + T_PL * S}" y="{ay}" width="{ARM_L * S}" height="{ARM_H * S}" fill="#fff" stroke="{INK}" stroke-width="1.4"/>')
|
||||
ex = ox + T_PL * S + ARM_L * S
|
||||
o.append(f'<line x1="{ex - 30}" y1="{ay + ARM_H * S}" x2="{ex - 30}" y2="{ay + ARM_H * S + 60}" stroke="{INK}" stroke-width="1.2"/>')
|
||||
o.append(f'<rect x="{ex - 130}" y="{ay + ARM_H * S + 60}" width="200" height="70" rx="3" fill="{INK}"/>')
|
||||
o.append(T(ex - 30, ay + ARM_H * S + 100, "SIGN (BY OTHERS)", size=7.5, w=700, fill="#fff", anchor="middle", ls=1))
|
||||
o.append(_cl(ox - 30, oy + ph / 2, ex + 30, oy + ph / 2))
|
||||
o.append(dim_h(ox, ex, oy - 16, f"{T_PL + ARM_L}", ext_from=ay))
|
||||
o.append(dim_v(ex + 26, ay, ay + ARM_H * S, f"{ARM_H}", ext_from=ex))
|
||||
o.append(tag(ox + 40, ay - 16, "A-1") + tag(ox + 4, oy + ph + 16, "B-1"))
|
||||
o.append("</svg>")
|
||||
return "".join(o)
|
||||
|
||||
|
||||
def detail():
|
||||
"""Detail: anchor at the plate, enlarged."""
|
||||
W, H = 300, 220
|
||||
o = [svg_open(W, H, grid=True)]
|
||||
o.append(f'<rect x="40" y="30" width="40" height="160" fill="url(#fh)" stroke="{INK}"/>')
|
||||
o.append(f'<rect x="80" y="40" width="22" height="140" fill="{CREAM}" stroke="{INK}" stroke-width="1.3"/>')
|
||||
o.append(f'<rect x="20" y="102" width="150" height="16" fill="#fff" stroke="{INK}" stroke-width="1.1"/>')
|
||||
o.append(f'<rect x="102" y="92" width="8" height="36" fill="{CREAM}" stroke="{INK}"/>')
|
||||
o.append(f'<polygon points="110,96 126,96 126,124 110,124" fill="#fff" stroke="{INK}"/>')
|
||||
o.append(_cl(10, 110, 190, 110))
|
||||
o.append(tag(150, 70, "F-1") + f'<line x1="142" y1="76" x2="122" y2="98" stroke="{INK}" stroke-width=".7"/>')
|
||||
o.append(T(150, 200, "M12 ANCHOR · SEE SCHEDULE", size=7, w=700, anchor="middle", fill=MUTE, ls=.8))
|
||||
o.append("</svg>")
|
||||
return "".join(o)
|
||||
|
||||
|
||||
FIGS = {"elevation": elevation, "section": section, "detail": detail}
|
||||
Binary file not shown.
@@ -0,0 +1,68 @@
|
||||
"""TEMPLATE — Installation Guide (INS). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
Step-by-step physical installation for someone outside MPM (customer facilities, installer, field crew).
|
||||
4–10 pages. See MPM_Document_Standard.md §9.1. Reference: INS-LT-15R-01 (LakeTran).
|
||||
|
||||
Order (do not reorder):
|
||||
masthead (automatic) → Before you start → Process at a glance → Hardware + Tools
|
||||
→ reference figure (template / drill schedule), optional → Walkthrough (one doc.step per step)
|
||||
→ Final check → Revision history (automatic)
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc
|
||||
from figs import FIGS
|
||||
|
||||
F = {k: f() for k, f in FIGS.items()}
|
||||
|
||||
doc = Doc(
|
||||
number="INS-MPM-SUBJ-01", rev="0.1", date="2026-11-06",
|
||||
customer=None, subject="Product",
|
||||
title="Product Name:<br>Mount Type", # the outcome, not "Installation Guide"
|
||||
lede="One sentence per phase of the job: mount it, wire it, hang it, lock it.",
|
||||
meta=[("Audience", "Who does the work"), ("Mount type", "What it mounts to")],
|
||||
slug="Customer_Product_Install",
|
||||
history=[("0.1", "2026-11-06", "Initial draft.")],
|
||||
)
|
||||
|
||||
doc.add(
|
||||
# Safety, permits, who must do what, and anything to confirm BEFORE the first hole. Bold the critical fact.
|
||||
doc.before_you_start([
|
||||
"<b>Confirm the location and mounting height</b> with your MPM project contact before drilling.",
|
||||
"<b>Wear eye protection and gloves.</b>",
|
||||
"<b>Line-voltage wiring must be done by a qualified electrician</b> per NEC and local code.",
|
||||
]),
|
||||
doc.section("Process at a glance"),
|
||||
doc.glance([("Mark<br>holes", "3 marks"), ("Drill", "Ø1/2\""), ("Mount<br>bracket", "3 bolts"),
|
||||
("Wire", "8 splices"), ("Hang<br>& test", "on bracket")]), # 4–8 tiles, ≤ 2 words + detail
|
||||
'<div class="keep">' + doc.two(
|
||||
doc.section("Hardware") + doc.table([("Item", ""), ("Spec", ""), ("Qty", "q")],
|
||||
[["Bracket", '<span class="mono">107 × 465 mm</span>', "1"]], cls="tight"),
|
||||
doc.section("Tools") + doc.table([("Tool", ""), ("Notes", "")], [["Drill", "Corded or 18 V+, low speed"]], cls="tight"),
|
||||
ratio=(11, 9)) + "</div>",
|
||||
|
||||
doc.section("Walkthrough", new_page=True),
|
||||
# One outcome per step. Kicker = the tool / part / place. 3–6 numbered actions. Say what success looks like.
|
||||
doc.step(1, "Layout", "Mark the hole locations", doc.steps([
|
||||
"Hold the bracket against the mounting face at the approved height.",
|
||||
"Set a level on the bracket edge and adjust until it's <b>plumb</b>.",
|
||||
"Mark through holes <b>A</b>, <b>B</b> and <b>C</b>.",
|
||||
]) + doc.note("Check your marks", "A to C should measure <b>312 mm</b>."),
|
||||
doc.figure(F["part"], "Caption: what the reader should see")), # side figure (35%)
|
||||
doc.step(2, "12 V DC · WAGO 221", "Connect power", doc.steps([
|
||||
"Confirm the breaker is <b>off</b>.",
|
||||
"Splice <b>brown</b> to <b>red (+)</b> and <b>blue</b> to <b>black (−)</b>.",
|
||||
]) + doc.note("Check polarity", "Reversing + and − can damage the sign.", tone="warn"),
|
||||
doc.figure(F["part"], "Wide figures go below the text", style="width:45%;margin:0 auto"), wide=True), # wide=True for diagrams
|
||||
|
||||
doc.checklist("Final check", [
|
||||
"Bracket plumb, all bolts tight",
|
||||
"Every splice tug-tested",
|
||||
"Sign powers up and every function works",
|
||||
"Photo of the finished install sent to MPM",
|
||||
]),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
@@ -0,0 +1,23 @@
|
||||
"""TEMPLATE figures for an INS. Wireframe style: ink linework on a faint grid, gold for the part
|
||||
being installed, red for warnings, dimensions in JetBrains Mono. See INS-LT-15R-01/source/figs.py
|
||||
for a full set (drill template, sections, wiring diagrams)."""
|
||||
from mpmdoc.figkit import T, svg_open, INK, KICK, GOLD, MUTE, CREAM
|
||||
|
||||
|
||||
def part():
|
||||
W, H = 300, 260
|
||||
o = [svg_open(W, H, grid=True)]
|
||||
o.append(f'<rect x="110" y="30" width="80" height="200" rx="3" fill="{CREAM}" stroke="{INK}" stroke-width="1.4"/>')
|
||||
for i, (lab, y) in enumerate([("A", 60), ("B", 140), ("C", 200)]):
|
||||
o.append(f'<circle cx="150" cy="{y}" r="6" fill="#fff" stroke="{INK}" stroke-width="1.2"/>')
|
||||
o.append(f'<circle cx="86" cy="{y}" r="9" fill="{GOLD}"/>')
|
||||
o.append(T(86, y + 3.5, lab, size=9, w=800, anchor="middle", ls=0))
|
||||
o.append(f'<line x1="150" y1="18" x2="150" y2="244" stroke="{KICK}" stroke-width=".9" stroke-dasharray="14 3 2 3"/>')
|
||||
o.append(T(150, 14, "CL", size=8, w=700, fill=KICK, anchor="middle"))
|
||||
o.append(T(214, 135, "PLUMB", size=8, w=700))
|
||||
o.append(T(214, 147, "level on edge", size=7.5, w=500, fill=MUTE, fam="Open Sans", ls=0))
|
||||
o.append("</svg>")
|
||||
return "".join(o)
|
||||
|
||||
|
||||
FIGS = {"part": part}
|
||||
Binary file not shown.
@@ -0,0 +1,66 @@
|
||||
"""TEMPLATE — Proposal (PRO). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
Pre-award: the customer's need, MPM's proposed solution, why MPM, scope, investment, timeline, next
|
||||
steps. It persuades with facts, not adjectives. Marked CONFIDENTIAL by default (pricing). 3–8 pages.
|
||||
After award, the agreed scope becomes a SOW with its own number.
|
||||
See MPM_Document_Standard.md §9.9.
|
||||
|
||||
Order: masthead (Prepared for · Valid until) → parties → At a glance (KPI tiles) → 1 Your situation
|
||||
→ 2 Proposed solution → 3 Why Message Point Media → 4 Scope summary → 5 Investment (priced BOM)
|
||||
→ 6 Timeline → 7 Assumptions and exclusions → 8 Next steps → approval → Revision history
|
||||
Rules: every price comes from the quotation (never estimated); every proof point in §3 is a fact
|
||||
(deployments, agencies, years, uptime) you can back up. No superlatives.
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc
|
||||
from mpmdoc.brand import PROVIDER
|
||||
|
||||
doc = Doc(
|
||||
number="PRO-MPM-SUBJ-01", rev="0.1", date="2026-11-06", customer="Agency name", subject="Project",
|
||||
title="Wayside Real-Time Signage for Agency",
|
||||
lede="What MPM proposes, for whom, in one line.",
|
||||
meta=[("Prepared for", "Agency name"), ("Valid until", "Dec 6, 2026")],
|
||||
slug="Customer_Project_Proposal",
|
||||
history=[("0.1", "2026-11-06", "Initial draft.")],
|
||||
)
|
||||
|
||||
price = doc.priced([ # extended prices from Quotation S00000; totals are computed
|
||||
("Hardware subtotal", [("SKU-0000", "Outdoor LED sign, model", 4, 40000.00), ("MNT-0000", "Mounting kit", 4, 2000.00)]),
|
||||
("Services subtotal", [("SVC-INST", "Installation, per sign", 4, 8000.00), ("SVC-PM", "Project management", 1, 3000.00)]),
|
||||
])
|
||||
|
||||
doc.add(
|
||||
doc.parties([("Prepared for", {"name": "Agency name", "contact": "Contact, title", "address": "City, ST"}),
|
||||
("Prepared by", PROVIDER)]),
|
||||
doc.kpis([("Signs", "4", "Wayside, outdoor", "strong"), ("Investment", doc.money(doc.price_total), "plus applicable tax", "key"),
|
||||
("Install", "8 weeks", "from award", "")]),
|
||||
doc.section("1. Your situation"),
|
||||
doc.p("The agency's need in its own terms: riders, stops, current system, what is not working."),
|
||||
doc.section("2. Proposed solution"),
|
||||
doc.p("What MPM will install and how it works for the rider. One paragraph, then the parts."),
|
||||
doc.bullets(["Signs: model, size, brightness, accessibility (audio, contrast).", "Content: MP.TV platform, real-time feed.",
|
||||
"Support: monitoring, warranty, service."]),
|
||||
doc.section("3. Why Message Point Media"),
|
||||
doc.bullets(["Fact: comparable deployments, with agency names where permitted.", "Fact: years in transit signage.",
|
||||
"Fact: platform uptime or support response, from records."]),
|
||||
doc.section("4. Scope summary"),
|
||||
doc.table([("Phase", "", "22%"), ("MPM provides", ""), ("Agency provides", "", "30%")],
|
||||
[["Survey and design", "Site survey, mounting design", "Site access"], ["Install", "Hardware, installation, configuration", "Power and network at each pole"]]),
|
||||
doc.section("5. Investment"),
|
||||
price,
|
||||
doc.note("Pricing", "Per MPM Quotation S00000, dated Nov 6, 2026. Valid until Dec 6, 2026. Sales tax not included."),
|
||||
doc.section("6. Timeline"),
|
||||
doc.table([("Phase", ""), ("Duration", "", "20%"), ("Starts", "", "25%")], [["Survey and design", "2 weeks", "Award"], ["Production", "4 weeks", "Design approval"], ["Install", "2 weeks", "Delivery"]]),
|
||||
doc.section("7. Assumptions and exclusions"),
|
||||
doc.two(doc.sub("Assumptions") + doc.bullets(["Power and network available at each location."]),
|
||||
doc.sub("Exclusions") + doc.bullets(["Permits, unless listed in §5.", "Civil work and trenching."])),
|
||||
doc.section("8. Next steps"),
|
||||
doc.steps(["Review this proposal and send questions to MPM.", "Issue a purchase order or notice of award.",
|
||||
"MPM issues the Statement of Work and schedules the site survey."]),
|
||||
doc.approval("Jason Stedwell", "Director of Technical Services, Message Point Media | Irondale, AL", "2026-11-06", label="Submitted by"),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
Binary file not shown.
@@ -0,0 +1,63 @@
|
||||
"""TEMPLATE — Standard Operating Procedure (SOP). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
A repeatable task for MPM staff (setup, onboarding, process). Marked INTERNAL USE automatically.
|
||||
2–6 pages. See MPM_Document_Standard.md §9.2. Reference: SOP-IT-UNIFI-01 (UniFi Talk).
|
||||
|
||||
Order (do not reorder):
|
||||
masthead (automatic) → Before you start → Process at a glance (who does each part)
|
||||
→ Walkthrough (doc.step per step, screenshots in img/) → special cases (doc.special)
|
||||
→ Setup checklist → Troubleshooting → Need help? → Revision history (automatic)
|
||||
|
||||
Screenshots: crop to the relevant part, mark the control to tap with a gold box, save as JPG in img/.
|
||||
They are captioned but not numbered. Name on-screen controls with ui('Label').
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc, ui
|
||||
|
||||
doc = Doc(
|
||||
number="SOP-IT-SUBJ-01", rev="0.1", date="2026-11-06", # CUST = department: IT, ENG, OPS, QA
|
||||
customer=None, subject="System",
|
||||
kicker="End-user walkthrough · System",
|
||||
title="Do the Thing with the App",
|
||||
lede="One sentence: what the reader will have done and be able to do at the end.",
|
||||
meta=[("Audience", "All MPM staff"), ("Time needed", "About 10 minutes")],
|
||||
slug="MPM_System_Task",
|
||||
history=[("0.1", "2026-11-06", "Initial draft.")],
|
||||
)
|
||||
|
||||
PHONE, SIDE = "92px", "20%" # one phone screenshot beside a step
|
||||
|
||||
doc.add(
|
||||
doc.before_you_start([
|
||||
"<b>What has already been done for you</b>, and by whom.",
|
||||
"What to have in hand before you start.",
|
||||
"Anything that expires or needs a special case (point to it by name).",
|
||||
]),
|
||||
doc.section("Process at a glance"),
|
||||
doc.glance([
|
||||
("Account<br>created", "IT", "✓", True), # done by someone else: tick + greyed tile
|
||||
("Open<br>email", "You"), ("Install<br>app", "You"), ("Sign in", "You"),
|
||||
]),
|
||||
|
||||
doc.section("Walkthrough", new_page=True),
|
||||
doc.step(1, "Your inbox", "Open the invitation email", doc.steps([
|
||||
f"Open the email from <b>Vendor</b> and tap {ui('Get Started')}.",
|
||||
"Continue to Step 2.",
|
||||
]) + doc.note("Good to know", "One short fact that prevents a support call."),
|
||||
doc.shot("img/placeholder.jpg", "Caption: what to look for", PHONE), side=SIDE),
|
||||
|
||||
doc.special("Some staff only", "Outside the U.S.? Do this first",
|
||||
"<p>Who this applies to and how they'll know.</p>"
|
||||
+ doc.steps([f"Tap {ui('VPN')}.", "Leave it on for your whole shift."])),
|
||||
|
||||
doc.checklist("Setup checklist", ["Email opened and link tapped", "App shows my name", "Test completed"], cols=1),
|
||||
doc.section("Troubleshooting"),
|
||||
doc.table([("Problem", "", "28%"), ("What to do", "")],
|
||||
[["<b>Email is missing</b>", "Check Spam/Junk. If it isn't there, ask IT to resend it."]]),
|
||||
doc.note("Need help?", "Contact MPM IT."),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
"""TEMPLATE — Statement of Work (SOW). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
The contractual scope after award: what MPM delivers, how, and the payment milestones. Follows the
|
||||
layout of SOW_Nashville_Replacement_Sign_S01012 (Aug 2026). 2–6 pages.
|
||||
See MPM_Document_Standard.md §9.10.
|
||||
|
||||
Order: masthead (Customer · Quotation) → opening paragraph (parties, MSA, end customer, quotation)
|
||||
→ 1.0 Scope overview → 2.0 Project deliverables (2.1 hardware + 2.1.1 priced BOM, 2.2 documentation)
|
||||
→ 3.0 Scope of work (3.1 initiation … 3.n warranty) → 4.0 Project milestones → 5.0 Assumptions and
|
||||
exclusions → [6.0 Acceptance: signatures, when the customer signs the SOW itself] → Revision history
|
||||
Rules: prices come from the quotation; milestones(expect=doc.price_total) fails the build if the milestones
|
||||
don't add up. Draft 0.x while negotiating; 1.0 = executed.
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc
|
||||
|
||||
doc = Doc(
|
||||
number="SOW-MPM-SUBJ-01", rev="0.1", date="2026-11-06", customer="Partner", subject="Project",
|
||||
title="Project Name",
|
||||
lede="Statement of work for the scope, at the site, for the end customer.",
|
||||
meta=[("Customer", "Partner, for End Customer"), ("Quotation", "S00000")],
|
||||
slug="Customer_Project_SOW",
|
||||
history=[("0.1", "2026-11-06", "Initial draft.")],
|
||||
)
|
||||
|
||||
doc.add(
|
||||
doc.p("This Statement of Work (SOW) is made by and between <b>Partner</b> (Customer) and <b>Message Point Media of "
|
||||
"Alabama, Inc</b> (MPM), and is subject to the Master Services Agreement between Customer and MPM, whose "
|
||||
"provisions are incorporated except as amended here. The work is for the benefit of <b>End Customer</b>. "
|
||||
"Pricing is per MPM Quotation S00000, dated Nov 6, 2026 (expires Feb 4, 2027)."),
|
||||
doc.section("1.0 Scope overview"),
|
||||
doc.p("What MPM will provide, in one paragraph. Say what is <i>not</i> included when a reader might assume it is."),
|
||||
doc.section("2.0 Project deliverables"),
|
||||
doc.sub("2.1 Hardware deliverables"),
|
||||
doc.p("MPM will procure, provision and deliver all hardware needed for the work, including:"),
|
||||
doc.bullets(["Item, with the specification that matters (pitch, size, rating)."]),
|
||||
doc.sub("2.1.1 Bill of materials (per Quotation S00000)"),
|
||||
doc.priced([
|
||||
("Hardware subtotal", [("SKU-0000", "Hardware item", 1, 10000.00), ("MNT-0000", "Mounting and bracket budget", 1, 500.00)]),
|
||||
("Installation subtotal", [("SVC-INST", "Installation", 1, 2500.00)]),
|
||||
]),
|
||||
doc.sub("2.2 Documentation deliverables"),
|
||||
doc.table([("Document", "", "32%"), ("Purpose", "")],
|
||||
[["Installation Completion Report", "Confirms installation, configuration and operational verification"],
|
||||
["As-built update", "Records serial numbers and redline changes"]]),
|
||||
doc.section("3.0 Scope of work"),
|
||||
doc.sub("3.1 Project initiation and scheduling"),
|
||||
doc.p("Within ten (10) business days of award, MPM confirms points of contact, site access and the installation window."),
|
||||
doc.sub("3.2 Site confirmation"), doc.p("How site conditions are confirmed, and what happens if they differ."),
|
||||
doc.sub("3.3 Hardware procurement and provisioning"), doc.p("Procurement, provisioning and QA testing before delivery."),
|
||||
doc.sub("3.4 Hardware delivery"), doc.p("Shipment as directed by Customer."),
|
||||
doc.sub("3.5 Installation"), doc.p("Installation, configuration, content assignment. Responsibilities for power and network."),
|
||||
doc.sub("3.6 Operational verification"), doc.p("Online, reporting to MP.TV, displaying assigned content."),
|
||||
doc.sub("3.7 Warranty"), doc.p("MPM standard Equipment Warranty Policy (name the policy), starting at End Customer approval."),
|
||||
doc.section("4.0 Project milestones"),
|
||||
doc.milestones([("Production deposit", "Due on contract execution", "25% of hardware", 2625.00),
|
||||
("Equipment delivery", "All hardware delivered to site", "75% of hardware", 7875.00),
|
||||
("Installation completion", "Installed, verified operational, accepted", "100% of installation", 2500.00)],
|
||||
expect=doc.price_total),
|
||||
doc.section("5.0 Assumptions and exclusions"),
|
||||
doc.bullets(["Existing power and network remain in place; changes to them are the End Customer's responsibility.",
|
||||
"Excludes permits, civil work and licensing unless listed in §2."]),
|
||||
doc.section("6.0 Acceptance"),
|
||||
doc.signatures([("Customer", "Name, title · date"), ("Message Point Media", "Jason Stedwell, Director of Technical Services · date")]),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
Binary file not shown.
@@ -0,0 +1,61 @@
|
||||
"""TEMPLATE — Site Survey (SVY). Copy this folder to <PROJECT>/source/ and fill it in. Photos go in img/.
|
||||
|
||||
A record of what exists on site and what it means for design. 4–12 pages.
|
||||
See MPM_Document_Standard.md §9.5. Reference: SVY-SOR-MON-01 (SORTA Monument Square).
|
||||
|
||||
Order: masthead → control table (kv) → About this document (dark) → Status note
|
||||
→ PART 1 Summary of Findings (customer) → PART 2 Engineering Detail (MPM internal)
|
||||
→ To verify list → Appendix A Photo log → Appendix B Field notes (verbatim) → Revision history
|
||||
Rules: dimensions as recorded in the field (convert, never round away the original); anything not
|
||||
measured is TBC or a To-verify item; photos are numbered figures and every one is in the photo log.
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc, verify
|
||||
|
||||
doc = Doc(
|
||||
number="SVY-MPM-SITE-01", rev="0.1", date="2026-11-06",
|
||||
customer=None, subject="Site name",
|
||||
title="Site Name Wayside Signage",
|
||||
lede="What was surveyed, for which project, and when the field visit took place.",
|
||||
meta=[("Survey date", "Nov 6, 2026"), ("Surveyor", "Jason Stedwell · MPM")],
|
||||
slug="Customer_Site_Site-Survey",
|
||||
classification="internal", # internal until Part 2 is split out for the customer
|
||||
history=[("0.1", "2026-11-06", "Preliminary draft from field notes and photographs.")],
|
||||
)
|
||||
|
||||
doc.add(
|
||||
doc.kv([("Prepared for", "Customer, City, ST"), ("Prepared by", "Message Point Media (MPM): name, role"),
|
||||
("Basis", "N pages of field notes · N site photographs · drawings received (or none)")]),
|
||||
doc.dark("About this document", [
|
||||
"<b>Part 1, Summary of Findings</b>, is written for the customer.",
|
||||
"<b>Part 2, Engineering Detail</b>, is MPM-internal.",
|
||||
"<b>Appendices</b> hold the photo log and a verbatim transcription of the field notes.",
|
||||
]),
|
||||
doc.note("Status of this revision", f"Preliminary draft. Items marked {verify()} are points of record to confirm with the customer."),
|
||||
|
||||
doc.part("Part 1", "Summary of Findings"),
|
||||
doc.section("1.1 Purpose"), doc.p("Why the survey was done, in rider/agency terms."),
|
||||
doc.section("1.2 Scope"), doc.p("What was in and out of scope, and why."),
|
||||
doc.section("1.3 What we found"),
|
||||
doc.table([("Stops", ""), ("Structure", ""), ("Existing mount", ""), ("Governing clearance", "")],
|
||||
[["A, B", "Steel column", "Side / cantilever", "Member <b>870 mm</b> from mount"]], caption="Families found on site."),
|
||||
doc.p("<b>Lead with the conclusion.</b> One bold sentence per finding, then what it means for the design."),
|
||||
|
||||
doc.part("Part 2", "Engineering Detail", internal=True),
|
||||
doc.section("2.1 Survey record"),
|
||||
doc.kv([("Survey date / time", "From photo EXIF"), ("Site name", f"As recorded {verify()}")]),
|
||||
doc.section("2.2 Observations and design constraints"),
|
||||
doc.sub("Field observations"), doc.bullets(["What was seen or measured, with the value in bold."]),
|
||||
doc.sub("Design constraints and implications"), doc.bullets(["What the observation forces in the design."]),
|
||||
doc.note("To verify with the customer before release", ["Every point of record still open, with its section number."], tone="verify"),
|
||||
|
||||
doc.part("Appendix A", "Photo Log"),
|
||||
doc.table([("#", "q"), ("Time", "m"), ("File ID", "m"), ("Subject", "")], [["1", "08:46:17", "D2C5E8C7", "What the photo shows"]], cls="tight"),
|
||||
doc.part("Appendix B", "Field Notes: Verbatim Transcription"),
|
||||
doc.transcript("Notebook heading", ["Spelling and abbreviations preserved.", "• bullet as written"]),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
Binary file not shown.
@@ -0,0 +1,52 @@
|
||||
"""TEMPLATE — Technical Action Plan (TAP). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
A planned engagement with a customer or OEM partner: scope, staffing, logistics, procedures, success
|
||||
criteria. Marked CONFIDENTIAL automatically. 5–12 pages.
|
||||
See MPM_Document_Standard.md §9.6. Reference: TAP-GIL-M37-01 (GILLIG).
|
||||
|
||||
Order: masthead → document-control table → confidentiality notice → 1 Purpose & scope (what changed)
|
||||
→ 2 Background & register → 3 Information required (Action required) → 4 Personnel
|
||||
→ 5 Kit → 6 Timeline → 7 Procedures by group → 8 Open items → 9 Acceptance criteria
|
||||
→ 10 Reporting → 11 Contacts → approval → Revision history
|
||||
Sections are numbered in the heading text ("3. Information required …"; "7.1 Group A …").
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc
|
||||
|
||||
doc = Doc(
|
||||
number="TAP-MPM-SUBJ-01", rev="0.1", date="2026-11-06",
|
||||
customer=None, subject="Product",
|
||||
footer="Customer · NDA",
|
||||
title="Customer On-Site Service Engagement",
|
||||
lede="What is being resolved, for which products, where.",
|
||||
meta=[("Customer", "Customer · Partner"), ("Engagement", "Dates")],
|
||||
slug="Customer_Subject_Action-Plan",
|
||||
history=[("0.1", "2026-11-06", "Initial draft.")],
|
||||
)
|
||||
|
||||
doc.add(
|
||||
doc.kv([("Supersedes / references", "Prior plans, bulletins, RMAs"), ("Distribution", "Customer | Partner"),
|
||||
("Affected products", "Product names and part numbers"), ("Field technician", "Name, role"),
|
||||
("Technical oversight", "Jason Stedwell, Director of Technical Services"), ("Engagement site", "Facility, City, ST")]),
|
||||
doc.note("Proprietary & confidential", "Provided exclusively to the named recipients under the existing NDA. "
|
||||
"Reproduction or disclosure to any third party without written consent from MPM is prohibited.", tone="warn"),
|
||||
|
||||
doc.section("1. Purpose and scope", new_page=True),
|
||||
doc.p("What this plan covers and the shared objective."),
|
||||
doc.note("What changed in this revision", "Only from 1.1 on: the scope change in one paragraph."),
|
||||
doc.section("2. Background and known-issue summary"),
|
||||
doc.table([("Reject #", "m"), ("Issue", ""), ("Group", ""), ("Status", "")], [["00000", "Reported issue", "A", "On-site"]],
|
||||
caption="Known-issue register.", cls="tight"),
|
||||
doc.section("3. Information required prior to engagement"),
|
||||
doc.special("Action required", "Customer response needed to finalize the visit",
|
||||
doc.table([("#", "q"), ("Information needed", ""), ("Why it matters", "")], [["1", "Units staged", "Work starts on arrival"]], cls="tight")),
|
||||
doc.section("4. Personnel"), doc.p("Who is on site, who oversees, who coordinates."),
|
||||
doc.section("7. Procedures by group"), doc.sub("7.1 Group A"), doc.steps(["Step.", "Step."]),
|
||||
doc.section("9. Acceptance criteria"), doc.bullets(["Pass criterion."], check=True),
|
||||
doc.approval("Jason Stedwell", "Director of Technical Services, Message Point Media | Irondale, AL", "2026-11-06"),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,72 @@
|
||||
"""TEMPLATE — Technical Note (TN). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
A TN answers ONE engineering question (does it fit, how much power, is a cut needed)
|
||||
and puts the answer first. 1–3 pages. See MPM_Document_Standard.md §9.3.
|
||||
|
||||
Order (do not reorder):
|
||||
masthead (automatic) → KPI tiles → Result → evidence sections (tables, calc, figures)
|
||||
→ Assumptions and open items → Revision history (automatic)
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc, KeyRow, up, dn
|
||||
from figs import FIGS, MEASURED, LIMIT # keep every number in ONE place (figs.py) and derive the rest
|
||||
|
||||
F = {k: f() for k, f in FIGS.items()}
|
||||
MARGIN = LIMIT - MEASURED
|
||||
|
||||
doc = Doc(
|
||||
number="TN-MPM-SUBJ-01", # TYPE-CUST-SUBJ-NN · register CUST + number in registry.md
|
||||
rev="0.1", # 0.x = Draft · 1.0 = first release
|
||||
date="2026-11-05", # ISO in source; displayed as "Nov 5, 2026"
|
||||
customer=None, # None = name from the CUST code; override for a fuller name
|
||||
subject="Subject", # short: the thing studied ("Bay 17", "Pingo P2.5 LED module")
|
||||
title="Question-Shaped Title: The Thing Being Checked",
|
||||
lede=("Two to three sentences: the situation, and what this note works out. "
|
||||
"Write it so someone who reads only this paragraph knows why the note exists."),
|
||||
meta=[("Site", "Where / what"), ("Requirement", "The criterion · e.g. ≤ 60 W")], # exactly two cells
|
||||
slug="Customer_Short-Title", # → TN-MPM-SUBJ-01_Customer_Short-Title_Rev0.1.pdf
|
||||
history=[("0.1", "2026-11-05", "Initial draft.")],
|
||||
)
|
||||
|
||||
doc.add(
|
||||
# 3–4 tiles: the input(s), THE ANSWER (tone="key"), and the verdict (tone="ok"/"bad")
|
||||
doc.kpis([
|
||||
("Measured", f"{MEASURED:.1f} W", "How it was measured"),
|
||||
("Limit", f"{LIMIT:.1f} W", "Where the limit comes from", "strong"),
|
||||
("Margin", f"{MARGIN:+.1f} W", "Limit − measured", "ok" if MARGIN >= 0 else "bad"),
|
||||
]),
|
||||
# 2–4 bullets. First bullet = the answer, with the number in bold. Then conditions / what to do.
|
||||
doc.result([
|
||||
f"The answer in one sentence, with the key value in <b>bold ({MEASURED:.1f} W)</b>.",
|
||||
"The condition the answer depends on, and what changes if it doesn't hold.",
|
||||
]),
|
||||
|
||||
doc.section("Comparison"),
|
||||
doc.table(
|
||||
[("Item", ""), ("Value", "m r"), ("Change", "d r"), ("Notes", "")],
|
||||
[
|
||||
["Row", "1.0", up("+1.0"), "Source of the number"],
|
||||
KeyRow(["<b>The row that matters</b>", "2.0", dn("−0.5"), "Highlight max. one row"]),
|
||||
],
|
||||
caption="What the table shows, units, and the datum.", ref="cmp"),
|
||||
|
||||
doc.section("Calculation"),
|
||||
doc.calc([
|
||||
"# One comment line per step, saying why",
|
||||
("Margin", "limit − measured = {:.1f} − {:.1f}".format(LIMIT, MEASURED), f"{MARGIN:+.1f} W",
|
||||
"PASS" if MARGIN >= 0 else "FAIL"),
|
||||
]),
|
||||
|
||||
doc.section("Figure"),
|
||||
doc.figure(F["bars"], "Caption says what to look at, not just what it is. Refer to it in text as [[fig:bars]].", ref="bars"),
|
||||
|
||||
doc.assumptions(
|
||||
["Every assumption the answer rests on, one per bullet."],
|
||||
["Everything still to confirm, phrased as an action (Confirm …, Add …)."],
|
||||
),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
@@ -0,0 +1,76 @@
|
||||
"""TEMPLATE — Technical Note, Simplified Chinese edition (技术说明 · 中文版).
|
||||
Copy next to the English build.py in <PROJECT>/source/. See MPM_Document_Standard.md §12.
|
||||
|
||||
Rules:
|
||||
* Same number, slug, revision and figures as build.py; the engine adds _ZH to the file name and refuses
|
||||
to build until the English PDF at this revision is in the project folder. Build English first.
|
||||
* Translate every string here, in the same order as build.py, so the two can be compared side by side.
|
||||
* Figure text goes through i18n.Translator: this document's TERMS first, then _STANDARDS/glossary_zh.md.
|
||||
tr.check() fails the build on any English left in a figure.
|
||||
* Numbers come from figs.py exactly as in English. Metric first, imperial in [ ].
|
||||
* The masthead carries "英文版为准" (English governs) automatically.
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
sys.path.insert(0, str(HERE))
|
||||
|
||||
from mpmdoc import Doc, KeyRow, up, dn, i18n
|
||||
from figs import FIGS, MEASURED, LIMIT
|
||||
|
||||
MARGIN = LIMIT - MEASURED
|
||||
|
||||
TERMS = { # this document's figure phrases (single terms: glossary)
|
||||
f"LIMIT · {LIMIT:.1f} W": f"限值 · {LIMIT:.1f} W",
|
||||
}
|
||||
tr = i18n.Translator(TERMS)
|
||||
F = {k: tr.svg(f()) for k, f in FIGS.items()}
|
||||
tr.check()
|
||||
|
||||
doc = Doc(
|
||||
number="TN-MPM-SUBJ-01", rev="0.1", date="2026-11-05", lang="zh",
|
||||
customer="客户名称", subject="主题",
|
||||
title="以问题为标题:被核查的对象",
|
||||
lede="两到三句话:现状,以及本说明要解决的问题。只读这一段的人也应知道本说明的目的。",
|
||||
meta=[("地点", "位置 / 对象"), ("要求", "判定标准 · 例如 ≤ 60 W")],
|
||||
slug="Customer_Short-Title", # identical to the English slug
|
||||
history=[("0.1", "2026-11-05", "初稿。")],
|
||||
)
|
||||
|
||||
doc.add(
|
||||
doc.kpis([
|
||||
("实测", f"{MEASURED:.1f} W", "测量方法"),
|
||||
("限值", f"{LIMIT:.1f} W", "限值来源", "strong"),
|
||||
("余量", f"{MARGIN:+.1f} W", "限值 − 实测", "ok" if MARGIN >= 0 else "bad"),
|
||||
]),
|
||||
doc.result([
|
||||
f"一句话结论,关键数值加粗 <b>({MEASURED:.1f} W)</b>。",
|
||||
"结论成立的前提条件,以及条件不满足时的变化。",
|
||||
]),
|
||||
|
||||
doc.section("对比"),
|
||||
doc.table(
|
||||
[("项目", ""), ("数值", "m r"), ("变化", "d r"), ("备注", "")],
|
||||
[
|
||||
["行", "1.0", up("+1.0"), "数据来源"],
|
||||
KeyRow(["<b>关键行</b>", "2.0", dn("−0.5"), "最多突出一行"]),
|
||||
],
|
||||
caption="表格内容、单位及基准。", ref="cmp"),
|
||||
|
||||
doc.section("计算"),
|
||||
doc.calc([
|
||||
"# 每步一行注释,说明原因",
|
||||
("余量", "限值 − 实测 = {:.1f} − {:.1f}".format(LIMIT, MEASURED), f"{MARGIN:+.1f} W",
|
||||
"PASS" if MARGIN >= 0 else "FAIL"),
|
||||
]),
|
||||
|
||||
doc.section("图示"),
|
||||
doc.figure(F["bars"], "图注说明应关注的内容。正文中引用为 [[fig:bars]]。", ref="bars"),
|
||||
|
||||
doc.assumptions(
|
||||
["结论所依据的每项假设,每条一项。"],
|
||||
["所有待确认事项,以动作表述(确认……、补充……)。"],
|
||||
),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
@@ -0,0 +1,31 @@
|
||||
"""TEMPLATE figures for a TN. Keep the source numbers here; build.py imports them.
|
||||
Each figure is a function returning an SVG string; register it in FIGS."""
|
||||
from mpmdoc.figkit import T, svg_open, INK, KICK, MUTE, LINE, RED, GRY
|
||||
|
||||
# ---- source numbers (one place only) ----
|
||||
MEASURED = 51.0
|
||||
LIMIT = 63.8
|
||||
|
||||
|
||||
def bars():
|
||||
W, L, R, top, rowh = 700, 150, 640, 30, 30
|
||||
rows = [("Measured", MEASURED, INK), ("Reference", 20.4, GRY)]
|
||||
vmax = 70.0
|
||||
x = lambda v: L + v * (R - L) / vmax
|
||||
bot = top + rowh * len(rows)
|
||||
o = [svg_open(W, bot + 30)]
|
||||
for v in range(0, 71, 10):
|
||||
o.append(f'<line x1="{x(v):.1f}" y1="{top-6}" x2="{x(v):.1f}" y2="{bot}" stroke="{LINE}" stroke-width="0.8"/>')
|
||||
o.append(T(x(v), bot + 14, v, size=7.4, w=500, fill=MUTE, anchor="middle", fam="JetBrains Mono", ls=0))
|
||||
o.append(f'<line x1="{x(LIMIT):.1f}" y1="{top-14}" x2="{x(LIMIT):.1f}" y2="{bot}" stroke="{RED}" stroke-width="1.4" stroke-dasharray="5 3"/>')
|
||||
o.append(T(x(LIMIT), top - 18, f"LIMIT · {LIMIT:.1f} W", size=6.8, w=800, fill=RED, anchor="middle", ls=1.0))
|
||||
for i, (name, v, c) in enumerate(rows):
|
||||
cy = top + i * rowh + rowh / 2
|
||||
o.append(T(L - 10, cy + 3, name, size=8.2, anchor="end"))
|
||||
o.append(f'<rect x="{L}" y="{cy-8.5:.1f}" width="{x(v)-L:.1f}" height="17" rx="2" fill="{c}"/>')
|
||||
o.append(T(x(v) + 6, cy + 3.2, f"{v:.1f} W", size=8.6, w=700, fam="JetBrains Mono", ls=0, halo=True))
|
||||
o.append("</svg>")
|
||||
return "".join(o)
|
||||
|
||||
|
||||
FIGS = {"bars": bars}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,71 @@
|
||||
"""TEMPLATE — Technical Service Bulletin (TSB). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
A known product issue and its fix, for every unit it affects. One number, two editions from this one
|
||||
script: the CUSTOMER edition (agencies, partners) and the INTERNAL edition (MPM field and support), which
|
||||
adds a Part 2 with diagnostic detail. The customer edition never contains Part 2. 2–5 pages.
|
||||
See MPM_Document_Standard.md §9.8.
|
||||
|
||||
Order: masthead (Compliance · Effective) → compliance pill → applicability (kv) → In short (dark)
|
||||
→ 1 Symptom → 2 Cause → 3 Corrective action → 4 Verification → 5 Parts → Compliance record
|
||||
→ [internal: PART 2 Diagnostic detail] → Revision history
|
||||
Compliance: Mandatory (red) = safety/failure, fix by a date · Recommended (gold) = fix at next visit
|
||||
· Informational (grey) = no action, awareness only.
|
||||
Numbers: customer code MPM for product-wide bulletins (TSB-MPM-LX270-01); a customer code when the
|
||||
issue is specific to one deployment. Subject = product family.
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc
|
||||
|
||||
REV, DATE = "0.1", "2026-11-06"
|
||||
HISTORY = [("0.1", DATE, "Initial draft.")]
|
||||
COMPLIANCE, PILL = "Recommended", "gold" # Mandatory/red · Recommended/gold · Informational/grey
|
||||
|
||||
for edition, slug in (("customer", "MPM_Product_Issue_TSB"), ("internal", "MPM_Product_Issue_TSB-Internal")):
|
||||
internal = edition == "internal"
|
||||
doc = Doc(
|
||||
number="TSB-MPM-SUBJ-01", rev=REV, date=DATE, subject="Product family",
|
||||
title="Product: Issue in a Few Words",
|
||||
lede="One line: what goes wrong, on which units, and what to do about it.",
|
||||
meta=[("Compliance", COMPLIANCE), ("Effective", "Nov 6, 2026")],
|
||||
slug=slug, classification=edition, history=HISTORY,
|
||||
)
|
||||
body = [
|
||||
doc.pill(COMPLIANCE, PILL),
|
||||
doc.kv([("Applies to", "Product, model, part number"),
|
||||
("Affected units", "Serial range, build dates or firmware versions"),
|
||||
("Not affected", "Units outside that range; how to tell"),
|
||||
("Labor estimate", "0.5 h per unit, one technician"),
|
||||
("Supersedes", "—"),
|
||||
("Distribution", "MPM field and support" if internal else "Customers and partners operating affected units")]),
|
||||
doc.result(["What happens, in one line.", "What to do, in one line.", "By when (Mandatory only)."]),
|
||||
doc.section("1. Symptom"),
|
||||
doc.p("What the rider, the agency or the platform sees. Exact: which LEDs, which message, how often."),
|
||||
doc.section("2. Cause"),
|
||||
doc.p("The cause in plain words. Customer edition: enough to trust the fix, no more."),
|
||||
doc.section("3. Corrective action"),
|
||||
doc.steps(["Power down the sign at the breaker and confirm it is dark.", "Replace the part (see §5).",
|
||||
"Power up and confirm the sign reports online on MP.TV."]),
|
||||
doc.section("4. Verification"),
|
||||
doc.bullets(["Sign reports online on MP.TV within 5 minutes.", "No dropout over a 10-minute content loop."], check=True),
|
||||
doc.section("5. Parts"),
|
||||
doc.table([("Part no.", "m", "22%"), ("Description", ""), ("Qty per unit", "r", "14%")],
|
||||
[["PN-0000", "Replacement part", "1"]]),
|
||||
doc.section("Compliance record"),
|
||||
doc.table([("Unit serial", "", "24%"), ("Site", ""), ("Date", "", "16%"), ("Technician", "", "20%"), ("Done", "c", "8%")],
|
||||
[["", "", "", "", '<span class="box"></span>']] * 6, cls="fill"),
|
||||
]
|
||||
if internal:
|
||||
body += [
|
||||
doc.part("Part 2", "Diagnostic detail", "MPM field and support only. Not for distribution.", internal=True),
|
||||
doc.section("Root cause"), doc.p("Failure analysis, test results, supplier findings."),
|
||||
doc.section("Field data"),
|
||||
doc.table([("Site", ""), ("Units", "r", "12%"), ("Failures", "r", "12%"), ("First seen", "", "18%")],
|
||||
[["Site name", "0", "0", "Nov 6, 2026"]]),
|
||||
doc.section("Support notes"),
|
||||
doc.bullets(["How to recognise the issue on a support call.", "Remote checks before dispatch."]),
|
||||
]
|
||||
doc.add(*body)
|
||||
doc.build()
|
||||
Binary file not shown.
@@ -0,0 +1,52 @@
|
||||
"""TEMPLATE — Field Work Order (WO). Copy this folder to <PROJECT>/source/ and fill it in.
|
||||
|
||||
A dispatch ticket for one job at one site, for MPM technicians or a subcontract installer. It says where,
|
||||
when, what to do, what to bring and what to send back; the technician fills in the completion record and
|
||||
both parties sign. Marked INTERNAL USE (it carries contacts and access details). 1–3 pages.
|
||||
See MPM_Document_Standard.md §9.11.
|
||||
|
||||
Order: masthead (Scheduled · Assigned to) → status pill → parties (Site · Site contact · MPM dispatch)
|
||||
→ job details (kv) → Safety → Work to be performed → Parts and materials → Return with this work order
|
||||
→ Completion record → signatures → Revision history
|
||||
Status pill: Dispatched (gold) · On site (ink) · Complete (green) · On hold (red).
|
||||
The signed, completed copy is scanned into the project's _INPUTS/ (never edited); this PDF stays as issued.
|
||||
Issue at 1.0; a re-dispatch with changed scope or date is 1.1.
|
||||
"""
|
||||
import sys, pathlib
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(next(p / "_STANDARDS" for p in HERE.parents if (p / "_STANDARDS").is_dir())))
|
||||
|
||||
from mpmdoc import Doc
|
||||
|
||||
doc = Doc(
|
||||
number="WO-MPM-SITE-01", rev="1.0", date="2026-11-06", customer="Agency", subject="Site",
|
||||
title="Install: Site Name, Sign Type",
|
||||
lede="One line: the job, the site, the outcome.",
|
||||
meta=[("Scheduled", "Nov 10, 2026 · 8:00 AM"), ("Assigned to", "Technician · Company")],
|
||||
slug="Customer_Site_Work-Order",
|
||||
history=[("1.0", "2026-11-06", "Issued.")],
|
||||
)
|
||||
|
||||
doc.add(
|
||||
doc.pill("Dispatched", "gold"),
|
||||
doc.parties([("Site", {"name": "Stop or facility name", "address": "Street, City, ST", "coordinates": "00.00000, -00.00000"}),
|
||||
("Site contact", {"name": "Name", "contact": "Role, agency", "phone": "000-000-0000"}),
|
||||
("MPM dispatch", {"name": "Jason Stedwell", "contact": "Director of Technical Services", "phone": "000-000-0000"})]),
|
||||
doc.kv([("Customer / project", "Agency · project name"), ("Installer", "MPM, or subcontractor company and lead"),
|
||||
("Access", "Hours, badge, escort, keys, lift or traffic control"),
|
||||
("Reference documents", "INS-…, DWG-…, SVY-… (bring or have on a device)")]),
|
||||
doc.note("Safety", "PPE, work at height, traffic control, lockout at the breaker before opening any enclosure.", tone="warn"),
|
||||
doc.section("Work to be performed"),
|
||||
doc.bullets(["Confirm the mounting point matches the drawing.", "Mount the sign; torque fasteners to spec.",
|
||||
"Terminate power and network; confirm the sign reports online on MP.TV."], check=True),
|
||||
doc.bom([("Parts and materials", [("Sign assembly", "Model, size", 1, "ea", "PN-0000"), ("Mounting kit", "Per drawing", 1, "kit", "PN-0000")],
|
||||
"Parts")], checks=("Staged", "Used")),
|
||||
doc.section("Return with this work order"),
|
||||
doc.bullets(["Photos: before, mounted, wired, powered with content.", "Serial number of every unit installed or removed.",
|
||||
"Any difference from the drawing, measured."], check=True),
|
||||
doc.fields([("Arrival", "Date and time"), ("Departure", "Date and time"), ("Serial(s) installed", ""), ("Serial(s) removed", ""),
|
||||
("Online on MP.TV", "Time confirmed"), ("Issues or deviations", "")], cols=2, title="Completion record"),
|
||||
doc.signatures([("Technician", "Name · company · date"), ("Site contact", "Name · date")]),
|
||||
)
|
||||
|
||||
doc.build()
|
||||
Reference in new issue
Block a user