Repository navigation
0.9.0
Ossie schema 0.2.0.dev0. Tested with dactopus-ossie-clickhouse 0.12.0,
dbt v2 (dbt-oss 2.0.8) and dbt v1 (dbt-core 1.11 with
dbt-clickhouse 1.10) on ClickHouse 26.9, the WooCommerce package also
on 26.7. Several stores, properties or networks in one deployment: every
table starts with the label source_id, orders carries platform,
and the unions add up the tables of several databases. Every column has
a declared type, and some types change: an incremental run stops on the
changed schema, and a deployment runs dbt build --full-refresh once.
No input table changes.
Added
- Every table of every package has a first column
source_id, the
label of the store, property or network its rows come from: the
variablega4_source_id,shopify_source_id,woocommerce_source_id
orsensor_community_source_id,LowCardinality(String). The default
is the package's name (shopify); a label other than 1 to 64 of
a-z,0-9and_stops the build. A dbt test on every model fails
on a row of another label, as an incremental run after a new label
leaves, and names--full-refresh.deployment.ymlmaps the role
source_idto the variable, andtests/check_deployment.pyrequires
it. - The Ossie models have a field
source_idin every dataset. It comes
first in everyprimary_keyand on both sides of every relationship,
and every count of distinct values counts them within a label
(orders.buyer_count,product_categories.product_count,
product_brands.product_count,readings.reporting_devices,
sessions.users,sessions.user_conversion_rate): the tables of
several stores, each built with its own label, keep their orders and
customers apart. In one database the label is constant and every
number stays the same. Its description and synonyms (store, shop;
property, site; network) let an agent find it, and each model's
instructions say how to answer over several: for a store, property or
network the question names or describes, an agent lists the labels and
takes the one that names it (for a store, also the one store in a
currency the question names), and asks when none does; a question that
names none means all of them; identifiers repeat across them, so an
agent groups by one with itssource_id; "today" is the date where the
model is served, while each one's dates are in its own zone. GA4 users
count within a property and are not matched across properties, and a
sensor network's silent devices count from its own latest contact. ordershas a columnplatform,shopifyorwoocommerce, after
currency, and the commerce model a fieldorders.platform: an agent
over stores of both platforms finds which store a question about
"the Shopify store" means, and which platform's report rules apply to
a store. The unions carry it.- Unions:
unions/commerce,unions/web_analyticsandunions/iot, dbt
projects that add up the tables of several stores, properties or
networks, each built by its package into a database of its own under
its own label. A union writes a view of each dataset of the domain's
Ossie model,UNION ALLover the databases of
commerce_union_databases,web_analytics_union_databasesor
iot_union_databases, with the columns the model reads and their
types as a dbt contract. A commerce union takes Shopify and
WooCommerce stores alike. The build stops on a database whose column
types differ from the contract and on the union's own database among
those it adds up; its test fails when a label is in two databases.
deployment.ymlmaps the roleinput_databasesto the variable.
tests/check_contracts.pychecks the unions' contracts against the
Ossie models and the packages, thatsource_idcomes first in every
key and relationship of the Ossie models, that each package tests the
key of each dataset (Sensor.Community'sreadingswith
sensor_community_unique_key, which replaces the test
readings_one_per_sensor_time_measure), and that the copies of
macros/union.sqland of each package'smacros/freshness.sqlare
the same;tests/check_union.pychecks that each label's answers over
a union are its database's own.
Fixed
- WooCommerce: an incremental run over a table whose columns a release
changed stops before its pre-hooks delete a row. dbt's own check of
the columns comes after the hooks: the run stopped withorder_lines
andrefundsshort of the rows of its window,refundsempty on the
fixture. - WooCommerce: category and brand names (
line_categories.category,
category_path,line_brands.brandand the same fields of
product_categoriesandproduct_brands) are decoded from the HTML
escaping WordPress stores them with:Home & Garden, not
Home & Garden. No table changes: the next run rebuilds the
category and brand tables whole.
Changed
-
CI runs a job only when a file it reads changed (the filters in
ci.yml): a pull request that changes only documentation runs none,
one that changes one package runs that package's jobs and the unions
job. The Ossie models are validated together in a job of their own.
The one checkmainrequires isCI passed, which fails when a job
failed or was cancelled; a skipped job does not block a merge. -
CI runs on
ubuntu-24.04, notubuntu-latest, and keys the uv cache
byci.yml: before, the key was constant and the cache was never
refreshed. -
AGENTS.md keeps what an agent applies while writing and is not in
CONTRIBUTING.md: the rules ofsource_id, contracts and unions, the
model format, ClickHouse traits, which page a change ships with, and
the development setup. Layout, scope, the authoring rules and the
checks before a pull request are read from CONTRIBUTING.md, whose
canon rule gains the two details only AGENTS.md had: a source that
has less of a field still provides it when the meaning is the same,
and no canonical field goes in "for completeness". -
The README is split. Each package's guide (loading, settings, known
differences from the source's own reports) issources/<source>/README.md;
what each Ossie model answers and its limits isdocs/<domain>.md;
the packages in a dbt project, labels, time zones, serving and unions
aredocs/deployment.md; layout, what a source package is, out of
scope and the authoring rules are in CONTRIBUTING.md. The README keeps
the overview, one quick start and versioning. Notes on how the
packages were checked against test stores, and the migration note for
snapshots loaded before 0.5.1, are gone from the guides: the checked
numbers are stated, the history is this changelog. The install
recipes take dactopus-ossie-clickhouse from PyPI. -
The commerce model's instructions: one deployment can hold several
stores, under their labels, in different currencies. An agent asks
every amount byorders.currency, answers in the one currency or in
each, and never adds up two; amounts of customers go by
customers.source_id.orders.currencyandcustomers.net_amount
say the same. A customer of two stores counts in each. -
README, Known differences from WooCommerce Analytics: buyers in a
month, where the reports also count a customer with only a refund. -
Every package declares the type of each column it writes, a dbt
contract inmodels/schema.yml, and a build stops on any other. The
columns an Ossie model reads come first in each table, with the same
types in every package that serves the model: the tables of Shopify
and WooCommerce add up withUNION ALL.tests/check_contracts.py
checks it in CI. No value changes; these types do:- Amounts are
Decimal(38, 8)and never NULL:orders.total,
subtotal,discount_total,shipping_total,tax_total,
refunded_total,net_total;customers.net_total;line_total
andreturned_amountoforder_lines,line_categoriesand
line_brands;refunds.refunded_amount;sales_events.amount.
Shopify's wereDecimal(18, 4)(customers.net_total
Decimal(38, 4)), and Shopify'sorder_lines.discount_total
follows; WooCommerce's wereNullable(Decimal(38, 8)), all but
orders.refunded_total. orders.customer_idisNullable(Int64),customers.customer_id
Int64: Shopify's wereNullable(UInt64)andUInt64,
WooCommerce'sorders.customer_idInt64.- Shopify's quantities are
Int64:orders.item_quantity;
quantityandcurrent_quantityoforder_lines,
line_categoriesandline_brands. They wereUInt32and
UInt64. - WooCommerce's
orders.order_time,refunds.refund_timeand
sales_events.event_timeareDateTime('UTC'), not
DateTime64(6, 'UTC'): WooCommerce keeps whole seconds. - Flags are
Bool:orders.is_cancelledand
customers.second_order_within_*_days(Nullable(Bool)) of both
packages, WooCommerce'sorders.in_sales_reportsandis_directof
line_categories,product_categoriesand
product_categories_all. They wereUInt8. - Shopify's
order_lines.titleisString, notNullable(String).
The columns of every table come in a new order, the model's first.
Withsource_id(Added), every package runs once with
--full-refreshin place of the next incremental run, which stops. - Amounts are
-
GA4:
on_schema_change: fail, as in the other packages; dbt requires
it of an incremental model with a contract. -
WooCommerce: an order or refund in another currency without a rate
the package knows now fails in its model, with a message that names
the README's Known limits, where before thenot_nulltests on its
amounts failed. -
The commerce model reads its
Boolflags alone, without= 1
(orders.is_cancelled,orders.in_sales_reports,
customers.second_order_within_*_daysand the
customers.second_order_rate_*_daysmetrics,is_directof
line_categoriesandproduct_categories): Postgres has no operator
forBool = Int, so the model now translates to engines other than
ClickHouse. The numbers are the same.tests/check_contracts.pyfails
on an expression that compares aBoolcolumn with a number. -
The commerce model's instructions state each rule that depends on the
platform from the field that carries it, with Shopify and WooCommerce
as examples: which orders the sales reports count
(orders.in_sales_reports), the order date (orders.order_date), a
refund of 0, who a customer is (customers), what a category and a
brand are (line_categories.category,line_brands.brand). The
rules are the same. -
The web_analytics model's instructions say when GA4 exports a day:
once, usually in the afternoon of the following day. The days after
the latest date with rows have not arrived yet, and an agent says up
to which date the data reaches instead of reporting them as days
without visits; the latest date is a whole day, and a low count there
is not a partial export
(#48).
docs/web_analytics.mdsays the same. -
ROADMAP.md: Magento (Adobe Commerce) under Exploring, a store whose
MySQL database reaches ClickHouse by CDC, as WooCommerce's does.