feat(dataflow): model production SQL flow patterns
This commit is contained in:
@@ -9,3 +9,8 @@ Each directory is an executable product contract:
|
||||
|
||||
Source nodes use a fixture filename in `config.fixture`; the golden-flow test
|
||||
injects those rows before validation and bounded execution.
|
||||
|
||||
Current contracts cover monthly reconciliation, sanctions screening, a
|
||||
HEICO-style current-status export, and the set-based normalization core of a
|
||||
RELE-style booking workflow. Fixtures are synthetic and contain no production
|
||||
records.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[
|
||||
{
|
||||
"student_id": "S-100",
|
||||
"full_name": "Ada Lovelace",
|
||||
"birth_date": "10.12.2000",
|
||||
"program": "CS",
|
||||
"status_group": "current"
|
||||
},
|
||||
{
|
||||
"student_id": "S-300",
|
||||
"full_name": "Katherine Johnson",
|
||||
"birth_date": "26.08.2001",
|
||||
"program": "MATH",
|
||||
"status_group": "current"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"nodes": [
|
||||
{
|
||||
"id": "statuses",
|
||||
"type": "source.inline",
|
||||
"label": "Student statuses",
|
||||
"position": {"x": 40, "y": 80},
|
||||
"config": {
|
||||
"source_name": "student_statuses",
|
||||
"fixture": "student-statuses.json",
|
||||
"rows": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "exclusions",
|
||||
"type": "source.inline",
|
||||
"label": "Export exclusions",
|
||||
"position": {"x": 520, "y": 300},
|
||||
"config": {
|
||||
"source_name": "export_exclusions",
|
||||
"fixture": "export-exclusions.json",
|
||||
"rows": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "latest-rank",
|
||||
"type": "window.rank",
|
||||
"label": "Latest status per student",
|
||||
"position": {"x": 280, "y": 80},
|
||||
"config": {
|
||||
"method": "row_number",
|
||||
"target_column": "status_rank",
|
||||
"partition_by": ["student_id"],
|
||||
"order_by": [
|
||||
{"column": "status_date", "direction": "desc"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "latest-only",
|
||||
"type": "filter",
|
||||
"label": "Keep latest status",
|
||||
"position": {"x": 520, "y": 80},
|
||||
"config": {
|
||||
"column": "status_rank",
|
||||
"operator": "eq",
|
||||
"value": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "eligible-only",
|
||||
"type": "combine.join",
|
||||
"label": "Exclude ineligible students",
|
||||
"position": {"x": 760, "y": 160},
|
||||
"config": {
|
||||
"join_type": "anti",
|
||||
"left_keys": ["student_id"],
|
||||
"right_keys": ["student_id"],
|
||||
"right_prefix": "excluded_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "export-calculations",
|
||||
"type": "calculate",
|
||||
"label": "Calculate export fields",
|
||||
"position": {"x": 1000, "y": 160},
|
||||
"config": {
|
||||
"calculations": [
|
||||
{
|
||||
"target_column": "full_name",
|
||||
"expression": "trim(first_name) || ' ' || trim(last_name)",
|
||||
"result_type": "string"
|
||||
},
|
||||
{
|
||||
"target_column": "birth_date_display",
|
||||
"expression": "to_char(birth_date, 'DD.MM.YYYY')",
|
||||
"result_type": "string"
|
||||
},
|
||||
{
|
||||
"target_column": "status_group",
|
||||
"expression": "case when status in ('active', 'enrolled') then 'current' else 'inactive' end",
|
||||
"result_type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "export-fields",
|
||||
"type": "select",
|
||||
"label": "HEICO export",
|
||||
"position": {"x": 1240, "y": 160},
|
||||
"config": {
|
||||
"fields": [
|
||||
{"column": "student_id", "alias": "student_id"},
|
||||
{"column": "full_name", "alias": "full_name"},
|
||||
{"column": "birth_date_display", "alias": "birth_date"},
|
||||
{"column": "program", "alias": "program"},
|
||||
{"column": "status_group", "alias": "status_group"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "output",
|
||||
"type": "output",
|
||||
"label": "Export rows",
|
||||
"position": {"x": 1480, "y": 160},
|
||||
"config": {}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{"id": "e1", "source": "statuses", "target": "latest-rank"},
|
||||
{"id": "e2", "source": "latest-rank", "target": "latest-only"},
|
||||
{
|
||||
"id": "e3",
|
||||
"source": "latest-only",
|
||||
"target": "eligible-only",
|
||||
"target_port": "left"
|
||||
},
|
||||
{
|
||||
"id": "e4",
|
||||
"source": "exclusions",
|
||||
"target": "eligible-only",
|
||||
"target_port": "right"
|
||||
},
|
||||
{"id": "e5", "source": "eligible-only", "target": "export-calculations"},
|
||||
{"id": "e6", "source": "export-calculations", "target": "export-fields"},
|
||||
{"id": "e7", "source": "export-fields", "target": "output"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"student_id": "S-200",
|
||||
"reason": "No active enrollment"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"student_id": "S-100",
|
||||
"first_name": " Ada ",
|
||||
"last_name": "Lovelace",
|
||||
"birth_date": "2000-12-10",
|
||||
"program": "CS",
|
||||
"status": "active",
|
||||
"status_date": "2026-04-01"
|
||||
},
|
||||
{
|
||||
"student_id": "S-100",
|
||||
"first_name": " Ada ",
|
||||
"last_name": "Lovelace",
|
||||
"birth_date": "2000-12-10",
|
||||
"program": "CS",
|
||||
"status": "enrolled",
|
||||
"status_date": "2025-10-01"
|
||||
},
|
||||
{
|
||||
"student_id": "S-200",
|
||||
"first_name": "Grace",
|
||||
"last_name": "Hopper",
|
||||
"birth_date": "1999-12-09",
|
||||
"program": "IS",
|
||||
"status": "withdrawn",
|
||||
"status_date": "2026-03-15"
|
||||
},
|
||||
{
|
||||
"student_id": "S-300",
|
||||
"first_name": "Katherine",
|
||||
"last_name": "Johnson",
|
||||
"birth_date": "2001-08-26",
|
||||
"program": "MATH",
|
||||
"status": "active",
|
||||
"status_date": "2026-04-02"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": "heico-student-status",
|
||||
"title": "HEICO-style current student status export",
|
||||
"user_story": "GovOPlaN/govoplan-dataflow#17",
|
||||
"graph": "graph.json",
|
||||
"expected_output": "expected-output.json"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"period": "2026/01",
|
||||
"booking_class": "personnel",
|
||||
"booking_count": 2,
|
||||
"net_amount": 70
|
||||
},
|
||||
{
|
||||
"period": "2026/02",
|
||||
"booking_class": "allocation",
|
||||
"booking_count": 1,
|
||||
"net_amount": 45
|
||||
},
|
||||
{
|
||||
"period": "2026/02",
|
||||
"booking_class": "other",
|
||||
"booking_count": 1,
|
||||
"net_amount": 20
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"nodes": [
|
||||
{
|
||||
"id": "bookings",
|
||||
"type": "source.inline",
|
||||
"label": "Bookings",
|
||||
"position": {"x": 40, "y": 120},
|
||||
"config": {
|
||||
"source_name": "bookings",
|
||||
"fixture": "bookings.json",
|
||||
"rows": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "booking-calculations",
|
||||
"type": "calculate",
|
||||
"label": "Normalize and classify",
|
||||
"position": {"x": 280, "y": 120},
|
||||
"config": {
|
||||
"calculations": [
|
||||
{
|
||||
"target_column": "cost_center",
|
||||
"expression": "cast(trim(cost_text) as integer)",
|
||||
"result_type": "integer"
|
||||
},
|
||||
{
|
||||
"target_column": "period",
|
||||
"expression": "to_char(booking_date, 'YYYY/MM')",
|
||||
"result_type": "string"
|
||||
},
|
||||
{
|
||||
"target_column": "booking_class",
|
||||
"expression": "case when cost_center between 62000 and 65999 then 'personnel' when cost_center between 97000000 and 97999999 then 'allocation' else 'other' end",
|
||||
"result_type": "string"
|
||||
},
|
||||
{
|
||||
"target_column": "signed_amount",
|
||||
"expression": "case when entry_type = 'credit' then 0 - amount else amount end",
|
||||
"result_type": "integer"
|
||||
},
|
||||
{
|
||||
"target_column": "record_key",
|
||||
"expression": "lpad(cast(sequence as text), 6, '0')",
|
||||
"result_type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "required-fields",
|
||||
"type": "quality.rules",
|
||||
"label": "Required booking fields",
|
||||
"position": {"x": 520, "y": 120},
|
||||
"config": {
|
||||
"rules": [
|
||||
{"id": "cost-center", "column": "cost_center", "operator": "not_null"},
|
||||
{"id": "period", "column": "period", "operator": "not_null"},
|
||||
{"id": "amount", "column": "signed_amount", "operator": "not_null"}
|
||||
],
|
||||
"action": "fail"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "booking-summary",
|
||||
"type": "aggregate",
|
||||
"label": "Monthly booking summary",
|
||||
"position": {"x": 760, "y": 120},
|
||||
"config": {
|
||||
"group_by": ["period", "booking_class"],
|
||||
"aggregates": [
|
||||
{"function": "count", "column": "*", "alias": "booking_count"},
|
||||
{"function": "sum", "column": "signed_amount", "alias": "net_amount"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "summary-order",
|
||||
"type": "sort",
|
||||
"label": "Order summary",
|
||||
"position": {"x": 1000, "y": 120},
|
||||
"config": {
|
||||
"fields": [
|
||||
{"column": "period", "direction": "asc"},
|
||||
{"column": "booking_class", "direction": "asc"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "output",
|
||||
"type": "output",
|
||||
"label": "Booking summary",
|
||||
"position": {"x": 1240, "y": 120},
|
||||
"config": {}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{"id": "e1", "source": "bookings", "target": "booking-calculations"},
|
||||
{"id": "e2", "source": "booking-calculations", "target": "required-fields"},
|
||||
{"id": "e3", "source": "required-fields", "target": "booking-summary"},
|
||||
{"id": "e4", "source": "booking-summary", "target": "summary-order"},
|
||||
{"id": "e5", "source": "summary-order", "target": "output"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
[
|
||||
{
|
||||
"sequence": 1,
|
||||
"booking_date": "2026-01-12",
|
||||
"cost_text": " 62150 ",
|
||||
"entry_type": "debit",
|
||||
"amount": 100
|
||||
},
|
||||
{
|
||||
"sequence": 2,
|
||||
"booking_date": "2026-01-13",
|
||||
"cost_text": "62210",
|
||||
"entry_type": "credit",
|
||||
"amount": 30
|
||||
},
|
||||
{
|
||||
"sequence": 3,
|
||||
"booking_date": "2026-02-02",
|
||||
"cost_text": "97020500",
|
||||
"entry_type": "debit",
|
||||
"amount": 45
|
||||
},
|
||||
{
|
||||
"sequence": 4,
|
||||
"booking_date": "2026-02-03",
|
||||
"cost_text": "81000",
|
||||
"entry_type": "debit",
|
||||
"amount": 20
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": "rele-booking-transform",
|
||||
"title": "RELE-style booking normalization and aggregation",
|
||||
"user_story": "GovOPlaN/govoplan-dataflow#17",
|
||||
"graph": "graph.json",
|
||||
"expected_output": "expected-output.json"
|
||||
}
|
||||
Reference in New Issue
Block a user