Skip to content

Commit 5587eb0

Browse files
Feature/dim-locations (#118)
* Add dim_locations. Issue #95 * Change stg_ports -> int_ports * Start decoupling dim_ports and dim_connectors * Add fact_location_capacity for location measures. Add location_key to fact_visits. Add port, connector and charge point counts to int_ports * Build hardware dimension layer and rewire facts - Add dim_charge_points, dim_ports, dim_connectors at correct grains - Fix fan-out bugs in all three dims (were sourcing from connector-grain int_ports without dedup) - Drop commissioned/decommissioned_ts from dim_ports and dim_connectors (charge point attributes, not port/connector) - Rewire fact_downtime_daily, fact_uptime, fact_visits to reference dims instead of int_ports - Add composite grain tests for dim_ports and dim_connectors - Add semantic models for charge_points, ports, connectors; wire FK entities into charge_attempts, uptime, visits - Update marts.yml docs to reflect all changes * Revert int_ports to its grain and data. Fix dim_charge_points adding port_count. Add connector_count to dim_ports * Add port_count section for dim_charge_points in marts.yml * Fix connectors_count -> connector_count * Change tests: to data_tests: project-wide according to new dbt naming * Fix arguments syntax in marts.yml * Change unit_tests mocking dims instead of int_ports * MissingArgumentsPropertyInGenericTestDeprecation there's a deprecation warning (MissingArgumentsPropertyInGenericTestDeprecation, 2 occurrences) about dbt_utils.accepted_range on dim_ports needing its arguments nested under an arguments: property. Not an error, but worth fixing. * demo to run full-refresh The first run failed because the Docker volume had a stale fact_visits table with the old location_key column. * demo ports test fix * Revert "demo to run full-refresh" This reverts commit e62adc4. * Add decommissioned_ts and is_active to dim_ports * Revert "Add decommissioned_ts and is_active to dim_ports" This reverts commit 4527dba. --------- Co-authored-by: Daria <daria@kwwhat.com>
1 parent d9c160a commit 5587eb0

15 files changed

Lines changed: 505 additions & 172 deletions

demo/chat-bi/tests/decommissioned_ports_check.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ name: decommissioned_ports_check
22
prompt: "How many ports are currently decommissioned?"
33
sql: |
44
SELECT COUNT(*) AS decommissioned_ports
5-
FROM analytics.ANALYTICS.dim_ports
6-
WHERE decommissioned_ts IS NOT NULL
5+
FROM analytics.ANALYTICS.dim_charge_points cp
6+
JOIN analytics.ANALYTICS.dim_ports p ON cp.charge_point_id = p.charge_point_id
7+
WHERE cp.decommissioned_ts IS NOT NULL

models/intermediate/int_ports.sql

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,24 @@
55
)
66
}}
77

8+
with ports as (
9+
select
10+
charge_point_id,
11+
location_id,
12+
port_id,
13+
connector_id,
14+
connector_type,
15+
commissioned_ts,
16+
decommissioned_ts,
17+
from {{ ref('stg_ports') }}
18+
)
19+
820
select
921
charge_point_id,
1022
location_id,
1123
port_id,
1224
connector_id,
1325
connector_type,
1426
commissioned_ts,
15-
decommissioned_ts
16-
from {{ ref('stg_ports') }}
27+
decommissioned_ts,
28+
from ports

models/intermediate/intermediate.yml

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,32 @@ models:
66
columns:
77
- name: charge_point_id
88
description: "Identifier for the charge point"
9-
tests:
9+
data_tests:
1010
- not_null
1111
- name: connector_id
1212
description: "Identifier for the connector"
13-
tests:
13+
data_tests:
1414
- not_null
1515
- name: port_id
1616
description: "Identifier for the port"
17-
tests:
17+
data_tests:
1818
- not_null
1919
- name: latest_status
2020
description: "Most recent OCPP status for this connector"
21-
tests:
21+
data_tests:
2222
- not_null
2323
- accepted_values:
2424
arguments:
2525
values: ['Available', 'Preparing', 'Charging', 'SuspendedEVSE', 'SuspendedEV', 'Finishing', 'Reserved', 'Unavailable', 'Faulted']
2626
- name: latest_error_code
2727
description: "Error code from the most recent StatusNotification"
28-
tests:
28+
data_tests:
2929
- not_null
3030
- name: latest_status_ts
3131
description: "Timestamp when the most recent status was ingested"
32-
tests:
32+
data_tests:
3333
- not_null
34-
tests:
34+
data_tests:
3535
- dbt_utils.unique_combination_of_columns:
3636
arguments:
3737
combination_of_columns:
@@ -44,30 +44,30 @@ models:
4444
columns:
4545
- name: charge_point_id
4646
description: "Identifier for the charge point"
47-
tests:
47+
data_tests:
4848
- not_null
4949
- name: unique_id
5050
description: "Unique identifier for the OCPP message"
51-
tests:
51+
data_tests:
5252
- not_null
5353
- name: connector_id
5454
description: "Identifier for the connector"
55-
tests:
55+
data_tests:
5656
- not_null
5757
- name: ingested_ts
5858
description: "Timestamp when the status notification was ingested"
59-
tests:
59+
data_tests:
6060
- not_null
6161
- name: status
6262
description: "Current status from the notification"
63-
tests:
63+
data_tests:
6464
- not_null
6565
- accepted_values:
6666
arguments:
6767
values: ['Available', 'Preparing', 'Charging', 'SuspendedEVSE', 'SuspendedEV', 'Finishing', 'Reserved', 'Unavailable', 'Faulted']
6868
- name: error_code
6969
description: "Error code from the status notification payload (NoError if no error to report)"
70-
tests:
70+
data_tests:
7171
- not_null
7272
- name: payload
7373
description: "Original payload from the status notification"
@@ -81,7 +81,7 @@ models:
8181
description: "Timestamp of the next status notification for the same charge point and connector (null for last status)"
8282
- name: confirmation_ingested_ts
8383
description: "Timestamp when the confirmation was ingested"
84-
tests:
84+
data_tests:
8585
- dbt_utils.unique_combination_of_columns:
8686
arguments:
8787
combination_of_columns:
@@ -104,25 +104,25 @@ models:
104104
columns:
105105
- name: charge_point_id
106106
description: "Identifier for the charge point"
107-
tests:
107+
data_tests:
108108
- not_null
109109
- name: connector_id
110110
description: "Identifier for the charging connector"
111-
tests:
111+
data_tests:
112112
- not_null
113113
- name: unique_id
114114
description: "Unique identifier for the status change that triggered this charge attempt"
115-
tests:
115+
data_tests:
116116
- not_null
117117
- name: ingested_ts
118118
description: "Timestamp when the StatusNotification with status 'Preparing' was ingested (start of charge attempt)"
119-
tests:
119+
data_tests:
120120
- not_null
121121
- name: previous_status
122122
description: "Status before changing to 'Preparing'"
123123
- name: status
124124
description: "Current status (should be 'Preparing')"
125-
tests:
125+
data_tests:
126126
- not_null
127127
- accepted_values:
128128
arguments:
@@ -141,11 +141,11 @@ models:
141141
description: "Array of error codes from StatusNotification messages"
142142
- name: _unique_transaction_count
143143
description: "Count of unique transaction IDs for this charge attempt (should be 0 or 1)"
144-
tests:
144+
data_tests:
145145
- accepted_values:
146146
arguments:
147147
values: [0, 1]
148-
tests:
148+
data_tests:
149149
- dbt_utils.unique_combination_of_columns:
150150
arguments:
151151
combination_of_columns:
@@ -158,23 +158,23 @@ models:
158158
columns:
159159
- name: transaction_id
160160
description: "Unique identifier for the transaction from OCPP StartTransaction/StopTransaction messages"
161-
tests:
161+
data_tests:
162162
- not_null
163163
- name: charge_point_id
164164
description: "Identifier for the charge point"
165-
tests:
165+
data_tests:
166166
- not_null
167167
- name: connector_id
168168
description: "Connector ID for this transaction"
169-
tests:
169+
data_tests:
170170
- not_null
171171
- name: ingested_ts
172172
description: "Earliest timestamp when any OCPP event for this transaction was ingested"
173-
tests:
173+
data_tests:
174174
- not_null
175175
- name: transaction_start_ts
176176
description: "Timestamp when the transaction started (from StartTransaction message)"
177-
tests:
177+
data_tests:
178178
- not_null
179179
- name: transaction_stop_ts
180180
description: "Timestamp when the transaction stopped (from StopTransaction message)"
@@ -193,11 +193,11 @@ models:
193193
- name: error_codes
194194
description: "Array of error codes from StatusNotification events that occurred during the transaction"
195195
- name: _unique_connectors_count
196-
tests:
196+
data_tests:
197197
- accepted_values:
198198
arguments:
199199
values: [1]
200-
tests:
200+
data_tests:
201201
- dbt_utils.unique_combination_of_columns:
202202
arguments:
203203
combination_of_columns:
@@ -210,55 +210,55 @@ models:
210210
columns:
211211
- name: charge_point_id
212212
description: "Unique identifier for the charging point"
213-
tests:
213+
data_tests:
214214
- not_null
215215
- name: transaction_id
216216
description: "Unique identifier for the charging transaction"
217-
tests:
217+
data_tests:
218218
- not_null
219219
- name: connector_id
220220
description: "Unique identifier for the connector within the charging point"
221-
tests:
221+
data_tests:
222222
- not_null
223223
- name: ingested_ts
224224
description: "Timestamp when transaction was ingested into the system"
225-
tests:
225+
data_tests:
226226
- not_null
227227
- name: measurand
228228
description: "Type of measurement (e.g., Energy.Active.Import.Register, Voltage, Current.Import)"
229-
tests:
229+
data_tests:
230230
- not_null
231231
- name: unit
232232
description: "Unit of measurement (e.g., Wh, V, A, W)"
233-
tests:
233+
data_tests:
234234
- not_null
235235
- name: phase
236236
description: "Electrical phase (e.g., L1, L2, L3) or null for single-phase measurements"
237237
- name: first_measurement_ts
238238
description: "Meter timestamp of the first measurement for the transaction"
239-
tests:
239+
data_tests:
240240
- not_null
241241
- name: last_measurement_ts
242242
description: "Meter timestamp of the last measurement for the transaction"
243-
tests:
243+
data_tests:
244244
- not_null
245245
- name: min_value
246246
description: "Minimum value recorded for the transaction"
247-
tests:
247+
data_tests:
248248
- not_null
249249
- name: max_value
250250
description: "Maximum value recorded for the transaction"
251-
tests:
251+
data_tests:
252252
- not_null
253253
- name: avg_value
254254
description: "Average value for the transaction"
255-
tests:
255+
data_tests:
256256
- not_null
257257
- name: _count
258258
description: "Total number of measurements for the transaction"
259-
tests:
259+
data_tests:
260260
- not_null
261-
tests:
261+
data_tests:
262262
- dbt_utils.unique_combination_of_columns:
263263
arguments:
264264
combination_of_columns:
@@ -292,7 +292,7 @@ models:
292292
description: "Timestamp when the charge point was commissioned (null if not commissioned yet)"
293293
- name: decommissioned_ts
294294
description: "Timestamp when the charge point was decommissioned (null if still active)"
295-
tests:
295+
data_tests:
296296
- dbt_utils.unique_combination_of_columns:
297297
arguments:
298298
combination_of_columns:

models/marts/dim_charge_points.sql

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{{
2+
config(
3+
materialized='table',
4+
description='Charge point dimension; one row per charge point.'
5+
)
6+
}}
7+
8+
with ports as (
9+
select
10+
charge_point_id,
11+
location_id,
12+
commissioned_ts,
13+
decommissioned_ts,
14+
count(distinct port_id) as port_count
15+
from {{ ref('int_ports') }}
16+
group by
17+
charge_point_id,
18+
location_id,
19+
commissioned_ts,
20+
decommissioned_ts
21+
)
22+
23+
select
24+
{{ dbt_utils.generate_surrogate_key(['ports.charge_point_id']) }} as charge_point_key,
25+
ports.charge_point_id,
26+
ports.location_id,
27+
ports.commissioned_ts,
28+
ports.decommissioned_ts,
29+
ports.decommissioned_ts is null as is_active,
30+
port_count,
31+
from ports

models/marts/dim_connectors.sql

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{{
2+
config(
3+
materialized='table',
4+
description='Connector dimension; full refresh from int_ports'
5+
)
6+
}}
7+
8+
with ports as (
9+
select
10+
charge_point_id,
11+
location_id,
12+
port_id,
13+
connector_id,
14+
connector_type,
15+
from {{ ref('int_ports') }}
16+
),
17+
18+
latest_status as (
19+
select
20+
charge_point_id,
21+
connector_id,
22+
latest_status,
23+
latest_error_code,
24+
latest_status_ts,
25+
from {{ ref('int_connector_latest_status') }}
26+
)
27+
28+
select
29+
{{ dbt_utils.generate_surrogate_key([
30+
'ports.charge_point_id',
31+
'ports.port_id',
32+
'ports.connector_id'
33+
]) }} as connector_key,
34+
ports.charge_point_id,
35+
ports.location_id,
36+
ports.port_id,
37+
ports.connector_id,
38+
ports.connector_type,
39+
latest_status.latest_status,
40+
latest_status.latest_error_code,
41+
latest_status.latest_status_ts,
42+
from ports
43+
left join latest_status
44+
on ports.charge_point_id = latest_status.charge_point_id
45+
and ports.connector_id = latest_status.connector_id

models/marts/dim_locations.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{{
2+
config(
3+
materialized='table',
4+
description="Conformed location dimension. One row per distinct charging location. SCD Type 1."
5+
)
6+
}}
7+
8+
with locations as (
9+
select distinct
10+
location_id,
11+
from {{ ref('int_ports') }}
12+
)
13+
14+
select
15+
{{ dbt_utils.generate_surrogate_key(['location_id']) }} as location_key,
16+
location_id,
17+
from locations

0 commit comments

Comments
 (0)