Badge system wiring

Table of Contents

Summary

ORE Studio renders pill badges in Qt list views and detail-dialog combos via a three-layer DB-driven system: ores.dq owns the four domain entities (badge_severity, code_domain, badge_definition, badge_mapping); ores.sql seeds them at install time; and ores.qt.api's BadgeCache loads them at login and resolves (domain, entity_code) pairs to colours at paint time. For codegen- backed entities, adding a new badge requires no hand-written C++ at all — the delegate wiring is generated from two org-model table columns (is_badge=/=badge_key); only the SQL population entry is manual. Because a missing SQL entry fails silently (the generated resolver just returns the shared fallback colour, not an error), it is the step most often forgotten. Return to Knowledge.

Detail

The four domain entities

All four live in the dq schema and are managed by ores.dq.

Entity Table Purpose
badge_severity ores_dq_badge_severities_tbl Named severity levels (secondary, info, success, warning, danger, primary). Codes align with Bootstrap 5 classes so Wt needs no translation.
code_domain ores_dq_code_domains_tbl Named namespace that disambiguates codes across entity types (e.g. account_type, login_status).
badge_definition ores_dq_badge_definitions_tbl One row per visual variant: label, background colour, text colour, severity, CSS class hint.
badge_mapping ores_dq_badge_mappings_tbl Junction: maps (code_domain, entity_code)badge_definition.

SQL population

All badge metadata lives in one idempotent script:

projects/ores.sql/populate/dq/dq_badge_system_populate.sql

The script uses upsert functions so it is safe to re-run. The structure per new badge domain is always three blocks:

  1. Code domain — one ores_dq_code_domains_upsert_fn call naming the domain and its display_order.
  2. Badge definitions — one ores_dq_badge_definitions_upsert_fn call per distinct visual variant (label, hex colours, severity code, CSS class, display_order).
  3. Badge mappings — one ores_dq_badge_mappings_upsert_fn call per value, mapping (domain, entity_code)badge_definition_code.

The entity code in the mapping must match exactly what the Qt model returns for that column (see §Qt wiring below).

BadgeCache

BadgeCache (projects/ores.qt/api/include/ores.qt/BadgeCache.hpp) is a session-scoped cache populated at login from the standard service/protocol/client pipeline. It exposes one method:

const badge_definition* resolve(
    const std::string& domain,
    const std::string& entity_code) const;

Returns nullptr on cache miss — callers must fall back to a default colour. The cache is injected into every ItemDelegate at construction via the plugin_context received in on_login.

Qt wiring — codegen-generated (do not hand-write)

For any entity generated via the ores.cpp.qt codegen profile, badge rendering is driven entirely by org-model table columns — never hand-write ItemDelegate::paint()=/=sizeHint() for a badge column; that predates the current mechanism and will drift from what regeneration produces.

  • List view: set is_badge: true and badge_key: my_domain on the column's row in the entity's "Columns (Qt model)" table. The ores.cpp.qt.mdi_window_impl template then emits a shared EntityItemDelegate (styled cs::badge_centered for that column) with a resolver closure bound to 'my_domain', falling back to color_constants::badge_fallback (orange) on a cache miss or unresolved value — the same fallback the combo path below uses, so a missing/miskeyed mapping renders identically (not a mismatched fallback colour) in both places.
  • Detail dialog combo: set badge_key: my_domain on the field's row in "Detail fields" (works for both static_combo and dynamic_combo). The generated DetailDialog calls apply_combo_badges(combo, badgeCache_, "my_domain").

Regenerate (codegen.sh generate --model <entity.org> --address ores.cpp.qt) and rebuild; no other file changes.

For hand-written (non-codegen) entities, the same BadgeCache::resolve() / EntityItemDelegate building blocks apply — wire a column to cs::badge_centered and a resolver closure the same way the generated code does (see EntityItemDelegate::paint() for the dispatch and ores.cpp.qt.mdi_window_impl.org's has_badge_columns block for the resolver-closure shape to copy).

The entity code passed to resolve() must exactly match the string the model returns for Qt::DisplayRole on that column. If the model returns a display name (tr("Online")), the badge mapping key must use that display name. If the model returns the raw DB code ("user"), the mapping key must use the raw code.

Available badge collections (code domains)

Every code_domain currently seeded by dq_badge_system_populate.sql, in display_order. Check here before adding a new domain — an existing one (or its badge_definition colours) may already fit.

Domain Order Purpose
party_status 1 Lifecycle status codes for party and counterparty records.
book_status 2 Lifecycle status codes for book records.
portfolio_status 3 Lifecycle status codes for portfolio records.
portfolio_type 4 Type codes for portfolio records (Virtual, Physical).
book_type 5 Type codes for book records (Trading, Banking).
login_status 6 Login activity status for user accounts (Never, Old, Recent, Online).
account_locked 7 Account lock status (Locked, Unlocked).
compute_task_state 8 State codes for compute task records.
compute_task_outcome 9 Outcome codes for completed compute tasks.
report_concurrency_policy 10 Concurrency policy codes for report definitions (Skip, Queue, Fail).
report_fsm_state 11 FSM lifecycle state codes for report definitions.
dq_origin 12 Data quality origin dimension codes.
dq_nature 13 Data quality nature dimension codes.
dq_treatment 14 Data quality treatment dimension codes.
tenant_status 15 Lifecycle status codes for tenant records.
workspace_status 16 Lifecycle status codes for workspace records.
account_type 17 Classification of account types (user, service, algorithm, llm).
account_online 18 Boolean online indicator for accounts (Yes, No).
regulatory_book_type 19 Basel III/IV FRTB trading book / banking book classification a book carries.
currency_pair_classification 21 Liquidity classification codes for currency pairs (major, minor, exotic, commodity).
monetary_nature 25 Nature classification codes for currencies (fiat, commodity, synthetic, supranational).
currency_market_tier 26 Liquidity tier classification codes for currencies (G10, emerging, exotic, frontier, historical).

badge_definition.name (the human-readable label, 2nd argument to ores_dq_badge_definitions_upsert_fn) has a database-wide unique index — not scoped per domain. Two domains both wanting a "Commodity" or "Exotic" badge must pick distinct labels (Commodity Currency vs Commodity, Exotic Tier vs Exotic); grep the population script for your label before adding it. Violating this fails the population script partway through and leaves the DB half-populated — re-run db recreate after fixing.

Worked example — account_type

account_type badge (added in sprint 19):

  • Code domain: account_type, display_order 17.
  • Four badge definitions: account_type_user (info/#3b82f6), account_type_service (secondary/#6b7280), account_type_algorithm (warning/#eab308), account_type_llm (primary/#7c3aed).
  • Four mappings keyed on the raw DB codes: 'user', 'service', 'algorithm', 'llm' (because ClientAccountModel returns account.account_type verbatim).
  • Delegate: Column::AccountType added to the badge guard in AccountItemDelegate::paint() and sizeHint() (min width 80 px).

See also

Emacs 29.3 (Org mode 9.6.15)