{# ---------- PAC-INFO MACROS ---------- #} {# These macros render semantic HTML with light class hooks. #} {%- macro key_value_table(pairs) -%} {# pairs: iterable of (key, value) 2-tuples - NOT a dict.items()-only call, so callers with non-unique/empty labels (e.g. several attributes sharing a blank label) don't silently lose rows the way collapsing into a dict first would. #} {%- for k, v in pairs -%} {%- if k != "key" -%} {%- endif -%} {%- endfor -%}
{{ k }} {{ render_text(v) }}
{%- endmacro -%} {#- Raw key beneath an attribute's label, per the ATTRIBUTE_KEY_DISPLAY setting (see render_predicates._attribute_key_display). "never" drops it from the markup entirely rather than hiding it with CSS, so a consumer-facing label doesn't ship keys in its DOM; the two visible modes differ only in styling, hence a modifier class instead of two branches here. Defaulted in-template as well, for any consumer that renders these macros without merging render_context_utils into its Jinja globals. -#} {%- macro attribute_key_hint(key) -%} {%- set key_display = attribute_key_display | default('always', true) -%} {#- `key` can legitimately be empty - a Document built from a user handover Service carries `service.key or ''` - and an empty hint is just a stray blank line. -#} {%- if key_display != 'never' and key %}
{{ key }} {%- endif %} {%- endmacro -%} {#- Same look as key_value_table, but rows are built from Attribute objects, so each label can carry its key hint - key_value_table takes bare (label, value) pairs with nowhere to put a key, and its other caller (category_block) has no keys at all. -#} {%- macro attribute_kv_table(attributes) -%} {%- for a in attributes -%} {%- endfor -%}
{{ a.label }}{{ attribute_key_hint(a.key) }} {{ render_text(a.values) }}
{%- endmacro -%} {%- macro category_block(category) -%}
{{ category.__class__.__name__ }}
{{ key_value_table(category.segments_as_dict().items()) }}
{%- endmacro -%} {# Renamed from service_table_section/services_table - neither name matched what they render (both emit
s, not s), and the pairing read backwards (the "table" was the inner piece, the "section" wrapped it). #} {%- macro service_links(services) %}
{%- for s in services -%} {{ s.service_name }} {%- endfor -%}
{%- endmacro -%} {%- macro service_group_block(user_handover_group) -%}
from {{ user_handover_group.origin }}
{{ service_links(user_handover_group.services)}}
{%- endmacro -%} {%- macro reference(value, label=None, card_request_params=dict(), target_class=None, display_label=True) %}
{% if display_label %} {# The card above is the primary content; the label is always a real link too, on top of it - route_pac_id_url_for lets an issuer route it through their own page instead of linking straight to the PAC-ID's own URL. #} {% if route_pac_id_url_for is defined %} {{ label or value }} {% else %} {{ label or value }} {% endif %} {% endif %} {% if pac_card_url_for is defined %}
{% endif %}
{%- endmacro %} {# A PacInfo.Document (name, key, url) - the uniform shape safety_data_sheet/ certificate_of_analysis return regardless of whether the source was an attribute or a user handover Service. Distinct from attribute_row() since Document has no .value_list/.label to match that macro's Attribute-shaped interface. #} {#- A document is a single cell, not a label/URL pair: the name is the link text and the URL only the href, since a raw document URL carries nothing a reader needs to see (and gets ellipsed by .lf-table's nowrap anyway). Same icon+label look as attribute_row's URL values (.lf-service), except the icon trails the name here and the anchor hugs its text instead of filling the cell - see .lf-service--inline. -#} {%- macro document_row(document) -%} {%- endmacro -%} {%- macro attribute_row(a) -%} {%- endmacro -%} {%- macro attribute_group_block(ag) -%}
{{ ag.group_label }}
{%- if dev_mode %}

(from {{ ag.origin }})

{%- endif %}
{{ document.name }} {{ attribute_key_hint(document.key) }}
{% if is_reference(a.key) %} {{ reference(a.key, a.label)}} {% elif is_url(a.key) %} {{ a.label }} {% else %} {{ a.label }} {% endif %} {{ attribute_key_hint(a.key) }} {% for value in a.value_list%} {%- if is_image(value) -%}
{{ label|e }}
{{ label }}
{%- elif is_reference(value) -%} {# Checked before is_url - a PAC-ID is itself a valid URL, so a bare (non-pyReference) PAC-ID string would otherwise always hit the generic link branch below and never reach the card. #} {{ reference(value)}} {%- elif is_url(value) -%} {# Same icon+label format as service_links' elements - reuses its classes rather than a parallel set, since the look is meant to be identical. #} {{ value }} {%- else -%} {{ render_text(value) }} {%- endif -%} {% if not loop.last %}
{% endif %} {% endfor%}
{%- for a in ag.attributes.values() -%} {{ attribute_row(a) }} {%- endfor -%}
{%- endmacro -%} {%- macro data_table_inline(dt) -%} {# Renders your DataTable structure; expects filters: is_data_table(dt) #} {%- if dt.data | length == 1 -%} {%- for i in range(dt.col_names | length) -%} {%- endfor -%}
{{ dt.col_names[i] }} {{ dt.data[0][i] }}
{%- else -%} {%- for rn in dt.col_names -%} {%- endfor -%} {%- for row in dt.data -%} {%- for e in row -%} {%- endfor -%} {%- endfor -%}
{{ rn }}
{{ e }}
{%- endif -%} {%- endmacro -%} {%- macro attached_data_block(attached_data) -%}
{%- for name, trex in attached_data.items() -%} {{ attached_data_trex(name, trex) }} {%- endfor -%}
{%- endmacro -%} {# One pictogram for a GHS0x code, using the bundled static image. resolve_static_image() only searches flat filenames directly in the static root (Path(base_name).stem strips any directory component before it even searches) - no subdirectory, no extension needed, it tries svg/png/jpg/jpeg/ webp itself. Supplier-provided images (see PacInfo.explicit_pictogram_image_urls) aren't matched to a particular code - they're rendered as their own separate images by pictogram_strip below. #} {%- macro pictogram_image(code) -%} {{ pictogram_name(code) }} {%- endmacro -%} {# Shown once above the hazard/precautionary statement table (see PacInfo.safety_pictogram_codes) rather than repeated per row - a hazard statement's pictogram is a property of the substance as a whole, and several codes commonly share the same one (e.g. H300/H310/H330 are all Skull-and-crossbones). explicit_image_urls are supplier-provided pictogram images (Resource-valued GHS_PICTOGRAM attribute values) - rendered in addition to the code-derived images, not matched against any particular code. #} {%- macro pictogram_strip(codes, explicit_image_urls=[]) -%} {%- if codes or explicit_image_urls -%}
{%- for code in codes -%} {{ pictogram_image(code) }} {%- endfor -%} {%- for url in explicit_image_urls -%} {%- endfor -%}
{%- endif -%} {%- endmacro -%} {# GHS hazard/precautionary statement rows - code beside its full text. Resolution (code extraction + labfreed/utilities/ghs/ghs_statements.json lookup) now happens in PacInfo.hazard_statements/precautionary_statements, not here - these macros just render the already-resolved HazardStatement/PrecautionaryStatement (code, text, text_origin). text is None when the code isn't in that table (e.g. an EU-specific EUH code, not part of the UN GHS document it was sourced from) and text_origin is always 'Predefined' for now - falls back to the bare code until attribute-delivered text is wired up - see developer-docs/design-choices.md. #} {# th/td classes match every other key/value table (key_value_table, attribute_row) - same 1/3-2/3 column widths and label bolding from .lf-table--kv, for free, no GHS-specific CSS needed. #} {%- macro hazard_statement_row(statement) -%} {{ statement.code }} {{ statement.text or statement.code }} {%- endmacro -%} {%- macro precautionary_statement_row(statement) -%} {{ statement.code }} {{ statement.text or statement.code }} {%- endmacro -%} {%- macro attached_data_trex(name, trex) %}
{{ name }}
{%- for k, v in trex.items() -%} {%- endfor -%}
{{ k }} {%- if is_data_table(v) -%} {{ data_table_inline(v) }} {%- else -%} {{ v }} {%- endif -%}
{% endmacro %}