Your first report in 10 minutes
Build a real Excel workbook from six synthetic sales rows. Everything runs locally: no database
server, password or account. You need Python 3.9+ and Git to follow the commands below.
On systems where Python is called python3 or py, use that command instead of python.
Excel, LibreOffice or another spreadsheet viewer can open the result.
The complete tutorial project and every intermediate step are tested in CI. Each step below names the exact source files and expected output. A small helper copies those files into your project, so there is no large SQL block to type.
1. Install and run one query
Section titled “1. Install and run one query”Open a terminal in a working directory. These commands work in PowerShell, Bash and Zsh:
python -m pip install dre-clidre --versiongit clone --depth 1 https://github.com/get-dre/dre.git dre-tutorial-sourcedre new first-report --profile samplepython dre-tutorial-source/examples/tutorial/prepare.py 1 first-reportcd first-reportdre validatedre run salesThe first run downloads the DuckDB and Excel plugins. Subsequent runs reuse them.
dre new also creates a hello report; it stays available, but this tutorial selects sales.
The helper creates this project connection:
connections: sample: targets: dev: {type: duckdb}With no path:, DuckDB runs in memory. The exact project files are
dependencies.yml,
sales.sql and
sales.yml:
name: first_reportdefault_profile: samplequeries: [sales]output: format: xlsxsales.sql selects the four January rows from an inline six-row dataset. The columns are
region, month, category and revenue. The run prints the workbook path under
target/run/sales/default/runs/<run-id>/; open that file.
Expected sales tab:
| region | month | category | revenue |
|---|---|---|---|
| North | 2026-01 | Hardware | 1200 |
| North | 2026-01 | Services | 800 |
| South | 2026-01 | Hardware | 900 |
| South | 2026-01 | Services | 600 |
Validation ends with Validation passed: 0 errors, 0 warnings. The run ends with one
succeeded report and zero failures. Nothing has been delivered outside your project.
2. Change the month with a variable
Section titled “2. Change the month with a variable”python ../dre-tutorial-source/examples/tutorial/prepare.py 2 .dre run sales --var month=2026-02Changed files: dre_project.yml and sales.sql. The project now defines:
vars: month: '2026-01'SQL reads it with:
where month = '{{ var("month") }}'Expected output: the same four columns, now with two February Hardware rows: North 1,400
and South 1,100. --var changes this run only; the file still defaults to January.
3. Add a Summary tab
Section titled “3. Add a Summary tab”python ../dre-tutorial-source/examples/tutorial/prepare.py 3 .dre run sales --accept-schema-changeChanged files: sales.yml and the new summary.sql.
queries: - {query: sales, tab_name: Sales} - {query: summary, tab_name: Summary}output: format: xlsxExpected output: a January workbook with Sales and Summary tabs. Summary has two columns,
region and revenue: North 2,000, South 1,500. Sales has the four January detail rows again.
Adding and renaming tabs changes the result schema. --accept-schema-change accepts this
intentional change; later runs do not need it. See Run options.
4. Format revenue as a number
Section titled “4. Format revenue as a number”python ../dre-tutorial-source/examples/tutorial/prepare.py 4 .dre run salesChanged file: sales.yml. Both query entries now set:
columns: revenue: {format: "#,##0.00"}Expected output: the same rows and values, displayed as 1,200.00, 800.00,
2,000.00 and so on. Cells remain numbers, so Excel can sum and sort them.
5. Make one workbook per region
Section titled “5. Make one workbook per region”python ../dre-tutorial-source/examples/tutorial/prepare.py 5 .dre run sales --set allChanged files: sales.yml, sales.sql and summary.sql. The report declares:
sets: - {name: north, vars: {region: North}} - {name: south, vars: {region: South}}default_set: northBoth SQL files now filter on region = '{{ var("region") }}'.
Expected output: two successful Bindings. Each has its own workbook under
target/run/sales/north/runs/<run-id>/ or target/run/sales/south/runs/<run-id>/.
North has two Sales rows and a Summary total of 2,000; South has two rows and a total of 1,500.
A plain dre run sales uses North; --set south runs only South.
6. Name a schedule
Section titled “6. Name a schedule”python ../dre-tutorial-source/examples/tutorial/prepare.py 6 .dre validatedre schedule lsNew file: schedules.yml.
- name: weekday_sales report: sales set: north cron: '0 9 * * 1-5' timezone: UTCExpected output: weekday_sales appears with its next weekday occurrences at 09:00 UTC.
The dates depend on when you run the command. The existing workbooks stay as they are.
Listing schedules does not start a background process. See Scheduling
and Orchestration to have your external scheduler dispatch each firing
with dre run --schedule weekday_sales.
7. Deliver to a local folder
Section titled “7. Deliver to a local folder”python ../dre-tutorial-source/examples/tutorial/prepare.py 7 .dre run sales --set allChanged file: sales.yml. The output gains:
destination: {profile: local, path: "out/{{ var('region') }}.xlsx"}Expected files: out/North.xlsx and out/South.xlsx, each with Sales and Summary tabs.
North still totals 2,000; South 1,500. DRE also keeps each run’s original output and
run_results.json under target/.
local is built in and needs no destination profile. When you are ready, use an
SFTP profile, S3 profile or Slack profile
instead. Their tested examples start with remote delivery disabled and
read credentials from environment variables.
Try the monthly finance pack for formulas, totals, source declarations, account mappings and a filled Excel template. Read Build and run reports for the general workflow, Concepts for the terminology and CLI reference for all commands.