Getting started¶
This tutorial generates a synthetic report for a small template, reads the package back with dpm-xbrl and checks the rules with dpm-xl. It takes about ten minutes.
Install¶
dpm-synth needs Python 3.11 or newer. It installs dpm-xl, which evaluates DPM-XL rules, and dpm-xbrl, which reads and writes EBA reports.
The template¶
A template describes one module: its tables, the cells of each table, and
the rules a report must satisfy. It is a JSON document in the
dpm-synth-template/1 format. This is
minimal.json:
{
"format": "dpm-synth-template/1",
"release": "1.0",
"module": "DEMO",
"entry_point": "https://example.com/taxonomy/demo/1.0/mod/demo.json",
"reference_date": "2026-12-31",
"tables": [
{
"code": "T_01.00",
"cells": [
{"id": "T_01.00/r0010", "row": "0010", "column": "0010", "datapoint": "dp1001", "data_type": "m"},
{"id": "T_01.00/r0020", "row": "0020", "column": "0010", "datapoint": "dp1002", "data_type": "m"},
{"id": "T_01.00/r0030", "row": "0030", "column": "0010", "datapoint": "dp1003", "data_type": "m"}
]
}
],
"rules": [
{"code": "v1", "expression": "with {tT_01.00, c0010, default: 0, interval: true}: {r0010} = {r0020} + {r0030}"},
{"code": "v2", "expression": "with {tT_01.00, c0010}: {(r0020, r0030)} >= 0"}
]
}
- Table
T_01.00has three monetary cells (data_typem) in rows 0010 to 0030 of column 0010. Each cell names thedatapointthat carries its value in the package. - Rule
v1says row 0010 is the sum of rows 0020 and 0030; rulev2says both components are zero or more.
For real modules you do not write templates by hand: any tool that reads a DPM release can export them; see build a template from a DPM release. To write your own, see write a template by hand.
Generate from the command line¶
$ dpm-synth generate minimal.json -o report.zip --seed 7
rules: 2, passed: 2, failed: 0, not evaluated: 0, not applicable: 0
rounds: 2 (failing rules per round: 1, 0)
wrote report.zip (3 facts)
$ echo $?
0
The first round found one failing rule (the random total did not match its components), repaired it, and the second round found none. Exit status 0 means every rule that could be evaluated passes; 1 would mean some still fail. The package is written either way.
Read the package back¶
report.zip is an EBA xBRL-CSV report package. Read it with dpm-xbrl:
from dpmxbrl import read_report
report = read_report("report.zip")
print(report.entity_id, report.reference_date, report.filing_indicators)
for fact in report.facts:
print(fact.template, fact.datapoint, fact.value)
print([finding.code for finding in report.filing])
SYNTHETICDPMXL000002.CON 2026-12-31 {'T_01.00': True}
t_01.00 dp1001 1126000
t_01.00 dp1002 971000
t_01.00 dp1003 155000
['invalidFileName']
The entity is the fictional LEI SYNTHETICDPMXL000002 at the consolidated
level. The only EBA filing rule the package breaks is the file name, which
for a real submission encodes the entity, module and dates; name the output
accordingly if that matters to you.
Generate from Python¶
The same from Python, with the result of the repair loop:
from dpmxbrl import write_xbrl_csv
from dpmsynth import generate, load_template
template = load_template("minimal.json")
result = generate(template, seed=7)
print(f"{result.passed} passed, failed: {result.failed}")
print(f"rounds: {result.rounds}, failing per round: {result.history}")
write_xbrl_csv(result.report, "report.zip")
result.report is a dpmxbrl.Report; result.data holds the generated
values and is a dpm-xl data provider, so you can evaluate any rule on it:
from dpmxl import evaluate_rule
rule = "with {tT_01.00, c0010}: {r0010} >= {r0020}"
print(evaluate_rule(rule, result.data).status)
A bigger template¶
sample.json has a closed table with row and column
totals, a ratio and an enumerated cell, an open table keyed by an
enumerated property, an open table keyed by LEIs, and a time_shift rule
that needs last year's report:
$ dpm-synth generate sample.json -o sample.zip --seed 7
rules: 9, passed: 8, failed: 0, not evaluated: 1, not applicable: 0
rounds: 5 (failing rules per round: 5, 3, 1, 1, 0)
wrote sample.zip (20 facts)
The time_shift rule is "not evaluated": a single report has no previous
period. The evaluate-and-repair loop explains the
rounds.
Next steps¶
- Validate the output independently with dpm-xbrl and dpm-xl.
- Load-test with millions of open-table rows.
- Read the limits of the loop before relying on a module's every rule passing.