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:
- Code domain — one
ores_dq_code_domains_upsert_fncall naming the domain and itsdisplay_order. - Badge definitions — one
ores_dq_badge_definitions_upsert_fncall per distinct visual variant (label, hex colours, severity code, CSS class, display_order). - Badge mappings — one
ores_dq_badge_mappings_upsert_fncall 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: trueandbadge_key: my_domainon the column's row in the entity's "Columns (Qt model)" table. Theores.cpp.qt.mdi_window_impltemplate then emits a sharedEntityItemDelegate(styledcs::badge_centeredfor that column) with a resolver closure bound to'my_domain', falling back tocolor_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_domainon the field's row in "Detail fields" (works for bothstatic_comboanddynamic_combo). The generatedDetailDialogcallsapply_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'(becauseClientAccountModelreturnsaccount.account_typeverbatim). - Delegate:
Column::AccountTypeadded to the badge guard inAccountItemDelegate::paint()andsizeHint()(min width 80 px).
See also
- ores.qt.api —
BadgeCache,DelegatePaintUtils, and theplugin_contextthat injects the cache. - How do I add a badge to a Qt list view? — the four-step checklist.
- Qt plugin architecture — how
plugin_context(includingbadge_cache) is passed to plugins at login. - Entity controller pattern — the delegate sits in the entity UI stack alongside the model and list window.