|
|
@@ -2455,6 +2455,103 @@ def card_prefix(card: dict) -> str:
|
|
|
|
return f"{series}-{number}" if number else series
|
|
|
|
return f"{series}-{number}" if number else series
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def publication_url(data: dict) -> str:
|
|
|
|
|
|
|
|
"""Return the canonical publication URL used by the title block and QR."""
|
|
|
|
|
|
|
|
title_block = data.get("title_block") or {}
|
|
|
|
|
|
|
|
card = data.get("card") or {}
|
|
|
|
|
|
|
|
for value in (
|
|
|
|
|
|
|
|
title_block.get("url"),
|
|
|
|
|
|
|
|
data.get("publication_url"),
|
|
|
|
|
|
|
|
card.get("publication_url"),
|
|
|
|
|
|
|
|
card.get("url"),
|
|
|
|
|
|
|
|
):
|
|
|
|
|
|
|
|
if value:
|
|
|
|
|
|
|
|
return str(value).strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
host = title_block.get("url_host_uuid") or data.get("url_host_uuid")
|
|
|
|
|
|
|
|
port = title_block.get("url_port") or data.get("url_port")
|
|
|
|
|
|
|
|
document = title_block.get("doc_uuid") or data.get("doc_uuid") or card.get("uuid")
|
|
|
|
|
|
|
|
if host and document:
|
|
|
|
|
|
|
|
authority = str(host).strip()
|
|
|
|
|
|
|
|
if port:
|
|
|
|
|
|
|
|
authority += f":{str(port).strip()}"
|
|
|
|
|
|
|
|
return f"https://{authority}/{str(document).strip()}"
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def title_block_fields(data: dict) -> dict[str, str | bool]:
|
|
|
|
|
|
|
|
"""Resolve the ISO-7200-like header while keeping card metadata canonical."""
|
|
|
|
|
|
|
|
card = data["card"]
|
|
|
|
|
|
|
|
configured = data.get("title_block") or {}
|
|
|
|
|
|
|
|
url = publication_url(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def value(name: str, default: object = "") -> str:
|
|
|
|
|
|
|
|
current = configured.get(name, default)
|
|
|
|
|
|
|
|
return str(current).strip() if current is not None else ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
revision_date = str(card.get("revision_date", "")).strip()
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
"category": value("category", f"KARTA PRACY · {card.get('subject', '')}").upper(),
|
|
|
|
|
|
|
|
"standard": value("standard", "ISO 7200"),
|
|
|
|
|
|
|
|
"prepared_by": value("prepared_by", card.get("author", "—")) or "—",
|
|
|
|
|
|
|
|
"prepared_on": value("prepared_on", revision_date),
|
|
|
|
|
|
|
|
"checked_by": value("checked_by", "—") or "—",
|
|
|
|
|
|
|
|
"approved_by": value("approved_by", "—") or "—",
|
|
|
|
|
|
|
|
"title": value("title", card.get("title", "")),
|
|
|
|
|
|
|
|
"url": url,
|
|
|
|
|
|
|
|
"revision": value("revision", card.get("version", "")),
|
|
|
|
|
|
|
|
"issued_on": value("issued_on", revision_date),
|
|
|
|
|
|
|
|
"series": value("series", card_prefix(card)),
|
|
|
|
|
|
|
|
"document_type": value("document_type", "karta pracy"),
|
|
|
|
|
|
|
|
"tool": value("tool", "card-layouts"),
|
|
|
|
|
|
|
|
"sheet": value("sheet", "1 / 1"),
|
|
|
|
|
|
|
|
"show_qr": bool(configured.get("show_qr", bool(url))),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@lru_cache(maxsize=32)
|
|
|
|
|
|
|
|
def qr_svg(value: str) -> str:
|
|
|
|
|
|
|
|
"""Generate a self-contained QR SVG without a network dependency.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ReportLab ships a standards-compliant QR encoder on the supported
|
|
|
|
|
|
|
|
workstation image. The TeX renderer uses its native qrcode package;
|
|
|
|
|
|
|
|
HTML receives the same payload as inline SVG so the generated card is
|
|
|
|
|
|
|
|
fully portable.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
if not value:
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
from reportlab.graphics.barcode.qr import QrCodeWidget
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
widget = QrCodeWidget(value=value, barLevel="L")
|
|
|
|
|
|
|
|
widget.qr.make()
|
|
|
|
|
|
|
|
modules = widget.qr.modules
|
|
|
|
|
|
|
|
size = len(modules)
|
|
|
|
|
|
|
|
border = 4
|
|
|
|
|
|
|
|
path_parts: list[str] = []
|
|
|
|
|
|
|
|
for row_index, row in enumerate(modules):
|
|
|
|
|
|
|
|
column = 0
|
|
|
|
|
|
|
|
while column < size:
|
|
|
|
|
|
|
|
if not row[column]:
|
|
|
|
|
|
|
|
column += 1
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
start = column
|
|
|
|
|
|
|
|
while column < size and row[column]:
|
|
|
|
|
|
|
|
column += 1
|
|
|
|
|
|
|
|
length = column - start
|
|
|
|
|
|
|
|
path_parts.append(f"M{start + border} {row_index + border}h{length}v1h-{length}z")
|
|
|
|
|
|
|
|
full_size = size + border * 2
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
|
|
f'<svg class="title-qr" viewBox="0 0 {full_size} {full_size}" role="img" '
|
|
|
|
|
|
|
|
f'aria-label="Kod QR: {html_escape(value)}" shape-rendering="crispEdges">'
|
|
|
|
|
|
|
|
'<rect width="100%" height="100%" fill="#fefefc"/>'
|
|
|
|
|
|
|
|
f'<path d="{" ".join(path_parts)}" fill="#1c1c1a"/>'
|
|
|
|
|
|
|
|
"</svg>"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def margin_code_5a(prefix: str, official: str, local: str) -> str:
|
|
|
|
def margin_code_5a(prefix: str, official: str, local: str) -> str:
|
|
|
|
if prefix == "WE":
|
|
|
|
if prefix == "WE":
|
|
|
|
return " ".join(part for part in ("WE", official) if part)
|
|
|
|
return " ".join(part for part in ("WE", official) if part)
|
|
|
@@ -2570,6 +2667,53 @@ def render_5a_section_header(number: int, title: str) -> str:
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def render_title_block_html(data: dict) -> str:
|
|
|
|
|
|
|
|
fields = title_block_fields(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cell(label: str, value: object, extra_class: str = "") -> str:
|
|
|
|
|
|
|
|
classes = "title-cell" + (f" {extra_class}" if extra_class else "")
|
|
|
|
|
|
|
|
shown = html_escape(str(value)) if value else "—"
|
|
|
|
|
|
|
|
return f'<div class="{classes}"><span>{html_escape(label)}</span><strong>{shown}</strong></div>'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
qr = qr_svg(str(fields["url"])) if fields["show_qr"] else ""
|
|
|
|
|
|
|
|
qr_cell = (
|
|
|
|
|
|
|
|
f'<a class="title-qr-cell" href="{html_escape(str(fields["url"]))}" '
|
|
|
|
|
|
|
|
f'aria-label="Otwórz adres z kodu QR">{qr}</a>'
|
|
|
|
|
|
|
|
if qr
|
|
|
|
|
|
|
|
else '<div class="title-qr-cell title-qr-empty"><span>QR</span><strong>'
|
|
|
|
|
|
|
|
+ ("brak modułu QR" if fields["url"] else "brak URL")
|
|
|
|
|
|
|
|
+ "</strong></div>"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
prepared = (
|
|
|
|
|
|
|
|
'<div class="title-cell signer"><span>Opracował</span>'
|
|
|
|
|
|
|
|
f'<strong>{html_escape(str(fields["prepared_by"]))}</strong>'
|
|
|
|
|
|
|
|
f'<em>{html_escape(str(fields["prepared_on"]))}</em></div>'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
|
|
'<section class="title-block" aria-label="Tabliczka dokumentu">'
|
|
|
|
|
|
|
|
'<div class="title-signers">'
|
|
|
|
|
|
|
|
f'{prepared}'
|
|
|
|
|
|
|
|
f'{cell("Sprawdził", fields["checked_by"], "signer")}'
|
|
|
|
|
|
|
|
f'{cell("Zatwierdził", fields["approved_by"], "signer")}'
|
|
|
|
|
|
|
|
'</div>'
|
|
|
|
|
|
|
|
'<div class="title-document">'
|
|
|
|
|
|
|
|
f'<div class="title-category"><strong>{html_escape(str(fields["category"]))}</strong><span>{html_escape(str(fields["standard"]))}</span></div>'
|
|
|
|
|
|
|
|
f'<div class="title-name"><span>Tytuł</span><strong>{html_escape(str(fields["title"]))}</strong></div>'
|
|
|
|
|
|
|
|
f'<div class="title-url"><span>URL</span><code>{html_escape(str(fields["url"])) if fields["url"] else "—"}</code></div>'
|
|
|
|
|
|
|
|
'</div>'
|
|
|
|
|
|
|
|
'<div class="title-meta">'
|
|
|
|
|
|
|
|
f'{cell("Rewizja", fields["revision"])}'
|
|
|
|
|
|
|
|
f'{cell("Data wydania", fields["issued_on"])}'
|
|
|
|
|
|
|
|
f'{cell("Seria", fields["series"])}'
|
|
|
|
|
|
|
|
f'{cell("Typ", fields["document_type"])}'
|
|
|
|
|
|
|
|
f'{cell("Narzędzie", fields["tool"])}'
|
|
|
|
|
|
|
|
f'{cell("Arkusz", fields["sheet"])}'
|
|
|
|
|
|
|
|
'</div>'
|
|
|
|
|
|
|
|
f'{qr_cell}'
|
|
|
|
|
|
|
|
'</section>'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def render_metric_header_5a_html(data: dict) -> str:
|
|
|
|
def render_metric_header_5a_html(data: dict) -> str:
|
|
|
|
card = data["card"]
|
|
|
|
card = data["card"]
|
|
|
|
fields = data.get("metric_fill_fields") or [
|
|
|
|
fields = data.get("metric_fill_fields") or [
|
|
|
@@ -2580,10 +2724,7 @@ def render_metric_header_5a_html(data: dict) -> str:
|
|
|
|
second_label = fields[1].get("label", "Klasa · data") if len(fields) > 1 else "Klasa · data"
|
|
|
|
second_label = fields[1].get("label", "Klasa · data") if len(fields) > 1 else "Klasa · data"
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
'<div class="metric-box">'
|
|
|
|
'<div class="metric-box">'
|
|
|
|
'<div class="metric-title">'
|
|
|
|
f'{render_title_block_html(data)}'
|
|
|
|
f'<div>{html_escape(card_title_line_5a(card))}</div>'
|
|
|
|
|
|
|
|
f'<span>KARTA {html_escape(card["number"])}/{html_escape(card["count"])}</span>'
|
|
|
|
|
|
|
|
'</div>'
|
|
|
|
|
|
|
|
'<div class="metric-grid">'
|
|
|
|
'<div class="metric-grid">'
|
|
|
|
f'<div><span>Projekt</span><strong>{html_escape(card["project"])}</strong></div>'
|
|
|
|
f'<div><span>Projekt</span><strong>{html_escape(card["project"])}</strong></div>'
|
|
|
|
f'<div><span>Przedmiot</span><strong>{html_escape(card["subject"])}</strong></div>'
|
|
|
|
f'<div><span>Przedmiot</span><strong>{html_escape(card["subject"])}</strong></div>'
|
|
|
@@ -2784,12 +2925,12 @@ def render_code_subblock_5a_html(block: dict) -> str:
|
|
|
|
if block.get("pass_condition"):
|
|
|
|
if block.get("pass_condition"):
|
|
|
|
pass_line = f'<p class="pass-line">PASS: {html_escape(text_5a(block.get("pass_condition", "")))}</p>'
|
|
|
|
pass_line = f'<p class="pass-line">PASS: {html_escape(text_5a(block.get("pass_condition", "")))}</p>'
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
'<div class="code-subblock">'
|
|
|
|
f'<article class="code-subblock" data-block="{html_escape(slugify_web(block.get("slug") or block.get("title")))}">'
|
|
|
|
f'<h3>{html_escape(text_5a(block.get("title", "")))}</h3>'
|
|
|
|
f'<h3>{html_escape(text_5a(block.get("title", "")))}</h3>'
|
|
|
|
f'<p>{html_escape(text_5a(block.get("context", "")))}</p>'
|
|
|
|
f'<p>{html_escape(text_5a(block.get("context", "")))}</p>'
|
|
|
|
f'{render_code_columns_5a_html(block)}'
|
|
|
|
f'{render_code_columns_5a_html(block)}'
|
|
|
|
f'{pass_line}'
|
|
|
|
f'{pass_line}'
|
|
|
|
'</div>'
|
|
|
|
'</article>'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -3259,7 +3400,7 @@ body{margin:0;background:#eceae4;display:flex;flex-direction:column;align-items:
|
|
|
|
.kw-common{border-top:1px solid #e4e2dc;margin-top:2px;padding-top:3px;font:400 7px/1.6 'JetBrains Mono',monospace;color:#8a887f}.kw-common span{color:#1c1c1a}
|
|
|
|
.kw-common{border-top:1px solid #e4e2dc;margin-top:2px;padding-top:3px;font:400 7px/1.6 'JetBrains Mono',monospace;color:#8a887f}.kw-common span{color:#1c1c1a}
|
|
|
|
.margin-panel-foot{padding:4px 8px;border-top:1px solid #d8d6d0;font:400 7px 'JetBrains Mono',monospace;color:#8a887f}
|
|
|
|
.margin-panel-foot{padding:4px 8px;border-top:1px solid #d8d6d0;font:400 7px 'JetBrains Mono',monospace;color:#8a887f}
|
|
|
|
.page-main{display:flex;flex-direction:column;min-height:0}
|
|
|
|
.page-main{display:flex;flex-direction:column;min-height:0}
|
|
|
|
.metric-box{border:1.5px solid #1c1c1a}.metric-title{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding:9px 14px;border-bottom:1px solid #1c1c1a}.metric-title div{font-size:15px;font-weight:700}.metric-title span{font:500 9.5px 'JetBrains Mono',monospace;color:#4a4944;white-space:nowrap}
|
|
|
|
.metric-box{border:1.5px solid #1c1c1a}.title-block{display:grid;grid-template-columns:1.08fr 2.55fr 1.25fr 66px;min-height:96px;border-bottom:1px solid #1c1c1a}.title-signers,.title-document{display:grid;grid-template-rows:repeat(3,minmax(0,1fr));min-width:0}.title-signers{border-right:1px solid #1c1c1a}.title-document{border-right:1px solid #1c1c1a}.title-meta{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:repeat(3,minmax(0,1fr));min-width:0}.title-cell,.title-name,.title-url{display:flex;flex-direction:column;justify-content:center;min-width:0;padding:3px 6px;border-bottom:1px solid #d8d6d0}.title-signers .title-cell:last-child,.title-document>div:last-child,.title-meta .title-cell:nth-last-child(-n+2){border-bottom:0}.title-meta .title-cell:nth-child(odd){border-right:1px solid #d8d6d0}.title-cell span,.title-name span,.title-url span{font:600 6.7px/1.15 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;color:#8a887f}.title-cell strong,.title-name strong{font-size:8.3px;line-height:1.13;font-weight:500;overflow-wrap:anywhere}.title-cell em{font:400 6.4px/1.1 'JetBrains Mono',monospace;color:#8a887f;font-style:normal}.title-category{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:2px 6px;background:#3b5a8f;color:#fefefc;font-size:7.1px;letter-spacing:.06em;overflow:hidden}.title-category strong{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.title-category span{font:400 6.1px 'JetBrains Mono',monospace;white-space:nowrap;color:#fefefc}.title-name strong{font-size:9.2px;font-weight:700}.title-url code{font:400 6.4px/1.15 'JetBrains Mono',monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#4a4944}.title-qr-cell{display:flex;align-items:center;justify-content:center;padding:5px;border-left:1px solid #1c1c1a;background:#fefefc}.title-qr{width:100%;height:100%;display:block}.title-qr-empty{flex-direction:column;text-align:center;color:#8a887f}.title-qr-empty span{font:600 7px 'JetBrains Mono',monospace}.title-qr-empty strong{font:400 6px 'JetBrains Mono',monospace}.metric-title{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding:9px 14px;border-bottom:1px solid #1c1c1a}.metric-title div{font-size:15px;font-weight:700}.metric-title span{font:500 9.5px 'JetBrains Mono',monospace;color:#4a4944;white-space:nowrap}
|
|
|
|
.metric-grid{display:grid;grid-template-columns:1fr 1fr;font-size:11px}.metric-grid div{display:flex;gap:8px;padding:4px 14px;border-bottom:1px solid #d8d6d0}.metric-grid div:nth-child(odd){border-right:1px solid #d8d6d0}.metric-grid div:nth-last-child(-n+2){border-bottom:0}.metric-grid span{font:500 8.5px/1.9 'JetBrains Mono',monospace;color:#8a887f;text-transform:uppercase;flex:none}.metric-grid strong{font-weight:400}.metric-grid i{flex:1;border-bottom:1px dotted #9a988f}
|
|
|
|
.metric-grid{display:grid;grid-template-columns:1fr 1fr;font-size:11px}.metric-grid div{display:flex;gap:8px;padding:4px 14px;border-bottom:1px solid #d8d6d0}.metric-grid div:nth-child(odd){border-right:1px solid #d8d6d0}.metric-grid div:nth-last-child(-n+2){border-bottom:0}.metric-grid span{font:500 8.5px/1.9 'JetBrains Mono',monospace;color:#8a887f;text-transform:uppercase;flex:none}.metric-grid strong{font-weight:400}.metric-grid i{flex:1;border-bottom:1px dotted #9a988f}
|
|
|
|
.plain-box{border:1px solid #1c1c1a;padding:8px 14px;margin-top:11px}.plain-title{font-weight:700;font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;margin-bottom:2px}.plain-box p{margin:0;text-align:justify}
|
|
|
|
.plain-box{border:1px solid #1c1c1a;padding:8px 14px;margin-top:11px}.plain-title{font-weight:700;font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;margin-bottom:2px}.plain-box p{margin:0;text-align:justify}
|
|
|
|
.section-box{border:1px solid #1c1c1a;margin-top:10px}.section-box:first-child{margin-top:0}.section-box.dense{margin-top:8px}.section-head{display:flex;align-items:baseline;gap:8px;padding:5px 14px;background:#f4f3ef;border-bottom:1px solid #1c1c1a}.section-head span{font:600 9px 'JetBrains Mono',monospace;color:#8a887f}.section-head strong{font-weight:700;font-size:11.5px;letter-spacing:.12em;text-transform:uppercase}.section-body{padding:7px 14px}.dense .section-body{padding:6px 12px}
|
|
|
|
.section-box{border:1px solid #1c1c1a;margin-top:10px}.section-box:first-child{margin-top:0}.section-box.dense{margin-top:8px}.section-head{display:flex;align-items:baseline;gap:8px;padding:5px 14px;background:#f4f3ef;border-bottom:1px solid #1c1c1a}.section-head span{font:600 9px 'JetBrains Mono',monospace;color:#8a887f}.section-head strong{font-weight:700;font-size:11.5px;letter-spacing:.12em;text-transform:uppercase}.section-body{padding:7px 14px}.dense .section-body{padding:6px 12px}
|
|
|
@@ -3271,7 +3412,7 @@ body{margin:0;background:#eceae4;display:flex;flex-direction:column;align-items:
|
|
|
|
.theory-table{width:100%;border-collapse:collapse;font-size:11px}.theory-table th{text-align:left;padding:3px 10px;border:1px solid #1c1c1a;background:#f4f3ef}.theory-table th:first-child{width:26%}.theory-table td{padding:3px 10px;vertical-align:top;border:1px solid #d8d6d0}.theory-table code{font:400 10px 'JetBrains Mono',monospace}
|
|
|
|
.theory-table{width:100%;border-collapse:collapse;font-size:11px}.theory-table th{text-align:left;padding:3px 10px;border:1px solid #1c1c1a;background:#f4f3ef}.theory-table th:first-child{width:26%}.theory-table td{padding:3px 10px;vertical-align:top;border:1px solid #d8d6d0}.theory-table code{font:400 10px 'JetBrains Mono',monospace}
|
|
|
|
.tech-stack{display:grid;grid-template-columns:1fr 1fr;column-gap:18px;align-items:start}.stack-column h3{font:600 8.5px 'JetBrains Mono',monospace;letter-spacing:.08em;color:#4a4944;text-transform:uppercase;padding-bottom:3px;border-bottom:1px solid #1c1c1a;margin:0}.stack-grid{display:grid;grid-template-columns:88px 1fr;font-size:10px;line-height:1.35}.stack-grid div{padding:3px 0;border-bottom:1px solid #e4e2dc}.stack-grid div:nth-last-child(-n+2){border-bottom:0}.stack-grid code{font:400 9.5px/1.45 'JetBrains Mono',monospace}
|
|
|
|
.tech-stack{display:grid;grid-template-columns:1fr 1fr;column-gap:18px;align-items:start}.stack-column h3{font:600 8.5px 'JetBrains Mono',monospace;letter-spacing:.08em;color:#4a4944;text-transform:uppercase;padding-bottom:3px;border-bottom:1px solid #1c1c1a;margin:0}.stack-grid{display:grid;grid-template-columns:88px 1fr;font-size:10px;line-height:1.35}.stack-grid div{padding:3px 0;border-bottom:1px solid #e4e2dc}.stack-grid div:nth-last-child(-n+2){border-bottom:0}.stack-grid code{font:400 9.5px/1.45 'JetBrains Mono',monospace}
|
|
|
|
.part-banner{border:1.5px solid #1c1c1a;background:#f4f3ef;padding:7px 14px;display:grid;grid-template-columns:190px 1fr;column-gap:12px;align-items:baseline}.part-banner strong{font:700 12px 'JetBrains Mono',monospace;letter-spacing:.08em}.part-banner span{font-size:12px;text-align:justify}
|
|
|
|
.part-banner{border:1.5px solid #1c1c1a;background:#f4f3ef;padding:7px 14px;display:grid;grid-template-columns:190px 1fr;column-gap:12px;align-items:baseline}.part-banner strong{font:700 12px 'JetBrains Mono',monospace;letter-spacing:.08em}.part-banner span{font-size:12px;text-align:justify}
|
|
|
|
.compact-list{margin:3px 0 0 15px;padding:0;font-size:10.5px;line-height:1.32}.compact-list li{margin:1px 0}.three-col{display:grid;grid-template-columns:1fr 1fr 1fr;column-gap:12px}.three-col h3,.model-grid h3{font:600 8.5px 'JetBrains Mono',monospace;text-transform:uppercase;color:#4a4944;margin:0 0 3px}.three-col p,.model-grid p{margin:0 0 4px;font-size:10.5px}.pass-line{margin:5px 0 0;padding-top:4px;border-top:1px solid #e4e2dc;font-size:10.5px}
|
|
|
|
.compact-list{margin:3px 0 0 15px;padding:0;font-size:10.5px;line-height:1.32}.compact-list li{margin:1px 0}.code-subblock{border:1px solid #d8d6d0;border-left:3px solid #3b5a8f;padding:5px 7px;margin-top:5px}.code-subblock:first-child{margin-top:0}.code-subblock h3{margin:0 0 2px}.code-subblock>p{margin:0 0 4px}.three-col{display:grid;grid-template-columns:1fr 1fr 1fr;column-gap:12px}.three-col h3,.model-grid h3{font:600 8.5px 'JetBrains Mono',monospace;text-transform:uppercase;color:#4a4944;margin:0 0 3px}.three-col p,.model-grid p{margin:0 0 4px;font-size:10.5px}.pass-line{margin:5px 0 0;padding-top:4px;border-top:1px solid #e4e2dc;font-size:10.5px}
|
|
|
|
.math-task{display:grid;grid-template-columns:1fr 190px;column-gap:12px;align-items:start;border-bottom:1px solid #f0efe9;padding:3px 0;font-size:10.5px}.math-task:last-child{border-bottom:0}.math-task code{font:400 9px 'JetBrains Mono',monospace;color:#3b5a8f}.math-task em{grid-column:1 / -1;color:#8a887f;font-size:9.5px}.answer-line{display:block;border-bottom:1px dotted #9a988f;height:12px;margin-top:2px}
|
|
|
|
.math-task{display:grid;grid-template-columns:1fr 190px;column-gap:12px;align-items:start;border-bottom:1px solid #f0efe9;padding:3px 0;font-size:10.5px}.math-task:last-child{border-bottom:0}.math-task code{font:400 9px 'JetBrains Mono',monospace;color:#3b5a8f}.math-task em{grid-column:1 / -1;color:#8a887f;font-size:9.5px}.answer-line{display:block;border-bottom:1px dotted #9a988f;height:12px;margin-top:2px}
|
|
|
|
.model-grid{display:grid;grid-template-columns:1.5fr 1fr;column-gap:16px}.compact-table{width:100%;border-collapse:collapse;font-size:10px;line-height:1.25}.compact-table th{background:#f4f3ef;border:1px solid #1c1c1a;padding:3px 6px;text-align:left}.compact-table td{border:1px solid #d8d6d0;padding:3px 6px;vertical-align:top}.compact-table code{font:400 9px 'JetBrains Mono',monospace}.constants td:first-child{width:68%}.check-table td:first-child,.procedure-table td:first-child,.git-gate td:first-child{width:24px;text-align:center}.check-table td:nth-child(2){width:95px}
|
|
|
|
.model-grid{display:grid;grid-template-columns:1.5fr 1fr;column-gap:16px}.compact-table{width:100%;border-collapse:collapse;font-size:10px;line-height:1.25}.compact-table th{background:#f4f3ef;border:1px solid #1c1c1a;padding:3px 6px;text-align:left}.compact-table td{border:1px solid #d8d6d0;padding:3px 6px;vertical-align:top}.compact-table code{font:400 9px 'JetBrains Mono',monospace}.constants td:first-child{width:68%}.check-table td:first-child,.procedure-table td:first-child,.git-gate td:first-child{width:24px;text-align:center}.check-table td:nth-child(2){width:95px}
|
|
|
|
.command-list{margin:0 0 0 22px;padding:0;font-size:10.5px;line-height:1.45}.command-list code{font:400 9px 'JetBrains Mono',monospace}.safety-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}.safety-card{border:1px solid #d8d6d0;padding:5px 7px}.safety-card strong{display:block;font:600 9px 'JetBrains Mono',monospace;color:#3b5a8f;margin-bottom:2px}.safety-card span{font-size:10.5px;line-height:1.32;text-align:justify}
|
|
|
|
.command-list{margin:0 0 0 22px;padding:0;font-size:10.5px;line-height:1.45}.command-list code{font:400 9px 'JetBrains Mono',monospace}.safety-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}.safety-card{border:1px solid #d8d6d0;padding:5px 7px}.safety-card strong{display:block;font:600 9px 'JetBrains Mono',monospace;color:#3b5a8f;margin-bottom:2px}.safety-card span{font-size:10.5px;line-height:1.32;text-align:justify}
|
|
|
@@ -3322,11 +3463,8 @@ def build_5b_pages_html(data: dict) -> list[dict]:
|
|
|
|
"center": "".join(
|
|
|
|
"center": "".join(
|
|
|
|
[
|
|
|
|
[
|
|
|
|
render_metric_header_5a_html(data),
|
|
|
|
render_metric_header_5a_html(data),
|
|
|
|
render_mission_5a_html(data),
|
|
|
|
|
|
|
|
render_objectives_5a_html(data),
|
|
|
|
render_objectives_5a_html(data),
|
|
|
|
render_agenda_5a_html(data),
|
|
|
|
render_agenda_5a_html(data),
|
|
|
|
render_hw_gate_5a_html(data),
|
|
|
|
|
|
|
|
render_tech_stack_5a_html(data),
|
|
|
|
|
|
|
|
]
|
|
|
|
]
|
|
|
|
),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
@@ -3335,7 +3473,10 @@ def build_5b_pages_html(data: dict) -> list[dict]:
|
|
|
|
"center": "".join(
|
|
|
|
"center": "".join(
|
|
|
|
[
|
|
|
|
[
|
|
|
|
render_running_card_header_5a_html(data),
|
|
|
|
render_running_card_header_5a_html(data),
|
|
|
|
|
|
|
|
render_mission_5a_html(data),
|
|
|
|
render_theory_5a_html(data),
|
|
|
|
render_theory_5a_html(data),
|
|
|
|
|
|
|
|
render_tech_stack_5a_html(data),
|
|
|
|
|
|
|
|
render_hw_gate_5a_html(data),
|
|
|
|
render_math_5a_html(data),
|
|
|
|
render_math_5a_html(data),
|
|
|
|
]
|
|
|
|
]
|
|
|
|
),
|
|
|
|
),
|
|
|
@@ -3470,7 +3611,7 @@ body{margin:0;background:#eceae4;display:flex;flex-direction:column;align-items:
|
|
|
|
.tree-gap-left,.tree-gap-right{min-width:0}.tree-we{font-weight:600;color:#3b5a8f;margin-top:2px}.tree-we:first-child{margin-top:0}.tree-line.level-1{padding-left:5px}.tree-line.level-2{padding-left:10px;color:#4a4944}
|
|
|
|
.tree-gap-left,.tree-gap-right{min-width:0}.tree-we{font-weight:600;color:#3b5a8f;margin-top:2px}.tree-we:first-child{margin-top:0}.tree-line.level-1{padding-left:5px}.tree-line.level-2{padding-left:10px;color:#4a4944}
|
|
|
|
.page-main{display:flex;flex-direction:column;min-height:0;justify-content:space-between}
|
|
|
|
.page-main{display:flex;flex-direction:column;min-height:0;justify-content:space-between}
|
|
|
|
.page.page-technical{--page-rule-left:104px;--page-rule-width:915px;width:1123px;height:794px}.page-technical .technical-page-grid{grid-template-columns:77px 21px minmax(0,1fr) 21px 77px}.technical-main{display:flex;min-height:0}.technical-section{flex:1;display:flex;flex-direction:column;margin-top:0}.technical-section .section-body{flex:1;min-height:0;padding:6px 8px}.technical-sheet-figure{height:100%;min-height:0;margin:0;display:flex;flex-direction:column;gap:4px}.technical-sheet-link{flex:1;min-height:0;display:flex;align-items:center;justify-content:center}.technical-sheet-figure img{width:100%;height:100%;object-fit:contain;border:1px solid #d8d6d0;background:white}.technical-sheet-figure figcaption{display:flex;justify-content:space-between;align-items:baseline;gap:10px;font-size:7.2px;line-height:1.18;color:#4a4944}.technical-sheet-figure figcaption span{min-width:0}.technical-sheet-missing{flex:1;display:flex;align-items:center;justify-content:center;border:1px solid #d8d6d0;color:#8a887f;font:600 8px 'JetBrains Mono',monospace;text-transform:uppercase}
|
|
|
|
.page.page-technical{--page-rule-left:104px;--page-rule-width:915px;width:1123px;height:794px}.page-technical .technical-page-grid{grid-template-columns:77px 21px minmax(0,1fr) 21px 77px}.technical-main{display:flex;min-height:0}.technical-section{flex:1;display:flex;flex-direction:column;margin-top:0}.technical-section .section-body{flex:1;min-height:0;padding:6px 8px}.technical-sheet-figure{height:100%;min-height:0;margin:0;display:flex;flex-direction:column;gap:4px}.technical-sheet-link{flex:1;min-height:0;display:flex;align-items:center;justify-content:center}.technical-sheet-figure img{width:100%;height:100%;object-fit:contain;border:1px solid #d8d6d0;background:white}.technical-sheet-figure figcaption{display:flex;justify-content:space-between;align-items:baseline;gap:10px;font-size:7.2px;line-height:1.18;color:#4a4944}.technical-sheet-figure figcaption span{min-width:0}.technical-sheet-missing{flex:1;display:flex;align-items:center;justify-content:center;border:1px solid #d8d6d0;color:#8a887f;font:600 8px 'JetBrains Mono',monospace;text-transform:uppercase}
|
|
|
|
.metric-box{border:1.4px solid #1c1c1a}.metric-title{display:flex;justify-content:space-between;align-items:baseline;gap:8px;padding:7px 10px;border-bottom:1px solid #1c1c1a}.metric-title div{font-size:12.5px;font-weight:700}.metric-title span{font:500 8px 'JetBrains Mono',monospace;color:#4a4944;white-space:nowrap}
|
|
|
|
.metric-box{border:1.4px solid #1c1c1a}.title-block{display:grid;grid-template-columns:1.0fr 2.35fr 1.24fr 52px;min-height:82px;border-bottom:1px solid #1c1c1a}.title-signers,.title-document{display:grid;grid-template-rows:repeat(3,minmax(0,1fr));min-width:0}.title-signers{border-right:1px solid #1c1c1a}.title-document{border-right:1px solid #1c1c1a}.title-meta{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:repeat(3,minmax(0,1fr));min-width:0}.title-cell,.title-name,.title-url{display:flex;flex-direction:column;justify-content:center;min-width:0;padding:2px 4px;border-bottom:1px solid #d8d6d0}.title-signers .title-cell:last-child,.title-document>div:last-child,.title-meta .title-cell:nth-last-child(-n+2){border-bottom:0}.title-meta .title-cell:nth-child(odd){border-right:1px solid #d8d6d0}.title-cell span,.title-name span,.title-url span{font:600 5.4px/1.12 'JetBrains Mono',monospace;letter-spacing:.07em;text-transform:uppercase;color:#8a887f}.title-cell strong,.title-name strong{font-size:6.9px;line-height:1.1;font-weight:500;overflow-wrap:anywhere}.title-cell em{font:400 5.2px/1.05 'JetBrains Mono',monospace;color:#8a887f;font-style:normal}.title-category{display:flex;align-items:center;justify-content:space-between;gap:4px;padding:2px 4px;background:#3b5a8f;color:#fefefc;font-size:5.9px;letter-spacing:.04em;overflow:hidden}.title-category strong{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.title-category span{font:400 5.1px 'JetBrains Mono',monospace;white-space:nowrap;color:#fefefc}.title-name strong{font-size:7.6px;font-weight:700}.title-url code{font:400 5.4px/1.12 'JetBrains Mono',monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#4a4944}.title-qr-cell{display:flex;align-items:center;justify-content:center;padding:4px;border-left:1px solid #1c1c1a;background:#fefefc}.title-qr{width:100%;height:100%;display:block}.title-qr-empty{flex-direction:column;text-align:center;color:#8a887f}.title-qr-empty span{font:600 6px 'JetBrains Mono',monospace}.title-qr-empty strong{font:400 5px 'JetBrains Mono',monospace}.metric-title{display:flex;justify-content:space-between;align-items:baseline;gap:8px;padding:7px 10px;border-bottom:1px solid #1c1c1a}.metric-title div{font-size:12.5px;font-weight:700}.metric-title span{font:500 8px 'JetBrains Mono',monospace;color:#4a4944;white-space:nowrap}
|
|
|
|
.metric-grid{display:grid;grid-template-columns:1fr 1fr;font-size:9.4px}.metric-grid div{display:flex;gap:6px;padding:3px 10px;border-bottom:1px solid #d8d6d0}.metric-grid div:nth-child(odd){border-right:1px solid #d8d6d0}.metric-grid div:nth-last-child(-n+2){border-bottom:0}.metric-grid span{font:500 7.2px/1.7 'JetBrains Mono',monospace;color:#8a887f;text-transform:uppercase;flex:none}.metric-grid strong{font-weight:400}.metric-grid i{flex:1;border-bottom:1px dotted #9a988f}
|
|
|
|
.metric-grid{display:grid;grid-template-columns:1fr 1fr;font-size:9.4px}.metric-grid div{display:flex;gap:6px;padding:3px 10px;border-bottom:1px solid #d8d6d0}.metric-grid div:nth-child(odd){border-right:1px solid #d8d6d0}.metric-grid div:nth-last-child(-n+2){border-bottom:0}.metric-grid span{font:500 7.2px/1.7 'JetBrains Mono',monospace;color:#8a887f;text-transform:uppercase;flex:none}.metric-grid strong{font-weight:400}.metric-grid i{flex:1;border-bottom:1px dotted #9a988f}
|
|
|
|
.plain-box{border:1px solid #1c1c1a;padding:6px 10px;margin-top:7px}.plain-title{font-weight:700;font-size:9.8px;letter-spacing:.12em;text-transform:uppercase;margin-bottom:1px}.plain-box p{margin:0;text-align:justify}
|
|
|
|
.plain-box{border:1px solid #1c1c1a;padding:6px 10px;margin-top:7px}.plain-title{font-weight:700;font-size:9.8px;letter-spacing:.12em;text-transform:uppercase;margin-bottom:1px}.plain-box p{margin:0;text-align:justify}
|
|
|
|
.section-box{border:1px solid #1c1c1a;margin-top:7px}.section-box:first-child{margin-top:0}.section-box.dense{margin-top:6px}.section-head{display:flex;align-items:baseline;gap:6px;padding:4px 10px;background:#f4f3ef;border-bottom:1px solid #1c1c1a}.section-head span{font:600 7.8px 'JetBrains Mono',monospace;color:#8a887f}.section-head strong{font-weight:700;font-size:9.6px;letter-spacing:.12em;text-transform:uppercase}.section-body{padding:6px 10px}.dense .section-body{padding:5px 9px}
|
|
|
|
.section-box{border:1px solid #1c1c1a;margin-top:7px}.section-box:first-child{margin-top:0}.section-box.dense{margin-top:6px}.section-head{display:flex;align-items:baseline;gap:6px;padding:4px 10px;background:#f4f3ef;border-bottom:1px solid #1c1c1a}.section-head span{font:600 7.8px 'JetBrains Mono',monospace;color:#8a887f}.section-head strong{font-weight:700;font-size:9.6px;letter-spacing:.12em;text-transform:uppercase}.section-body{padding:6px 10px}.dense .section-body{padding:5px 9px}
|
|
|
@@ -3482,7 +3623,7 @@ body{margin:0;background:#eceae4;display:flex;flex-direction:column;align-items:
|
|
|
|
.theory-table{width:100%;border-collapse:collapse;font-size:9.4px}.theory-table th{text-align:left;padding:2px 7px;border:1px solid #1c1c1a;background:#f4f3ef}.theory-table th:first-child{width:31%}.theory-table td{padding:2px 7px;vertical-align:top;border:1px solid #d8d6d0}.theory-table code{font:400 8px 'JetBrains Mono',monospace}
|
|
|
|
.theory-table{width:100%;border-collapse:collapse;font-size:9.4px}.theory-table th{text-align:left;padding:2px 7px;border:1px solid #1c1c1a;background:#f4f3ef}.theory-table th:first-child{width:31%}.theory-table td{padding:2px 7px;vertical-align:top;border:1px solid #d8d6d0}.theory-table code{font:400 8px 'JetBrains Mono',monospace}
|
|
|
|
.tech-stack{display:grid;grid-template-columns:1fr 1fr;column-gap:10px;align-items:start}.stack-column h3{font:600 7.2px 'JetBrains Mono',monospace;letter-spacing:.08em;color:#4a4944;text-transform:uppercase;padding-bottom:2px;border-bottom:1px solid #1c1c1a;margin:0}.stack-grid{display:grid;grid-template-columns:70px 1fr;font-size:8.6px;line-height:1.25}.stack-grid div{padding:2px 0;border-bottom:1px solid #e4e2dc}.stack-grid div:nth-last-child(-n+2){border-bottom:0}.stack-grid code{font:400 7.7px/1.35 'JetBrains Mono',monospace}
|
|
|
|
.tech-stack{display:grid;grid-template-columns:1fr 1fr;column-gap:10px;align-items:start}.stack-column h3{font:600 7.2px 'JetBrains Mono',monospace;letter-spacing:.08em;color:#4a4944;text-transform:uppercase;padding-bottom:2px;border-bottom:1px solid #1c1c1a;margin:0}.stack-grid{display:grid;grid-template-columns:70px 1fr;font-size:8.6px;line-height:1.25}.stack-grid div{padding:2px 0;border-bottom:1px solid #e4e2dc}.stack-grid div:nth-last-child(-n+2){border-bottom:0}.stack-grid code{font:400 7.7px/1.35 'JetBrains Mono',monospace}
|
|
|
|
.part-banner{border:1.4px solid #1c1c1a;background:#f4f3ef;padding:5px 10px;display:grid;grid-template-columns:126px 1fr;column-gap:9px;align-items:baseline}.part-banner strong{font:700 9px 'JetBrains Mono',monospace;letter-spacing:.08em}.part-banner span{font-size:9.4px;text-align:justify}
|
|
|
|
.part-banner{border:1.4px solid #1c1c1a;background:#f4f3ef;padding:5px 10px;display:grid;grid-template-columns:126px 1fr;column-gap:9px;align-items:baseline}.part-banner strong{font:700 9px 'JetBrains Mono',monospace;letter-spacing:.08em}.part-banner span{font-size:9.4px;text-align:justify}
|
|
|
|
.compact-list{margin:2px 0 0 13px;padding:0;font-size:8.7px;line-height:1.25}.compact-list li{margin:1px 0}.three-col,.connection-notes{display:grid;grid-template-columns:1fr 1fr 1fr;column-gap:8px}.connection-notes{grid-template-columns:1fr 1fr;margin-top:4px}.three-col h3,.model-grid h3,.connection-notes h3{font:600 7.2px 'JetBrains Mono',monospace;text-transform:uppercase;color:#4a4944;margin:0 0 2px}.three-col p,.model-grid p{margin:0 0 3px;font-size:8.8px}.pass-line{margin:4px 0 0;padding-top:3px;border-top:1px solid #e4e2dc;font-size:8.6px}
|
|
|
|
.compact-list{margin:2px 0 0 13px;padding:0;font-size:8.7px;line-height:1.25}.compact-list li{margin:1px 0}.code-subblock{border:1px solid #d8d6d0;border-left:3px solid #3b5a8f;padding:4px 5px;margin-top:4px}.code-subblock:first-child{margin-top:0}.code-subblock h3{margin:0 0 2px}.code-subblock>p{margin:0 0 3px}.three-col,.connection-notes{display:grid;grid-template-columns:1fr 1fr 1fr;column-gap:8px}.connection-notes{grid-template-columns:1fr 1fr;margin-top:4px}.three-col h3,.model-grid h3,.connection-notes h3{font:600 7.2px 'JetBrains Mono',monospace;text-transform:uppercase;color:#4a4944;margin:0 0 2px}.three-col p,.model-grid p{margin:0 0 3px;font-size:8.8px}.pass-line{margin:4px 0 0;padding-top:3px;border-top:1px solid #e4e2dc;font-size:8.6px}
|
|
|
|
.math-task{display:grid;grid-template-columns:1fr 126px;column-gap:8px;align-items:start;border-bottom:1px solid #f0efe9;padding:3px 0;font-size:9px}.math-task:last-child{border-bottom:0}.math-task code{font:400 7.5px 'JetBrains Mono',monospace;color:#3b5a8f}.math-task em{grid-column:1 / -1;color:#8a887f;font-size:7.8px}.answer-line{display:block;border-bottom:1px dotted #9a988f;height:12px;margin-top:2px}
|
|
|
|
.math-task{display:grid;grid-template-columns:1fr 126px;column-gap:8px;align-items:start;border-bottom:1px solid #f0efe9;padding:3px 0;font-size:9px}.math-task:last-child{border-bottom:0}.math-task code{font:400 7.5px 'JetBrains Mono',monospace;color:#3b5a8f}.math-task em{grid-column:1 / -1;color:#8a887f;font-size:7.8px}.answer-line{display:block;border-bottom:1px dotted #9a988f;height:12px;margin-top:2px}
|
|
|
|
.model-grid{display:grid;grid-template-columns:1.25fr 1fr;column-gap:10px}.compact-table{width:100%;border-collapse:collapse;font-size:8.4px;line-height:1.18}.compact-table th{background:#f4f3ef;border:1px solid #1c1c1a;padding:2px 4px;text-align:left}.compact-table td{border:1px solid #d8d6d0;padding:2px 4px;vertical-align:top}.compact-table code{font:400 7.5px 'JetBrains Mono',monospace}.constants td:first-child{width:66%}.check-table td:first-child,.procedure-table td:first-child,.git-gate td:first-child{width:20px;text-align:center}.check-table td:nth-child(2){width:70px}.procedure-table td{height:25px}
|
|
|
|
.model-grid{display:grid;grid-template-columns:1.25fr 1fr;column-gap:10px}.compact-table{width:100%;border-collapse:collapse;font-size:8.4px;line-height:1.18}.compact-table th{background:#f4f3ef;border:1px solid #1c1c1a;padding:2px 4px;text-align:left}.compact-table td{border:1px solid #d8d6d0;padding:2px 4px;vertical-align:top}.compact-table code{font:400 7.5px 'JetBrains Mono',monospace}.constants td:first-child{width:66%}.check-table td:first-child,.procedure-table td:first-child,.git-gate td:first-child{width:20px;text-align:center}.check-table td:nth-child(2){width:70px}.procedure-table td{height:25px}
|
|
|
|
.command-list{margin:0 0 0 18px;padding:0;font-size:8.8px;line-height:1.32}.command-list code{font:400 7.5px 'JetBrains Mono',monospace}.safety-grid{display:grid;grid-template-columns:1fr 1fr;gap:5px}.safety-card{border:1px solid #d8d6d0;padding:4px 5px}.safety-card strong{display:block;font:600 7.5px 'JetBrains Mono',monospace;color:#3b5a8f;margin-bottom:1px}.safety-card span{font-size:8.4px;line-height:1.22;text-align:justify}
|
|
|
|
.command-list{margin:0 0 0 18px;padding:0;font-size:8.8px;line-height:1.32}.command-list code{font:400 7.5px 'JetBrains Mono',monospace}.safety-grid{display:grid;grid-template-columns:1fr 1fr;gap:5px}.safety-card{border:1px solid #d8d6d0;padding:4px 5px}.safety-card strong{display:block;font:600 7.5px 'JetBrains Mono',monospace;color:#3b5a8f;margin-bottom:1px}.safety-card span{font-size:8.4px;line-height:1.22;text-align:justify}
|
|
|
@@ -3531,6 +3672,7 @@ def render_preamble_5a(card: dict) -> list[str]:
|
|
|
|
r"\usepackage[polish]{babel}",
|
|
|
|
r"\usepackage[polish]{babel}",
|
|
|
|
r"\usepackage{array}",
|
|
|
|
r"\usepackage{array}",
|
|
|
|
r"\usepackage{tabularx}",
|
|
|
|
r"\usepackage{tabularx}",
|
|
|
|
|
|
|
|
r"\usepackage{multirow}",
|
|
|
|
r"\usepackage{xcolor}",
|
|
|
|
r"\usepackage{xcolor}",
|
|
|
|
r"\usepackage{amssymb}",
|
|
|
|
r"\usepackage{amssymb}",
|
|
|
|
r"\usepackage{graphicx}",
|
|
|
|
r"\usepackage{graphicx}",
|
|
|
@@ -3539,6 +3681,7 @@ def render_preamble_5a(card: dict) -> list[str]:
|
|
|
|
r"\IfFileExists{stix2.sty}{\usepackage{stix2}}{\usepackage{lmodern}}",
|
|
|
|
r"\IfFileExists{stix2.sty}{\usepackage{stix2}}{\usepackage{lmodern}}",
|
|
|
|
r"\IfFileExists{inconsolata.sty}{\usepackage{inconsolata}}{\usepackage{lmodern}}",
|
|
|
|
r"\IfFileExists{inconsolata.sty}{\usepackage{inconsolata}}{\usepackage{lmodern}}",
|
|
|
|
r"\usepackage[most]{tcolorbox}",
|
|
|
|
r"\usepackage[most]{tcolorbox}",
|
|
|
|
|
|
|
|
r"\usepackage{qrcode}",
|
|
|
|
r"\usepackage{hyperref}",
|
|
|
|
r"\usepackage{hyperref}",
|
|
|
|
r"\hypersetup{hidelinks}",
|
|
|
|
r"\hypersetup{hidelinks}",
|
|
|
|
r"\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}}",
|
|
|
|
r"\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}}",
|
|
|
@@ -3567,6 +3710,7 @@ def render_preamble_5a(card: dict) -> list[str]:
|
|
|
|
r"\newcommand{\FiveAWriteLine}{\par\noindent\dotfill}",
|
|
|
|
r"\newcommand{\FiveAWriteLine}{\par\noindent\dotfill}",
|
|
|
|
r"\newtcolorbox{FiveASection}[2]{enhanced,arc=0pt,boxrule=0.35pt,colback=ESCPaper,colframe=ESCInk,boxsep=0pt,left=2.8mm,right=2.8mm,top=1.2mm,bottom=1.2mm,colbacktitle=ESCHeader,coltitle=ESCInk,fonttitle=\bfseries\footnotesize,title={\ttfamily\textcolor{ESCMuted}{\S#1}\quad #2}}",
|
|
|
|
r"\newtcolorbox{FiveASection}[2]{enhanced,arc=0pt,boxrule=0.35pt,colback=ESCPaper,colframe=ESCInk,boxsep=0pt,left=2.8mm,right=2.8mm,top=1.2mm,bottom=1.2mm,colbacktitle=ESCHeader,coltitle=ESCInk,fonttitle=\bfseries\footnotesize,title={\ttfamily\textcolor{ESCMuted}{\S#1}\quad #2}}",
|
|
|
|
r"\newtcolorbox{FiveAPart}{enhanced,arc=0pt,boxrule=0.6pt,colback=ESCHeader,colframe=ESCInk,boxsep=0pt,left=2.8mm,right=2.8mm,top=1.2mm,bottom=1.2mm}",
|
|
|
|
r"\newtcolorbox{FiveAPart}{enhanced,arc=0pt,boxrule=0.6pt,colback=ESCHeader,colframe=ESCInk,boxsep=0pt,left=2.8mm,right=2.8mm,top=1.2mm,bottom=1.2mm}",
|
|
|
|
|
|
|
|
r"\newtcolorbox{CardContentBlock}{enhanced,arc=0pt,boxrule=0.28pt,colback=ESCPaper,colframe=ESCLine,boxsep=0pt,left=1.4mm,right=1.4mm,top=0.8mm,bottom=0.8mm}",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
@@ -3608,18 +3752,45 @@ def render_margin_tree_panel_5a_tex(data: dict, column: str) -> list[str]:
|
|
|
|
|
|
|
|
|
|
|
|
def render_metric_header_5a_tex(data: dict) -> list[str]:
|
|
|
|
def render_metric_header_5a_tex(data: dict) -> list[str]:
|
|
|
|
card = data["card"]
|
|
|
|
card = data["card"]
|
|
|
|
|
|
|
|
title_fields = title_block_fields(data)
|
|
|
|
fields = data.get("metric_fill_fields") or [{"label": "Uczeń"}, {"label": "Klasa · data"}]
|
|
|
|
fields = data.get("metric_fill_fields") or [{"label": "Uczeń"}, {"label": "Klasa · data"}]
|
|
|
|
first_label = fields[0].get("label", "Uczeń") if fields else "Uczeń"
|
|
|
|
first_label = fields[0].get("label", "Uczeń") if fields else "Uczeń"
|
|
|
|
second_label = fields[1].get("label", "Klasa · data") if len(fields) > 1 else "Klasa · data"
|
|
|
|
second_label = fields[1].get("label", "Klasa · data") if len(fields) > 1 else "Klasa · data"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def title_cell(label: str, value: object, mono: bool = False) -> str:
|
|
|
|
|
|
|
|
content = tex_escape(str(value)) if mono else tex_5a(value)
|
|
|
|
|
|
|
|
face = r"\ttfamily" if mono else ""
|
|
|
|
|
|
|
|
return rf"{{\tiny\ttfamily\textcolor{{ESCMuted}}{{{tex_escape(label.upper())}}}}}\par{{\scriptsize {face} {content}}}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
url = str(title_fields["url"])
|
|
|
|
|
|
|
|
safe_qr_url = re.fullmatch(r"https?://[A-Za-z0-9._~:/?&=+\-]+", url)
|
|
|
|
|
|
|
|
qr = (
|
|
|
|
|
|
|
|
rf"\qrcode[height=15.5mm,level=L]{{{url}}}"
|
|
|
|
|
|
|
|
if title_fields["show_qr"] and safe_qr_url
|
|
|
|
|
|
|
|
else r"{\tiny\ttfamily\textcolor{ESCMuted}{QR: brak URL}}"
|
|
|
|
|
|
|
|
)
|
|
|
|
return [
|
|
|
|
return [
|
|
|
|
r"\begin{tcolorbox}[enhanced,arc=0pt,boxrule=0.6pt,colback=ESCPaper,colframe=ESCInk,boxsep=0pt,left=2.8mm,right=2.8mm,top=1.8mm,bottom=1.8mm]",
|
|
|
|
r"\begin{tcolorbox}[enhanced,arc=0pt,boxrule=0.6pt,colback=ESCPaper,colframe=ESCInk,boxsep=0pt,left=0pt,right=0pt,top=0pt,bottom=0pt]",
|
|
|
|
rf"{{\bfseries {tex_5a(card_title_line_5a(card))}}}\hfill{{\scriptsize\ttfamily KARTA {tex_escape(card['number'])}/{tex_escape(card['count'])}}}\par",
|
|
|
|
r"\renewcommand{\arraystretch}{1.08}\scriptsize",
|
|
|
|
r"\vspace{0.2em}\hrule\vspace{0.2em}",
|
|
|
|
r"\noindent\begin{minipage}[t]{12.40cm}",
|
|
|
|
r"\scriptsize",
|
|
|
|
r"\begin{tabularx}{\linewidth}{|p{2.55cm}|Y|p{1.55cm}|p{1.75cm}|}",
|
|
|
|
r"\begin{tabularx}{\linewidth}{@{}>{\ttfamily\textcolor{ESCMuted}}p{2.2cm}X>{\ttfamily\textcolor{ESCMuted}}p{2.5cm}X@{}}",
|
|
|
|
r"\hline",
|
|
|
|
rf"Projekt & {tex_5a(card['project'])} & Przedmiot & {tex_5a(card['subject'])} \\",
|
|
|
|
rf"{title_cell('Opracował', title_fields['prepared_by'])}\newline{{\tiny\ttfamily\textcolor{{ESCMuted}}{{{tex_escape(str(title_fields['prepared_on']))}}}}} & "
|
|
|
|
rf"{tex_5a(first_label)} & \FiveAFill & {tex_5a(second_label)} & \FiveAFill \\",
|
|
|
|
rf"{{\scriptsize\bfseries {tex_5a(title_fields['category'])}}}\hfill{{\tiny\ttfamily {tex_escape(str(title_fields['standard']))}}} & "
|
|
|
|
|
|
|
|
rf"{title_cell('Rewizja', title_fields['revision'], True)} & {title_cell('Data wydania', title_fields['issued_on'], True)} \\\hline",
|
|
|
|
|
|
|
|
rf"{title_cell('Sprawdził', title_fields['checked_by'])} & "
|
|
|
|
|
|
|
|
rf"{title_cell('Tytuł', title_fields['title'])} & "
|
|
|
|
|
|
|
|
rf"{title_cell('Seria', title_fields['series'], True)} & {title_cell('Typ', title_fields['document_type'])} \\\hline",
|
|
|
|
|
|
|
|
rf"{title_cell('Zatwierdził', title_fields['approved_by'])} & "
|
|
|
|
|
|
|
|
rf"{title_cell('URL', url or '—', True)} & "
|
|
|
|
|
|
|
|
rf"{title_cell('Narzędzie', title_fields['tool'])} & {title_cell('Arkusz', title_fields['sheet'], True)} \\\hline",
|
|
|
|
r"\end{tabularx}",
|
|
|
|
r"\end{tabularx}",
|
|
|
|
|
|
|
|
r"\end{minipage}\hfill",
|
|
|
|
|
|
|
|
r"\begin{minipage}[t][15.5mm][c]{1.95cm}\centering",
|
|
|
|
|
|
|
|
qr,
|
|
|
|
|
|
|
|
r"\end{minipage}",
|
|
|
|
|
|
|
|
r"\hspace{2.0mm}{\tiny\ttfamily\textcolor{ESCMuted}{PROJEKT}}\hspace{0.8mm}{\scriptsize " + tex_5a(card["project"]) + r"}\hfill{\tiny\ttfamily\textcolor{ESCMuted}{PRZEDMIOT}}\hspace{0.8mm}{\scriptsize " + tex_5a(card["subject"]) + r"}\hspace{2.0mm}\par",
|
|
|
|
|
|
|
|
rf"\hspace{{2.0mm}}{{\tiny\ttfamily\textcolor{{ESCMuted}}{{{tex_escape(first_label.upper())}}}}}\hspace{{0.8mm}}\FiveAFill\hfill{{\tiny\ttfamily\textcolor{{ESCMuted}}{{{tex_escape(second_label.upper())}}}}}\hspace{{0.8mm}}\FiveAFill\hspace{{2.0mm}}\par",
|
|
|
|
r"\end{tcolorbox}",
|
|
|
|
r"\end{tcolorbox}",
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
@@ -3776,14 +3947,15 @@ def render_code_columns_5a_tex(block: dict) -> list[str]:
|
|
|
|
|
|
|
|
|
|
|
|
def render_code_subblock_5a_tex(block: dict) -> list[str]:
|
|
|
|
def render_code_subblock_5a_tex(block: dict) -> list[str]:
|
|
|
|
body = [
|
|
|
|
body = [
|
|
|
|
rf"\par\textbf{{{tex_5a(block.get('title', ''))}}}\par",
|
|
|
|
r"\begin{CardContentBlock}",
|
|
|
|
|
|
|
|
rf"\textbf{{{tex_5a(block.get('title', ''))}}}\par",
|
|
|
|
tex_5a(block.get("context", "")),
|
|
|
|
tex_5a(block.get("context", "")),
|
|
|
|
r"\par\vspace{0.15em}",
|
|
|
|
r"\par\vspace{0.15em}",
|
|
|
|
*render_code_columns_5a_tex(block),
|
|
|
|
*render_code_columns_5a_tex(block),
|
|
|
|
]
|
|
|
|
]
|
|
|
|
if block.get("pass_condition"):
|
|
|
|
if block.get("pass_condition"):
|
|
|
|
body.append(rf"\par{{\tiny\ttfamily\textcolor{{ESCAccent}}{{PASS: {tex_5a(block.get('pass_condition', ''))}}}}}")
|
|
|
|
body.append(rf"\par{{\tiny\ttfamily\textcolor{{ESCAccent}}{{PASS: {tex_5a(block.get('pass_condition', ''))}}}}}")
|
|
|
|
body.append(r"\vspace{0.25em}")
|
|
|
|
body.extend([r"\end{CardContentBlock}", r"\vspace{0.25em}"])
|
|
|
|
return body
|
|
|
|
return body
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -4186,6 +4358,7 @@ def render_preamble_5b(card: dict) -> list[str]:
|
|
|
|
r"\usepackage[polish]{babel}",
|
|
|
|
r"\usepackage[polish]{babel}",
|
|
|
|
r"\usepackage{array}",
|
|
|
|
r"\usepackage{array}",
|
|
|
|
r"\usepackage{tabularx}",
|
|
|
|
r"\usepackage{tabularx}",
|
|
|
|
|
|
|
|
r"\usepackage{multirow}",
|
|
|
|
r"\usepackage{xcolor}",
|
|
|
|
r"\usepackage{xcolor}",
|
|
|
|
r"\usepackage{amssymb}",
|
|
|
|
r"\usepackage{amssymb}",
|
|
|
|
r"\usepackage{graphicx}",
|
|
|
|
r"\usepackage{graphicx}",
|
|
|
@@ -4195,6 +4368,7 @@ def render_preamble_5b(card: dict) -> list[str]:
|
|
|
|
r"\IfFileExists{stix2.sty}{\usepackage{stix2}}{\usepackage{lmodern}}",
|
|
|
|
r"\IfFileExists{stix2.sty}{\usepackage{stix2}}{\usepackage{lmodern}}",
|
|
|
|
r"\IfFileExists{inconsolata.sty}{\usepackage{inconsolata}}{\usepackage{lmodern}}",
|
|
|
|
r"\IfFileExists{inconsolata.sty}{\usepackage{inconsolata}}{\usepackage{lmodern}}",
|
|
|
|
r"\usepackage[most]{tcolorbox}",
|
|
|
|
r"\usepackage[most]{tcolorbox}",
|
|
|
|
|
|
|
|
r"\usepackage{qrcode}",
|
|
|
|
r"\usepackage{hyperref}",
|
|
|
|
r"\usepackage{hyperref}",
|
|
|
|
r"\hypersetup{hidelinks}",
|
|
|
|
r"\hypersetup{hidelinks}",
|
|
|
|
r"\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}}",
|
|
|
|
r"\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}}",
|
|
|
@@ -4223,6 +4397,7 @@ def render_preamble_5b(card: dict) -> list[str]:
|
|
|
|
r"\newcommand{\FiveAWriteLine}{\par\noindent\dotfill}",
|
|
|
|
r"\newcommand{\FiveAWriteLine}{\par\noindent\dotfill}",
|
|
|
|
r"\newtcolorbox{FiveASection}[2]{enhanced,arc=0pt,boxrule=0.35pt,colback=ESCPaper,colframe=ESCInk,boxsep=0pt,left=2.0mm,right=2.0mm,top=1.0mm,bottom=1.0mm,colbacktitle=ESCHeader,coltitle=ESCInk,fonttitle=\bfseries\scriptsize,title={\ttfamily\textcolor{ESCMuted}{#1.}\quad #2}}",
|
|
|
|
r"\newtcolorbox{FiveASection}[2]{enhanced,arc=0pt,boxrule=0.35pt,colback=ESCPaper,colframe=ESCInk,boxsep=0pt,left=2.0mm,right=2.0mm,top=1.0mm,bottom=1.0mm,colbacktitle=ESCHeader,coltitle=ESCInk,fonttitle=\bfseries\scriptsize,title={\ttfamily\textcolor{ESCMuted}{#1.}\quad #2}}",
|
|
|
|
r"\newtcolorbox{FiveAPart}{enhanced,arc=0pt,boxrule=0.55pt,colback=ESCHeader,colframe=ESCInk,boxsep=0pt,left=2.0mm,right=2.0mm,top=1.0mm,bottom=1.0mm}",
|
|
|
|
r"\newtcolorbox{FiveAPart}{enhanced,arc=0pt,boxrule=0.55pt,colback=ESCHeader,colframe=ESCInk,boxsep=0pt,left=2.0mm,right=2.0mm,top=1.0mm,bottom=1.0mm}",
|
|
|
|
|
|
|
|
r"\newtcolorbox{CardContentBlock}{enhanced,arc=0pt,boxrule=0.28pt,colback=ESCPaper,colframe=ESCLine,boxsep=0pt,left=1.1mm,right=1.1mm,top=0.7mm,bottom=0.7mm}",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
@@ -4447,15 +4622,9 @@ def build_5b_pages_tex(data: dict) -> list[list[str]]:
|
|
|
|
"center": [
|
|
|
|
"center": [
|
|
|
|
*render_metric_header_5a_tex(data),
|
|
|
|
*render_metric_header_5a_tex(data),
|
|
|
|
r"\vfill",
|
|
|
|
r"\vfill",
|
|
|
|
*render_mission_5a_tex(data),
|
|
|
|
|
|
|
|
r"\vfill",
|
|
|
|
|
|
|
|
*render_objectives_5a_tex(data),
|
|
|
|
*render_objectives_5a_tex(data),
|
|
|
|
r"\vfill",
|
|
|
|
r"\vfill",
|
|
|
|
*render_agenda_5a_tex(data),
|
|
|
|
*render_agenda_5a_tex(data),
|
|
|
|
r"\vfill",
|
|
|
|
|
|
|
|
*render_hw_gate_5a_tex(data),
|
|
|
|
|
|
|
|
r"\vfill",
|
|
|
|
|
|
|
|
*render_tech_stack_5a_tex(data),
|
|
|
|
|
|
|
|
],
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -4463,8 +4632,14 @@ def build_5b_pages_tex(data: dict) -> list[list[str]]:
|
|
|
|
"center": [
|
|
|
|
"center": [
|
|
|
|
*running_card_header_5b_tex(data),
|
|
|
|
*running_card_header_5b_tex(data),
|
|
|
|
r"\vfill",
|
|
|
|
r"\vfill",
|
|
|
|
|
|
|
|
*render_mission_5a_tex(data),
|
|
|
|
|
|
|
|
r"\vfill",
|
|
|
|
*render_theory_5a_tex(data),
|
|
|
|
*render_theory_5a_tex(data),
|
|
|
|
r"\vfill",
|
|
|
|
r"\vfill",
|
|
|
|
|
|
|
|
*render_tech_stack_5a_tex(data),
|
|
|
|
|
|
|
|
r"\vfill",
|
|
|
|
|
|
|
|
*render_hw_gate_5a_tex(data),
|
|
|
|
|
|
|
|
r"\vfill",
|
|
|
|
*render_math_5a_tex(data),
|
|
|
|
*render_math_5a_tex(data),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|