read_reports() now returns 1,127 reports covering
May 2016 to the end of 2025, up from 118 covering 2025. The 2016-2024
reports tell one continuous fictional story that leads into the 2025
reports. Authors change roles over the years, and each year from 2017
has its own Annual Operating Plan memo. The figures still come from the
“mega” dataset, and some reports in every year carry deliberate errors.
inst/extdata/reports.rds grows from about 90 KB to about
730 KB.
About 100 of the 2025 reports were rewritten so the world is fully fictional: the PC refresh is now driven by the end of support for Lumen 10 (a fictional operating system) instead of Windows 10, and no real retailers or events are named. The Litware recall now matches the data: it is a safety inspection, and the affected models were never off sale.
New glossary of the terms the reports use, with the phrasings to
search for:
system.file("extdata", "reports", "glossary.yaml", package = "contoso").
read_reports() returns 118 fictional internal
business reports about Contoso’s 2025 trading (weekly sales flashes,
monthly e-commerce, supply chain and plant reviews, quarterly regional
reviews, pricing committee minutes, operations notices, industry notes
and the Annual Operating Plan). The figures come from the “mega” cloud
dataset. Some reports carry deliberate errors, for testing financial
planning and analysis assistants. The reports ship compressed in
inst/extdata/reports.rds (about 90 KB).
export_reports() writes the reports as one YAML file
each, the layout document stores and retrieval tools expect. This adds
‘yaml12’ to Imports.
New vignette, vignette("business-reports"),
describing the reports. New articles on the website: “Getting started”
and “Searching the reports with ragnar”.
contoso_dict_columns(). The data dictionary is
a standard data-dict
file, so the data-dict CLI already reads, validates and
renders it, and yaml::read_yaml() reads it in R. Use
system.file("extdata", "small", "data-dict.yaml", package = "contoso")
to find it.yaml has moved from Imports to Suggests; it was only
needed by the function removed above.
Depends is now R (>= 4.1.0), which the package
code has in fact required for some time – it uses the native pipe
|> and the \(x) function
shorthand.
size = "large", where every row of
fx was present twice (200,900 rows over 100,450 distinct
rates, exact duplicates). Joining sales to fx
doubled the row count and any revenue converted through it.
medium and mega were unaffected, and
contoso_10m/fx.parquet is now byte-identical to the file
the other sizes already shared. This is the same duplication fixed for
sales in 2.2.0; fx was missed at the
time.The bundled data dictionary now validates against the data-dict spec at all three
levels (validate-spec, validate-meta,
validate-data). Removed the non-spec rows and
missing keys, added the required range to
every ordered column, and recorded the composite primary keys on
sales and orderrows (order_key +
line_number) and on fx (date +
from_currency + to_currency).
Corrected the dictionary where it contradicted the data:
line_number is zero-based, not 1-based, and the
sales-to-fx join was documented as
many-to-one when it fans out five-to-one unless
from_currency is filtered.
The derived measures in sales are now written as
checkable assertions rather than prose, so
data-dict validate-data verifies that
gross_revenue, net_revenue,
unit_discount, discounts, cogs,
gross_margin and unit_margin still follow
their documented formulas.
The dictionary for the cloud datasets ships as
inst/extdata/cloud/data-dict.yaml, covering
size = "medium", "large" and
"mega". It carries the same types, constraints, assertions,
relationships and glossary as the small dictionary; only the observed
range, examples and the descriptions that are
genuinely different differ.
The reports returned by read_reports() have their
own data dictionary, inst/extdata/reports/data-dict.yaml,
describing each column and noting that some reports are deliberately
wrong.
customer now has one row per customer (3,165 rows,
down from 7,794). Previous versions built it by joining the customer
catalogue onto sales, which repeated each customer once per
sales line they appeared on. This made customer_key
non-unique, so joining customer to sales,
orders or any other table silently multiplied rows. No
attribute varied within a customer, so the deduplication loses no data,
but any code that relied on the old row count — or that worked around
the fan-out with distinct() — should be reviewed. Thanks to
Hadley Wickham for
reporting.
The cloud dataset behind
create_contoso_duckdb(size = "small") carried the same
fan-out and has been corrected, so it again matches the bundled
customer. The medium, large and
mega sizes were never affected.
Column types are now consistent across all four sizes. Previously
date columns were stored as strings in medium and
large — so comparisons on order_date and
friends were string comparisons, and date arithmetic failed — while
small and mega used real dates. Integer widths
also varied (DOUBLE, INTEGER,
BIGINT) between sizes, and in medium
sales.order_date was a date while
orders.order_date was a string. Dates are now
DATE, keys and counts BIGINT, and measures
DOUBLE, everywhere.
Fixed size = "large", where every row of
sales was present twice (47,439,870 rows over 23,719,935
order lines, exact duplicates). Any revenue, cost, margin or quantity
total taken from that size was doubled. The documented row count for
large was the inflated figure and is now 23,719,935. Other
sizes were unaffected.
Fixed product_code in medium and
large, which was stored as an integer and had lost its
leading zero (101001 rather than 0101001), so
it did not match the same product’s code in small and
mega.
db_dir parameter (no longer needed)