Data formats¶
The CLI reads two JSON formats: rule files and data files. Python callers
normally build a provider instead
(Connect your data); the CLI's
dpmxl.cli.load_memory_provider(document) turns a data document into a
MemoryProvider if you want the same
format in Python. The --json output of the CLI is described in the
CLI reference.
Rule files¶
A .json rule file is a list of objects, one per rule
(examples/rules.json):
| Field | Type | Content |
|---|---|---|
code |
string | The rule's label in the output. Defaults to the rule's 1-based position. |
expression |
string | The rule. A rule without one is skipped. |
precondition |
string, optional | An expression that must yield a true record for the rule to apply. |
expression and precondition may also be objects with an expression
field, as some rule exports nest them. A file that does not hold a list, or
a list entry that is not an object, is an error (exit status 3).
[
{
"code": "v0001",
"expression": "with {tC_01.00, c*, default: 0, interval: true}: {r0010} = {r0020} + {r0030}",
"precondition": "{v_C_01.00}"
},
{"code": "v0002", "expression": "{tC_27.00, c0010} > 0"}
]
Any other file is a text rule file: one expression per line, # starts a
comment line, blank lines are ignored
(examples/rules.txt).
Data files¶
A data file is one JSON object describing a report
(examples/data.json).
Every field is optional.
| Field | Type | Content |
|---|---|---|
tables |
list | Table layouts: code; rows, columns and sheets (lists of codes); open_keys (key names of an open table); template (a template code whose filing indicator {v_…} this table answers); open_axes (which of rows, columns and sheets are open, identified by the open keys); data_types (cells' data types, each row, column, sheet and type: number, integer, string, date, boolean or item). The last two are optional and used only by dpm-xl check --layouts. |
facts |
list | Reported values, below. |
reported |
list | Table or template codes with a positive filing indicator. Defaults to every table with a fact. |
parameters |
object | Report parameters by name, typed like fact values; a list is a set. |
table_groups |
object | Group code to a list of table codes, for {g_…}. |
operations |
object | Operation code to its expression, for {o_…}. |
earlier |
list | Other periods' reports for time_shift: period (A, S, Q, M, W or D), count (periods earlier, negative for later) and data, a nested data document. |
Facts¶
| Field | Type | Content |
|---|---|---|
table |
string | Table code (required). |
row, column, sheet |
string, optional | Closed coordinates; omit the ones the table does not have. |
keys |
object, optional | Open key components by name, for open tables. |
value |
any | The value; null for a nil fact. |
decimals |
integer or "INF", optional |
XBRL decimals of a number; "INF" or absent means exact. |
type |
string, optional | number, item or date, to read a string value as that type. |
Values are typed as follows: JSON numbers (and strings with "type":
"number") become Number with the given decimals; strings starting with
[ (or with "type": "item") become items such as [eba_CU:EUR]; strings
with "type": "date" become dates; lists become sets; Booleans and other
strings stay as they are. Open key values are typed the same way, without
type.
Example¶
This document uses every field; the Python below loads it the way
dpm-xl eval does and evaluates a rule against each part.
from dpmxl import evaluate_rule
from dpmxl.cli import load_memory_provider
document = {
"tables": [
{"code": "C_01.00", "rows": ["0010", "0020"], "columns": ["0010"]},
{
"code": "C_27.00",
"columns": ["0010", "0020", "0030"],
"open_keys": ["qCPI"],
"template": "C_27",
"open_axes": ["rows"],
"data_types": [{"column": "0010", "type": "number"}],
},
],
"facts": [
{
"table": "C_01.00",
"row": "0010",
"column": "0010",
"value": 1204,
"decimals": 0,
},
{
"table": "C_01.00",
"row": "0020",
"column": "0010",
"value": "1180.5",
"decimals": "INF",
"type": "number",
},
{
"table": "C_27.00",
"column": "0010",
"keys": {"qCPI": "LEI-A"},
"value": 120,
"decimals": -3,
},
{
"table": "C_27.00",
"column": "0020",
"keys": {"qCPI": "LEI-A"},
"value": "[eba_CU:EUR]",
},
{
"table": "C_27.00",
"column": "0030",
"keys": {"qCPI": "LEI-A"},
"value": "2026-06-30",
"type": "date",
},
],
"reported": ["C_01.00", "C_27.00"],
"parameters": {
"refPeriod": "2026-12-31",
"Currencies": ["[eba_CU:EUR]", "[eba_CU:USD]"],
},
"table_groups": {"G_01": ["C_01.00"]},
"operations": {"v0001_a": "{tC_01.00, r0010, c0010} * 2"},
"earlier": [
{
"period": "Q",
"count": 1,
"data": {
"tables": [{"code": "C_01.00", "rows": ["0010"], "columns": ["0010"]}],
"facts": [
{
"table": "C_01.00",
"row": "0010",
"column": "0010",
"value": 1100,
"decimals": 0,
}
],
},
},
],
}
provider = load_memory_provider(document)
for expression in (
"{tC_01.00, r0010, c0010} > {tC_01.00, r0020, c0010}",
"{tC_27.00, c0020} in {p_Currencies, set-item}",
"{tC_27.00, c0030} < {p_refPeriod, date}",
"{v_C_27}",
"{g_G_01, r0010, c0010} > 0",
"{o_v0001_a} > 2000",
"{tC_01.00, r0010, c0010} >= time_shift({tC_01.00, r0010, c0010}, Q, 1)",
):
print(evaluate_rule(expression, provider).status, expression)
passed {tC_01.00, r0010, c0010} > {tC_01.00, r0020, c0010}
passed {tC_27.00, c0020} in {p_Currencies, set-item}
passed {tC_27.00, c0030} < {p_refPeriod, date}
passed {v_C_27}
passed {g_G_01, r0010, c0010} > 0
passed {o_v0001_a} > 2000
passed {tC_01.00, r0010, c0010} >= time_shift({tC_01.00, r0010, c0010}, Q, 1)
A malformed document is reported as error: … with exit status 3: an
invalid value (a number that does not parse, decimals that is neither an
integer nor INF or is beyond the decimal128 range) names the fact by its
1-based position, as in error: fact 1: invalid number 'x1'; a missing
required field such as table gives malformed data document.