From 466a0af6c37e1e7c5e526f98d3a59d5079c98a48 Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Wed, 12 Aug 2026 13:19:34 +0100 Subject: [PATCH 1/8] feat(elt-pipelines): electricity_sharepoint transform This is the copied directly from the warehouses/facility_ops/transform directory, omitting the other transforms. They don't currently work because of schema changes between the warehouses and elt-pipelines ingest steps, so will be ported separately, but the electricity_sharepoint -> power_consumption transform works without changes. --- .../facility_ops/transform/.gitignore | 8 + .../facility_ops/transform/.sqlfluff | 37 +++ .../facility_ops/transform/.sqlfluffignore | 4 + .../facility_ops/transform/README.md | 26 +++ .../facility_ops/transform/analyses/.gitkeep | 0 .../facility_ops/transform/dbt_project.yml | 46 ++++ .../macros/create_equipment_category_key.sql | 4 + .../transform/macros/normalize_whitespace.sql | 8 + .../transform/macros/parse_utc_timestamp.sql | 16 ++ .../marts/accelerator/power_consumption.sql | 23 ++ .../marts/accelerator/power_consumption.yml | 11 + .../staging/estates/_estates__sources.yml | 7 + .../stg_electricity_sharepoint_rdm_data.sql | 18 ++ .../stg_electricity_sharepoint_rdm_data.yml | 11 + .../facility_ops/transform/package-lock.yml | 11 + .../facility_ops/transform/packages.yml | 7 + .../facility_ops/transform/profiles.yml | 31 +++ .../transform/requirements/developer.in | 2 + .../transform/requirements/developer.txt | 213 ++++++++++++++++++ .../transform/requirements/requirements.in | 5 + .../transform/requirements/requirements.txt | 189 ++++++++++++++++ .../facility_ops/transform/seeds/.gitkeep | 0 .../facility_ops/transform/snapshots/.gitkeep | 0 .../facility_ops/transform/tests/.gitkeep | 0 .../base_opralogweb__additional_columns.sql | 21 ++ .../base_opralogweb__chapter_entry.sql | 24 ++ .../fixtures/base_opralogweb__entries.sql | 27 +++ .../base_opralogweb__logbook_chapter.sql | 9 + .../fixtures/base_opralogweb__logbooks.sql | 4 + .../base_opralogweb__more_entry_columns.sql | 78 +++++++ .../fixtures/base_statusdisplay__cycles.sql | 4 + .../base_statusdisplay__cycles__phases.sql | 7 + 32 files changed, 851 insertions(+) create mode 100644 elt-pipelines/facility_ops/transform/.gitignore create mode 100644 elt-pipelines/facility_ops/transform/.sqlfluff create mode 100644 elt-pipelines/facility_ops/transform/.sqlfluffignore create mode 100644 elt-pipelines/facility_ops/transform/README.md create mode 100644 elt-pipelines/facility_ops/transform/analyses/.gitkeep create mode 100644 elt-pipelines/facility_ops/transform/dbt_project.yml create mode 100644 elt-pipelines/facility_ops/transform/macros/create_equipment_category_key.sql create mode 100644 elt-pipelines/facility_ops/transform/macros/normalize_whitespace.sql create mode 100644 elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql create mode 100644 elt-pipelines/facility_ops/transform/models/marts/accelerator/power_consumption.sql create mode 100644 elt-pipelines/facility_ops/transform/models/marts/accelerator/power_consumption.yml create mode 100644 elt-pipelines/facility_ops/transform/models/staging/estates/_estates__sources.yml create mode 100644 elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.sql create mode 100644 elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.yml create mode 100644 elt-pipelines/facility_ops/transform/package-lock.yml create mode 100644 elt-pipelines/facility_ops/transform/packages.yml create mode 100644 elt-pipelines/facility_ops/transform/profiles.yml create mode 100644 elt-pipelines/facility_ops/transform/requirements/developer.in create mode 100644 elt-pipelines/facility_ops/transform/requirements/developer.txt create mode 100644 elt-pipelines/facility_ops/transform/requirements/requirements.in create mode 100644 elt-pipelines/facility_ops/transform/requirements/requirements.txt create mode 100644 elt-pipelines/facility_ops/transform/seeds/.gitkeep create mode 100644 elt-pipelines/facility_ops/transform/snapshots/.gitkeep create mode 100644 elt-pipelines/facility_ops/transform/tests/.gitkeep create mode 100644 elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__additional_columns.sql create mode 100644 elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__chapter_entry.sql create mode 100644 elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__entries.sql create mode 100644 elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__logbook_chapter.sql create mode 100644 elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__logbooks.sql create mode 100644 elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__more_entry_columns.sql create mode 100644 elt-pipelines/facility_ops/transform/tests/fixtures/base_statusdisplay__cycles.sql create mode 100644 elt-pipelines/facility_ops/transform/tests/fixtures/base_statusdisplay__cycles__phases.sql diff --git a/elt-pipelines/facility_ops/transform/.gitignore b/elt-pipelines/facility_ops/transform/.gitignore new file mode 100644 index 00000000..64b8f622 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/.gitignore @@ -0,0 +1,8 @@ +logs/ +target/ +dbt_packages/ +spark-warehouse/ +metastore_db/ + +derby.log +.user.yml diff --git a/elt-pipelines/facility_ops/transform/.sqlfluff b/elt-pipelines/facility_ops/transform/.sqlfluff new file mode 100644 index 00000000..72890088 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/.sqlfluff @@ -0,0 +1,37 @@ +[sqlfluff] +dialect = trino +templater = dbt +runaway_limit = 10 +max_line_length = 80 +indent_unit = space + +[sqlfluff:indentation] +tab_space_size = 4 + +[sqlfluff:layout:type:comma] +spacing_before = touch +line_position = trailing + +[sqlfluff:rules:capitalisation.keywords] +capitalisation_policy = lower + +[sqlfluff:rules:aliasing.table] +aliasing = explicit + +[sqlfluff:rules:aliasing.column] +aliasing = explicit + +[sqlfluff:rules:aliasing.expression] +allow_scalar = False + +[sqlfluff:rules:capitalisation.identifiers] +extended_capitalisation_policy = lower + +[sqlfluff:rules:capitalisation.functions] +capitalisation_policy = lower + +[sqlfluff:rules:capitalisation.literals] +capitalisation_policy = lower + +[sqlfluff:rules:ambiguous.column_references] # Number in group by +group_by_and_order_by_style = implicit diff --git a/elt-pipelines/facility_ops/transform/.sqlfluffignore b/elt-pipelines/facility_ops/transform/.sqlfluffignore new file mode 100644 index 00000000..7b96c194 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/.sqlfluffignore @@ -0,0 +1,4 @@ +reports +target +dbt_packages +macros diff --git a/elt-pipelines/facility_ops/transform/README.md b/elt-pipelines/facility_ops/transform/README.md new file mode 100644 index 00000000..7eecc8fd --- /dev/null +++ b/elt-pipelines/facility_ops/transform/README.md @@ -0,0 +1,26 @@ +# Facility Operations catalog models + +[dbt](https://docs.getdbt.com/) projects for defining models within the `facility_ops` catalog. + +## One-time setup + +Install the Python requirements into a virtual environment: + +```bash +> uv pip install -r ./requirements/requirements.txt +``` + +Run the models against a local catalog, ensure the docker services in +[infra/local/README](../../../../infra/local/README.md#one-time-setup) are running: + +```bash +dbt run +``` + +Running the models against a remote catalog requires environment variables to be set to +point to the Trino instance, see [profiles.yml](./profiles.yml) for the required variables. +Once defined run: + +```bash +dbt run --profile remote +``` diff --git a/elt-pipelines/facility_ops/transform/analyses/.gitkeep b/elt-pipelines/facility_ops/transform/analyses/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/elt-pipelines/facility_ops/transform/dbt_project.yml b/elt-pipelines/facility_ops/transform/dbt_project.yml new file mode 100644 index 00000000..68499fc9 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/dbt_project.yml @@ -0,0 +1,46 @@ +name: facility_ops +version: "1.0.0" + +# default project points at stack in local/infra +profile: "local" + +flags: + require_certificate_validation: True + +model-paths: [ "models" ] +analysis-paths: [ "analyses" ] +test-paths: [ "tests" ] +seed-paths: [ "seeds" ] +macro-paths: [ "macros" ] +snapshot-paths: [ "snapshots" ] + +clean-targets: + # directories to be removed by `dbt clean` + - "target" + - "dbt_packages" + +dispatch: + # Use macros from trino_utils in preference. https://hub.getdbt.com/starburstdata/trino_utils/latest/ + - macro_namespace: dbt_utils + search_order: [ "trino_utils", "dbt_utils" ] + - macro_namespace: dbt_date + search_order: [ "trino_utils", "dbt_date" ] + - macro_namespace: metrics + search_order: [ "trino_utils", "metrics" ] + +models: + +file_format: parquet + +materialized: view + facility_ops: + # The full schema name of the final tables is "{target_schema}_"{custom_schema}" + # - 'target_schema' is defined in profiles.yml + # - 'custom_schema' is defined for each collection of models below + # See https://docs.getdbt.com/docs/build/custom-schemas + staging: + +schema: staging + marts: + +materialized: table + accelerator: + +schema: accelerator + beamlines: + +schema: beamlines diff --git a/elt-pipelines/facility_ops/transform/macros/create_equipment_category_key.sql b/elt-pipelines/facility_ops/transform/macros/create_equipment_category_key.sql new file mode 100644 index 00000000..6b5aaba2 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/macros/create_equipment_category_key.sql @@ -0,0 +1,4 @@ +-- Creates a key for the equipment -> category mapping table +{%- macro create_equipment_category_key(text_col) -%} + lower( {{ normalize_whitespace(text_col) }} ) +{%- endmacro -%} diff --git a/elt-pipelines/facility_ops/transform/macros/normalize_whitespace.sql b/elt-pipelines/facility_ops/transform/macros/normalize_whitespace.sql new file mode 100644 index 00000000..8d0bab94 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/macros/normalize_whitespace.sql @@ -0,0 +1,8 @@ +-- Removes leading and trailing whitespace and replaces any multiple-spaces with a single space +{%- macro normalize_whitespace(text_col) -%} + {{ return(adapter.dispatch('normalize_whitespace')(text_col)) }} +{% endmacro %} + +{%- macro default__normalize_whitespace(text_col) -%} + regexp_replace(trim({{ text_col }}), '\s+', ' ') +{%- endmacro -%} diff --git a/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql b/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql new file mode 100644 index 00000000..35502409 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql @@ -0,0 +1,16 @@ +-- Parse separate date and time strings into a single UTC timestamp +{%- macro parse_utc_timestamp(date_col, date_format, time_col, time_format='HH:mm:ss', src_timezone='UTC') -%} + {{ return(adapter.dispatch('parse_utc_timestamp')(date_col, date_format, time_col, time_format, src_timezone)) }} +{% endmacro %} + + +{%- macro trino__parse_utc_timestamp(date_col, date_format, time_col, time_format, src_timezone) -%} + with_timezone( + cast( + parse_datetime({{ adapter.quote(date_col) }} || ' ' || {{ adapter.quote(time_col) }}, + '{{ date_format ~ ' ' ~ time_format }}') + as timestamp(6) + ), + '{{ src_timezone }}' + ) at time zone 'UTC' +{%- endmacro -%} diff --git a/elt-pipelines/facility_ops/transform/models/marts/accelerator/power_consumption.sql b/elt-pipelines/facility_ops/transform/models/marts/accelerator/power_consumption.sql new file mode 100644 index 00000000..b849fda4 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/models/marts/accelerator/power_consumption.sql @@ -0,0 +1,23 @@ +{{ + config( + properties={ + "partitioning": "ARRAY['year(power_measured_at)']", + }, + on_table_exists = 'drop' +) +}} + +with + +staged as ( + + select + + power_measured_at, + total_isis_power_mw + + from {{ ref('stg_electricity_sharepoint_rdm_data') }} + +) + +select * from staged diff --git a/elt-pipelines/facility_ops/transform/models/marts/accelerator/power_consumption.yml b/elt-pipelines/facility_ops/transform/models/marts/accelerator/power_consumption.yml new file mode 100644 index 00000000..fe492fd1 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/models/marts/accelerator/power_consumption.yml @@ -0,0 +1,11 @@ +models: + - name: power_consumption + description: > + The total power consumption for ISIS over time. + columns: + - name: power_measured_at + data_tests: + - not_null + - name: total_isis_power_mw + data_tests: + - not_null diff --git a/elt-pipelines/facility_ops/transform/models/staging/estates/_estates__sources.yml b/elt-pipelines/facility_ops/transform/models/staging/estates/_estates__sources.yml new file mode 100644 index 00000000..cafd7824 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/models/staging/estates/_estates__sources.yml @@ -0,0 +1,7 @@ +version: 2 + +sources: + - name: estates_electricity_sharepoint + database: facility_ops_landing + tables: + - name: rdm_data diff --git a/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.sql b/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.sql new file mode 100644 index 00000000..0bab0a59 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.sql @@ -0,0 +1,18 @@ +with source as ( + + select * from {{ source('estates_electricity_sharepoint', 'rdm_data') }} + +), + +renamed as ( + + select + + date_time as power_measured_at, + isis_elec_total_power_mw as total_isis_power_mw + + from source + +) + +select * from renamed diff --git a/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.yml b/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.yml new file mode 100644 index 00000000..d4a781bc --- /dev/null +++ b/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.yml @@ -0,0 +1,11 @@ +models: + - name: stg_electricity_sharepoint_rdm_data + description: > + ISIS electricity consumption. + columns: + - name: power_measured_at + data_tests: + - not_null + - name: power_consumption_mw + data_tests: + - not_null diff --git a/elt-pipelines/facility_ops/transform/package-lock.yml b/elt-pipelines/facility_ops/transform/package-lock.yml new file mode 100644 index 00000000..017c94e5 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/package-lock.yml @@ -0,0 +1,11 @@ +packages: + - name: codegen + package: dbt-labs/codegen + version: 0.14.0 + - name: dbt_utils + package: dbt-labs/dbt_utils + version: 1.3.3 + - name: trino_utils + package: starburstdata/trino_utils + version: 0.6.0 +sha1_hash: dc8ade786ecde1c757d4fd39ba36eb7ddc99a6af diff --git a/elt-pipelines/facility_ops/transform/packages.yml b/elt-pipelines/facility_ops/transform/packages.yml new file mode 100644 index 00000000..f4bc168a --- /dev/null +++ b/elt-pipelines/facility_ops/transform/packages.yml @@ -0,0 +1,7 @@ +packages: + - package: dbt-labs/codegen + version: 0.14.0 + - package: dbt-labs/dbt_utils + version: 1.3.3 + - package: starburstdata/trino_utils + version: 0.6.0 diff --git a/elt-pipelines/facility_ops/transform/profiles.yml b/elt-pipelines/facility_ops/transform/profiles.yml new file mode 100644 index 00000000..d4dcc190 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/profiles.yml @@ -0,0 +1,31 @@ +local: + target: trino + outputs: + trino: + type: trino + method: ldap + user: machine-infra + password: s3cr3t + host: localhost + port: 58443 + http_scheme: https + database: facility_ops + schema: analytics + threads: 8 + cert: false + suppress_cert_warning: true + +remote: + target: trino + outputs: + trino: + type: trino + method: ldap + user: "{{ env_var('DBT_TRINO_USER') }}" + password: "{{ env_var('DBT_TRINO_PASSWORD') }}" + http_scheme: "{{ env_var('DBT_TRINO_HTTP_SCHEME') }}" + host: "{{ env_var('DBT_TRINO_HOST') }}" + port: "{{ env_var('DBT_TRINO_PORT') | int }}" + database: "{{ env_var('DBT_TRINO_CATALOG') }}" + schema: "{{ env_var('DBT_TRINO_CATALOG_SCHEMA_PREFIX') }}analytics" + threads: "{{ env_var('DBT_TRINO_THREADS', '8') | int }}" diff --git a/elt-pipelines/facility_ops/transform/requirements/developer.in b/elt-pipelines/facility_ops/transform/requirements/developer.in new file mode 100644 index 00000000..2746bd3a --- /dev/null +++ b/elt-pipelines/facility_ops/transform/requirements/developer.in @@ -0,0 +1,2 @@ +sqlfluff>=3.4.0,<3.5.0 +sqlfluff-templater-dbt>=3.4.0,<3.5.0 diff --git a/elt-pipelines/facility_ops/transform/requirements/developer.txt b/elt-pipelines/facility_ops/transform/requirements/developer.txt new file mode 100644 index 00000000..ca268123 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/requirements/developer.txt @@ -0,0 +1,213 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile requirements/developer.in +agate==1.9.1 + # via + # dbt-adapters + # dbt-common + # dbt-core +annotated-types==0.7.0 + # via pydantic +attrs==25.3.0 + # via + # jsonschema + # referencing +babel==2.17.0 + # via agate +certifi==2025.8.3 + # via requests +chardet==7.4.3 + # via + # diff-cover + # sqlfluff +charset-normalizer==3.4.3 + # via requests +click==8.3.0 + # via + # dbt-core + # sqlfluff +colorama==0.4.6 + # via + # dbt-common + # sqlfluff +daff==1.4.2 + # via dbt-core +dbt-adapters==1.24.5 + # via dbt-core +dbt-common==1.38.0 + # via + # dbt-adapters + # dbt-core +dbt-core==1.12.0 + # via sqlfluff-templater-dbt +dbt-core-experimental-parser==2.0.0a5 + # via dbt-core +dbt-extractor==0.6.0 + # via dbt-core +dbt-protos==1.0.541 + # via + # dbt-adapters + # dbt-common + # dbt-core +deepdiff==8.6.2 + # via dbt-common +diff-cover==9.6.0 + # via sqlfluff +idna==3.15 + # via requests +importlib-metadata==8.7.0 + # via metricflow +iniconfig==2.1.0 + # via pytest +isodate==0.6.1 + # via + # agate + # dbt-common +jinja2==3.1.6 + # via + # dbt-common + # dbt-core + # diff-cover + # jinja2-simple-tags + # metricflow + # sqlfluff +jinja2-simple-tags==0.6.1 + # via sqlfluff-templater-dbt +jsonschema==4.25.1 + # via + # dbt-common + # dbt-core + # metricflow +jsonschema-specifications==2025.9.1 + # via jsonschema +leather==0.4.0 + # via agate +markupsafe==3.0.2 + # via jinja2 +mashumaro[msgpack]==3.14 + # via + # dbt-adapters + # dbt-common + # dbt-core +metricflow==0.211.0 + # via dbt-core +more-itertools==10.8.0 + # via metricflow +msgpack==1.2.1 + # via mashumaro +networkx==3.5 + # via dbt-core +orderly-set==5.5.0 + # via deepdiff +packaging==25.0 + # via + # dbt-core + # pytest +parsedatetime==2.6 + # via agate +pathspec==0.12.1 + # via + # dbt-common + # dbt-core + # sqlfluff +platformdirs==4.4.0 + # via sqlfluff +pluggy==1.6.0 + # via + # diff-cover + # pytest +protobuf==6.33.5 + # via + # dbt-adapters + # dbt-common + # dbt-core + # dbt-protos +pydantic==2.11.7 + # via + # dbt-core + # metricflow +pydantic-core==2.33.2 + # via pydantic +pygments==2.19.2 + # via + # diff-cover + # pytest +pytest==9.0.3 + # via sqlfluff +python-dateutil==2.9.0.post0 + # via + # dbt-common + # metricflow +python-dotenv==1.2.2 + # via dbt-core +python-slugify==8.0.4 + # via agate +pytimeparse==1.1.8 + # via agate +pytz==2025.2 + # via + # dbt-adapters + # dbt-core +pyyaml==6.0.2 + # via + # dbt-core + # metricflow + # sqlfluff +rapidfuzz==3.14.5 + # via metricflow +referencing==0.36.2 + # via + # jsonschema + # jsonschema-specifications + # metricflow +regex==2025.9.1 + # via sqlfluff +requests==2.33.0 + # via + # dbt-common + # dbt-core + # snowplow-tracker +rpds-py==0.27.1 + # via + # jsonschema + # referencing +six==1.17.0 + # via + # isodate + # python-dateutil +snowplow-tracker==1.1.0 + # via dbt-core +sqlfluff==3.4.2 + # via + # -r requirements/developer.in + # sqlfluff-templater-dbt +sqlfluff-templater-dbt==3.4.2 + # via -r requirements/developer.in +sqlglot==30.14.0 + # via metricflow +sqlparse==0.5.5 + # via dbt-core +tabulate==0.10.0 + # via metricflow +tblib==3.1.0 + # via sqlfluff +text-unidecode==1.3 + # via python-slugify +tqdm==4.67.1 + # via sqlfluff +typing-extensions==4.15.0 + # via + # dbt-adapters + # dbt-common + # dbt-core + # mashumaro + # metricflow + # pydantic + # pydantic-core + # snowplow-tracker + # typing-inspection +typing-inspection==0.4.1 + # via pydantic +urllib3==2.7.0 + # via requests +zipp==3.23.0 + # via importlib-metadata diff --git a/elt-pipelines/facility_ops/transform/requirements/requirements.in b/elt-pipelines/facility_ops/transform/requirements/requirements.in new file mode 100644 index 00000000..3842d03a --- /dev/null +++ b/elt-pipelines/facility_ops/transform/requirements/requirements.in @@ -0,0 +1,5 @@ +dbt-adapters>=1.24.5,<2 +dbt-core>=1.12.0,<2 +dbt-trino>=1.10.2,<2 +# Temp pin to avoid requests raising exception. See https://github.com/psf/requests/issues/7219 +chardet==7.4.3 diff --git a/elt-pipelines/facility_ops/transform/requirements/requirements.txt b/elt-pipelines/facility_ops/transform/requirements/requirements.txt new file mode 100644 index 00000000..66234d1a --- /dev/null +++ b/elt-pipelines/facility_ops/transform/requirements/requirements.txt @@ -0,0 +1,189 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile requirements/requirements.in +agate==1.9.1 + # via + # dbt-adapters + # dbt-common + # dbt-core +annotated-types==0.7.0 + # via pydantic +attrs==26.1.0 + # via + # jsonschema + # referencing +babel==2.18.0 + # via agate +certifi==2026.2.25 + # via requests +chardet==7.4.3 + # via -r requirements/requirements.in +charset-normalizer==3.4.7 + # via requests +click==8.3.2 + # via dbt-core +colorama==0.4.6 + # via dbt-common +daff==1.4.2 + # via dbt-core +dbt-adapters==1.24.5 + # via + # -r requirements/requirements.in + # dbt-core + # dbt-trino +dbt-common==1.38.0 + # via + # dbt-adapters + # dbt-core + # dbt-trino +dbt-core==1.12.0 + # via + # -r requirements/requirements.in + # dbt-trino +dbt-core-experimental-parser==2.0.0a5 + # via dbt-core +dbt-extractor==0.6.0 + # via dbt-core +dbt-protos==1.0.541 + # via + # dbt-adapters + # dbt-common + # dbt-core +dbt-trino==1.10.2 + # via -r requirements/requirements.in +deepdiff==8.6.2 + # via dbt-common +idna==3.15 + # via requests +importlib-metadata==8.9.0 + # via metricflow +isodate==0.7.2 + # via + # agate + # dbt-common +jinja2==3.1.6 + # via + # dbt-common + # dbt-core + # metricflow +jsonschema==4.26.0 + # via + # dbt-common + # dbt-core + # metricflow +jsonschema-specifications==2025.9.1 + # via jsonschema +leather==0.4.1 + # via agate +lz4==4.4.5 + # via trino +markupsafe==3.0.3 + # via jinja2 +mashumaro[msgpack]==3.14 + # via + # dbt-adapters + # dbt-common + # dbt-core +metricflow==0.211.0 + # via dbt-core +more-itertools==10.8.0 + # via metricflow +msgpack==1.2.1 + # via mashumaro +networkx==3.6.1 + # via dbt-core +orderly-set==5.5.0 + # via deepdiff +orjson==3.11.8 + # via trino +packaging==26.1 + # via dbt-core +parsedatetime==2.6 + # via agate +pathspec==0.12.1 + # via + # dbt-common + # dbt-core +protobuf==6.33.6 + # via + # dbt-adapters + # dbt-common + # dbt-core + # dbt-protos +pydantic==2.13.3 + # via + # dbt-core + # metricflow +pydantic-core==2.46.3 + # via pydantic +python-dateutil==2.9.0.post0 + # via + # dbt-common + # metricflow + # trino +python-dotenv==1.2.2 + # via dbt-core +python-slugify==8.0.4 + # via agate +pytimeparse==1.1.8 + # via agate +pytz==2026.1.post1 + # via + # dbt-adapters + # dbt-core + # trino +pyyaml==6.0.3 + # via + # dbt-core + # metricflow +rapidfuzz==3.14.5 + # via metricflow +referencing==0.37.0 + # via + # jsonschema + # jsonschema-specifications + # metricflow +requests==2.33.1 + # via + # dbt-common + # dbt-core + # snowplow-tracker + # trino +rpds-py==0.30.0 + # via + # jsonschema + # referencing +six==1.17.0 + # via python-dateutil +snowplow-tracker==1.1.0 + # via dbt-core +sqlglot==30.14.0 + # via metricflow +sqlparse==0.5.5 + # via dbt-core +tabulate==0.10.0 + # via metricflow +text-unidecode==1.3 + # via python-slugify +trino==0.337.0 + # via dbt-trino +typing-extensions==4.15.0 + # via + # dbt-adapters + # dbt-common + # dbt-core + # mashumaro + # metricflow + # pydantic + # pydantic-core + # snowplow-tracker + # typing-inspection +typing-inspection==0.4.2 + # via pydantic +tzlocal==5.3.1 + # via trino +urllib3==2.7.0 + # via requests +zipp==3.23.1 + # via importlib-metadata +zstandard==0.25.0 + # via trino diff --git a/elt-pipelines/facility_ops/transform/seeds/.gitkeep b/elt-pipelines/facility_ops/transform/seeds/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/elt-pipelines/facility_ops/transform/snapshots/.gitkeep b/elt-pipelines/facility_ops/transform/snapshots/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/elt-pipelines/facility_ops/transform/tests/.gitkeep b/elt-pipelines/facility_ops/transform/tests/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__additional_columns.sql b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__additional_columns.sql new file mode 100644 index 00000000..ee716ee4 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__additional_columns.sql @@ -0,0 +1,21 @@ +select + cast(1 as integer) as additional_column_id, + cast('Equipment' as varchar) as column_title + +union all + +select + cast(2 as integer) as additional_column_id, + cast('Group' as varchar) as column_title + +union all + +select + cast(3 as integer) as additional_column_id, + cast('Lost Time' as varchar) as column_title + +union all + +select + cast(4 as integer) as additional_column_id, + cast('Group Leader comments' as varchar) as column_title diff --git a/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__chapter_entry.sql b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__chapter_entry.sql new file mode 100644 index 00000000..73b26b90 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__chapter_entry.sql @@ -0,0 +1,24 @@ +select + + cast(100 as integer) as entry_id, + cast(24 as integer) as principal_logbook, + cast(1 as integer) as logbook_chapter_no, + cast(24 as integer) as logbook_id + +union all + +select + + cast(101 as integer) as entry_id, + cast(24 as integer) as principal_logbook, + cast(2 as integer) as logbook_chapter_no, + cast(24 as integer) as logbook_id + +union all + +select + + cast(102 as integer) as entry_id, + cast(24 as integer) as principal_logbook, + cast(2 as integer) as logbook_chapter_no, + cast(24 as integer) as logbook_id diff --git a/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__entries.sql b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__entries.sql new file mode 100644 index 00000000..c29f1759 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__entries.sql @@ -0,0 +1,27 @@ +select + + cast(100 as integer) as entry_id, + with_timezone(timestamp '2017-04-25 23:59:59', 'UTC') as fault_occurred_at, + date '2017-04-24' as fault_date, + cast('Comment 100' as varchar) as fault_description, + false as logically_deleted + +union all + +select + + cast(101 as integer) as entry_id, + with_timezone(timestamp '2024-01-01 00:00:01', 'UTC') as fault_occurred_at, + date '2024-01-01' as fault_date, + cast('Comment 101' as varchar) as fault_description, + false as logically_deleted + +union all + +select + + cast(102 as integer) as entry_id, + with_timezone(timestamp '2024-01-01 00:01:03', 'UTC') as fault_occurred_at, + date '2024-01-01' as fault_date, + cast('Deleted 102' as varchar) as fault_description, + true as logically_deleted diff --git a/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__logbook_chapter.sql b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__logbook_chapter.sql new file mode 100644 index 00000000..d7c3f375 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__logbook_chapter.sql @@ -0,0 +1,9 @@ +select + + cast(1 as integer) as logbook_chapter_no + +union all + +select + + cast(2 as integer) as logbook_chapter_no diff --git a/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__logbooks.sql b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__logbooks.sql new file mode 100644 index 00000000..30e4e40b --- /dev/null +++ b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__logbooks.sql @@ -0,0 +1,4 @@ +select + + cast(24 as integer) as logbook_id, + cast('MCR Running Log' as varchar) as logbook_name diff --git a/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__more_entry_columns.sql b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__more_entry_columns.sql new file mode 100644 index 00000000..662fa7eb --- /dev/null +++ b/elt-pipelines/facility_ops/transform/tests/fixtures/base_opralogweb__more_entry_columns.sql @@ -0,0 +1,78 @@ +select + + cast(100 as integer) as entry_id, + cast('Equipment 100' as varchar) as string_data, + cast(null as double) as number_data, + cast(1 as integer) as additional_column_id + +union all + +select + + cast(100 as integer) as entry_id, + cast('Group 100' as varchar) as string_data, + cast(null as double) as number_data, + cast(2 as integer) as additional_column_id + +union all + +select + + cast(100 as integer) as entry_id, + cast(null as varchar) as string_data, + cast(4.2 as double) as number_data, + cast(3 as integer) as additional_column_id + +union all + +select + + cast(101 as integer) as entry_id, + cast('Equipment 101' as varchar) as string_data, + cast(null as double) as number_data, + cast(1 as integer) as additional_column_id + +union all + +select + + cast(101 as integer) as entry_id, + cast('Group 101' as varchar) as string_data, + cast(null as double) as number_data, + cast(2 as integer) as additional_column_id + +union all + +select + + cast(101 as integer) as entry_id, + cast(null as varchar) as string_data, + cast(5.3 as double) as number_data, + cast(3 as integer) as additional_column_id + +union all + +select + + cast(102 as integer) as entry_id, + cast('Equipment 102' as varchar) as string_data, + cast(null as double) as number_data, + cast(1 as integer) as additional_column_id + +union all + +select + + cast(102 as integer) as entry_id, + cast('Group 102' as varchar) as string_data, + cast(null as double) as number_data, + cast(2 as integer) as additional_column_id + +union all + +select + + cast(102 as integer) as entry_id, + cast(null as varchar) as string_data, + cast(4.3 as double) as number_data, + cast(3 as integer) as additional_column_id diff --git a/elt-pipelines/facility_ops/transform/tests/fixtures/base_statusdisplay__cycles.sql b/elt-pipelines/facility_ops/transform/tests/fixtures/base_statusdisplay__cycles.sql new file mode 100644 index 00000000..9eac95b0 --- /dev/null +++ b/elt-pipelines/facility_ops/transform/tests/fixtures/base_statusdisplay__cycles.sql @@ -0,0 +1,4 @@ +select + + cast('h344o234ii5o6o4' as varchar) as dlt_id, + cast('2024/2' as varchar) as name diff --git a/elt-pipelines/facility_ops/transform/tests/fixtures/base_statusdisplay__cycles__phases.sql b/elt-pipelines/facility_ops/transform/tests/fixtures/base_statusdisplay__cycles__phases.sql new file mode 100644 index 00000000..5194a88e --- /dev/null +++ b/elt-pipelines/facility_ops/transform/tests/fixtures/base_statusdisplay__cycles__phases.sql @@ -0,0 +1,7 @@ +select + + cast('user-time' as varchar) as phase, + cast(1 as integer) as target, + timestamp '2024-07-09 07:30:00 UTC' as started_at, + timestamp '2024-07-24 07:30:00 UTC' as ended_at, + cast('h344o234ii5o6o4' as varchar) as dlt_cycles_id From 27c0a12e7987c9fd613866009f62b152c3683789 Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Wed, 12 Aug 2026 15:17:33 +0100 Subject: [PATCH 2/8] docs(elt-pipelines): Interim updates to transform README --- elt-pipelines/README.md | 13 ++++++---- .../facility_ops/transform/README.md | 25 ++++++++++++++++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/elt-pipelines/README.md b/elt-pipelines/README.md index 658469c4..ddccbf5e 100644 --- a/elt-pipelines/README.md +++ b/elt-pipelines/README.md @@ -104,11 +104,14 @@ elt-pipelines/ | | |-- / | | | |-- / | | | | |-- .py +| |-- transform/ ``` - This directory structure is required for using `elt-common` -- Each 'target warehouse' is the name of an Iceberg warehouse. The data ingested by the pipelines inside that directory - end up in that warehouse. -- Data from ingest pipelines is considered 'raw' data, and is loaded into a warehouse suffixed with `_landing`. -- Under construction: Each warehouse will also have a `transform` subdirectory containing pipelines for converting the - raw data into its final state in the target warehouse. +- Each 'target warehouse' is the name of an Iceberg warehouse. The data handled by the pipelines inside that directory + end up in that warehouse +- Data from ingest pipelines is considered 'raw' data, and is loaded into a warehouse suffixed with `_landing` under + the `_` namespace +- The `transform` directory contains a `dbt` project which defines ways of transforming the raw data in the landing + warehouse into cleaned 'models' in ``. For more details see + [one of the project readmes](facility_ops/transform/README.md) diff --git a/elt-pipelines/facility_ops/transform/README.md b/elt-pipelines/facility_ops/transform/README.md index 7eecc8fd..4370b945 100644 --- a/elt-pipelines/facility_ops/transform/README.md +++ b/elt-pipelines/facility_ops/transform/README.md @@ -1,17 +1,34 @@ # Facility Operations catalog models -[dbt](https://docs.getdbt.com/) projects for defining models within the `facility_ops` catalog. +This is a [dbt](https://docs.getdbt.com/) project which defines transforms for turning the +raw data in the `facility_ops_landing` warehouse into cleaned models in `facility_ops`. -## One-time setup +**Under construction. Currently only the electricity_sharepoint transforms work.** -Install the Python requirements into a virtual environment: +## Running with elt + +The `elt run` command defined by [`elt-common`](../../../elt-common) can be used to run +transforms from this project. + +Ingest jobs ingest data into a namespace defined by the +[ingest directory structure](../../README.md#directory-structure). That namespace can be used +to run the transform(s) for the pipeline with `elt run facility_ops --step transform`. + +## Running with dbt + +It's also possible to run the transforms (or interact with them in other ways) using `dbt` +directly. + +To set up the environment, install the python requirements into a virtual environment: ```bash > uv pip install -r ./requirements/requirements.txt ``` +Run `dbt deps` to install the project dependencies. + Run the models against a local catalog, ensure the docker services in -[infra/local/README](../../../../infra/local/README.md#one-time-setup) are running: +[infra/local/README](../../../infra/local/README.md#local-set-up) are running: ```bash dbt run From 5745e06d6dfe171d1a33b140ed1cf8ee61bfd89d Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Wed, 12 Aug 2026 15:42:42 +0100 Subject: [PATCH 3/8] Fix dbt test --- .../staging/estates/stg_electricity_sharepoint_rdm_data.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.yml b/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.yml index d4a781bc..6a193366 100644 --- a/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.yml +++ b/elt-pipelines/facility_ops/transform/models/staging/estates/stg_electricity_sharepoint_rdm_data.yml @@ -6,6 +6,6 @@ models: - name: power_measured_at data_tests: - not_null - - name: power_consumption_mw + - name: total_isis_power_mw data_tests: - not_null From 2595d86e698e067aa93525bd0b5f363f7670fa6d Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Wed, 12 Aug 2026 16:01:49 +0100 Subject: [PATCH 4/8] Remove unused project config --- elt-pipelines/facility_ops/transform/dbt_project.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/elt-pipelines/facility_ops/transform/dbt_project.yml b/elt-pipelines/facility_ops/transform/dbt_project.yml index 68499fc9..b1c293cb 100644 --- a/elt-pipelines/facility_ops/transform/dbt_project.yml +++ b/elt-pipelines/facility_ops/transform/dbt_project.yml @@ -42,5 +42,3 @@ models: +materialized: table accelerator: +schema: accelerator - beamlines: - +schema: beamlines From cf3935408fd5c81b83f0be5b8c796d5bd32a3c9f Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Thu, 13 Aug 2026 11:41:58 +0100 Subject: [PATCH 5/8] Remove requirements from dbt project These are included transitively from elt-common via elt-pipelines. The dbt-adapater dependency is provided transitively by dbt-trino. For the pinned dependency, it looks like it may have been fixed upstream. If not, I'll look at fixing it when it causes problems. --- .../facility_ops/transform/README.md | 37 +-- .../transform/requirements/developer.in | 2 - .../transform/requirements/developer.txt | 213 ------------------ .../transform/requirements/requirements.in | 5 - .../transform/requirements/requirements.txt | 189 ---------------- 5 files changed, 12 insertions(+), 434 deletions(-) delete mode 100644 warehouses/facility_ops/transform/requirements/developer.in delete mode 100644 warehouses/facility_ops/transform/requirements/developer.txt delete mode 100644 warehouses/facility_ops/transform/requirements/requirements.in delete mode 100644 warehouses/facility_ops/transform/requirements/requirements.txt diff --git a/elt-pipelines/facility_ops/transform/README.md b/elt-pipelines/facility_ops/transform/README.md index 4370b945..d25debc8 100644 --- a/elt-pipelines/facility_ops/transform/README.md +++ b/elt-pipelines/facility_ops/transform/README.md @@ -16,28 +16,15 @@ to run the transform(s) for the pipeline with `elt run facility_ops ## Running with dbt -It's also possible to run the transforms (or interact with them in other ways) using `dbt` -directly. - -To set up the environment, install the python requirements into a virtual environment: - -```bash -> uv pip install -r ./requirements/requirements.txt -``` - -Run `dbt deps` to install the project dependencies. - -Run the models against a local catalog, ensure the docker services in -[infra/local/README](../../../infra/local/README.md#local-set-up) are running: - -```bash -dbt run -``` - -Running the models against a remote catalog requires environment variables to be set to -point to the Trino instance, see [profiles.yml](./profiles.yml) for the required variables. -Once defined run: - -```bash -dbt run --profile remote -``` +It's also possible to run the transforms (or interact with them in other ways) using `dbt` directly. + +- Use a python environment with [elt-pipelines](../../README.md#setting-up-a-python-virtual-environment) + or [elt-common](../../../elt-common/README.md#setting-up-a-python-virtual-environment) installed + - These provide the required `dbt` dependencies, and the `dbt` cli tool +- Make the dbt project directory (`elt-pipelines/facility_ops/transform`) the working directory +- Run `dbt deps` to install the project dependencies +- For running against a local catalog, ensure the docker + services [are running](../../../infra/local/README.md#local-set-up) +- Run `dbt` commands whilst in the dbt project directory +- To run against a remote catalog, ensure the [required environment variables](./profiles.yml) are set up to point to + the Trino instance, then use the `--profile remote` option when running `dbt` commands diff --git a/warehouses/facility_ops/transform/requirements/developer.in b/warehouses/facility_ops/transform/requirements/developer.in deleted file mode 100644 index 2746bd3a..00000000 --- a/warehouses/facility_ops/transform/requirements/developer.in +++ /dev/null @@ -1,2 +0,0 @@ -sqlfluff>=3.4.0,<3.5.0 -sqlfluff-templater-dbt>=3.4.0,<3.5.0 diff --git a/warehouses/facility_ops/transform/requirements/developer.txt b/warehouses/facility_ops/transform/requirements/developer.txt deleted file mode 100644 index ca268123..00000000 --- a/warehouses/facility_ops/transform/requirements/developer.txt +++ /dev/null @@ -1,213 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile requirements/developer.in -agate==1.9.1 - # via - # dbt-adapters - # dbt-common - # dbt-core -annotated-types==0.7.0 - # via pydantic -attrs==25.3.0 - # via - # jsonschema - # referencing -babel==2.17.0 - # via agate -certifi==2025.8.3 - # via requests -chardet==7.4.3 - # via - # diff-cover - # sqlfluff -charset-normalizer==3.4.3 - # via requests -click==8.3.0 - # via - # dbt-core - # sqlfluff -colorama==0.4.6 - # via - # dbt-common - # sqlfluff -daff==1.4.2 - # via dbt-core -dbt-adapters==1.24.5 - # via dbt-core -dbt-common==1.38.0 - # via - # dbt-adapters - # dbt-core -dbt-core==1.12.0 - # via sqlfluff-templater-dbt -dbt-core-experimental-parser==2.0.0a5 - # via dbt-core -dbt-extractor==0.6.0 - # via dbt-core -dbt-protos==1.0.541 - # via - # dbt-adapters - # dbt-common - # dbt-core -deepdiff==8.6.2 - # via dbt-common -diff-cover==9.6.0 - # via sqlfluff -idna==3.15 - # via requests -importlib-metadata==8.7.0 - # via metricflow -iniconfig==2.1.0 - # via pytest -isodate==0.6.1 - # via - # agate - # dbt-common -jinja2==3.1.6 - # via - # dbt-common - # dbt-core - # diff-cover - # jinja2-simple-tags - # metricflow - # sqlfluff -jinja2-simple-tags==0.6.1 - # via sqlfluff-templater-dbt -jsonschema==4.25.1 - # via - # dbt-common - # dbt-core - # metricflow -jsonschema-specifications==2025.9.1 - # via jsonschema -leather==0.4.0 - # via agate -markupsafe==3.0.2 - # via jinja2 -mashumaro[msgpack]==3.14 - # via - # dbt-adapters - # dbt-common - # dbt-core -metricflow==0.211.0 - # via dbt-core -more-itertools==10.8.0 - # via metricflow -msgpack==1.2.1 - # via mashumaro -networkx==3.5 - # via dbt-core -orderly-set==5.5.0 - # via deepdiff -packaging==25.0 - # via - # dbt-core - # pytest -parsedatetime==2.6 - # via agate -pathspec==0.12.1 - # via - # dbt-common - # dbt-core - # sqlfluff -platformdirs==4.4.0 - # via sqlfluff -pluggy==1.6.0 - # via - # diff-cover - # pytest -protobuf==6.33.5 - # via - # dbt-adapters - # dbt-common - # dbt-core - # dbt-protos -pydantic==2.11.7 - # via - # dbt-core - # metricflow -pydantic-core==2.33.2 - # via pydantic -pygments==2.19.2 - # via - # diff-cover - # pytest -pytest==9.0.3 - # via sqlfluff -python-dateutil==2.9.0.post0 - # via - # dbt-common - # metricflow -python-dotenv==1.2.2 - # via dbt-core -python-slugify==8.0.4 - # via agate -pytimeparse==1.1.8 - # via agate -pytz==2025.2 - # via - # dbt-adapters - # dbt-core -pyyaml==6.0.2 - # via - # dbt-core - # metricflow - # sqlfluff -rapidfuzz==3.14.5 - # via metricflow -referencing==0.36.2 - # via - # jsonschema - # jsonschema-specifications - # metricflow -regex==2025.9.1 - # via sqlfluff -requests==2.33.0 - # via - # dbt-common - # dbt-core - # snowplow-tracker -rpds-py==0.27.1 - # via - # jsonschema - # referencing -six==1.17.0 - # via - # isodate - # python-dateutil -snowplow-tracker==1.1.0 - # via dbt-core -sqlfluff==3.4.2 - # via - # -r requirements/developer.in - # sqlfluff-templater-dbt -sqlfluff-templater-dbt==3.4.2 - # via -r requirements/developer.in -sqlglot==30.14.0 - # via metricflow -sqlparse==0.5.5 - # via dbt-core -tabulate==0.10.0 - # via metricflow -tblib==3.1.0 - # via sqlfluff -text-unidecode==1.3 - # via python-slugify -tqdm==4.67.1 - # via sqlfluff -typing-extensions==4.15.0 - # via - # dbt-adapters - # dbt-common - # dbt-core - # mashumaro - # metricflow - # pydantic - # pydantic-core - # snowplow-tracker - # typing-inspection -typing-inspection==0.4.1 - # via pydantic -urllib3==2.7.0 - # via requests -zipp==3.23.0 - # via importlib-metadata diff --git a/warehouses/facility_ops/transform/requirements/requirements.in b/warehouses/facility_ops/transform/requirements/requirements.in deleted file mode 100644 index 3842d03a..00000000 --- a/warehouses/facility_ops/transform/requirements/requirements.in +++ /dev/null @@ -1,5 +0,0 @@ -dbt-adapters>=1.24.5,<2 -dbt-core>=1.12.0,<2 -dbt-trino>=1.10.2,<2 -# Temp pin to avoid requests raising exception. See https://github.com/psf/requests/issues/7219 -chardet==7.4.3 diff --git a/warehouses/facility_ops/transform/requirements/requirements.txt b/warehouses/facility_ops/transform/requirements/requirements.txt deleted file mode 100644 index 66234d1a..00000000 --- a/warehouses/facility_ops/transform/requirements/requirements.txt +++ /dev/null @@ -1,189 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile requirements/requirements.in -agate==1.9.1 - # via - # dbt-adapters - # dbt-common - # dbt-core -annotated-types==0.7.0 - # via pydantic -attrs==26.1.0 - # via - # jsonschema - # referencing -babel==2.18.0 - # via agate -certifi==2026.2.25 - # via requests -chardet==7.4.3 - # via -r requirements/requirements.in -charset-normalizer==3.4.7 - # via requests -click==8.3.2 - # via dbt-core -colorama==0.4.6 - # via dbt-common -daff==1.4.2 - # via dbt-core -dbt-adapters==1.24.5 - # via - # -r requirements/requirements.in - # dbt-core - # dbt-trino -dbt-common==1.38.0 - # via - # dbt-adapters - # dbt-core - # dbt-trino -dbt-core==1.12.0 - # via - # -r requirements/requirements.in - # dbt-trino -dbt-core-experimental-parser==2.0.0a5 - # via dbt-core -dbt-extractor==0.6.0 - # via dbt-core -dbt-protos==1.0.541 - # via - # dbt-adapters - # dbt-common - # dbt-core -dbt-trino==1.10.2 - # via -r requirements/requirements.in -deepdiff==8.6.2 - # via dbt-common -idna==3.15 - # via requests -importlib-metadata==8.9.0 - # via metricflow -isodate==0.7.2 - # via - # agate - # dbt-common -jinja2==3.1.6 - # via - # dbt-common - # dbt-core - # metricflow -jsonschema==4.26.0 - # via - # dbt-common - # dbt-core - # metricflow -jsonschema-specifications==2025.9.1 - # via jsonschema -leather==0.4.1 - # via agate -lz4==4.4.5 - # via trino -markupsafe==3.0.3 - # via jinja2 -mashumaro[msgpack]==3.14 - # via - # dbt-adapters - # dbt-common - # dbt-core -metricflow==0.211.0 - # via dbt-core -more-itertools==10.8.0 - # via metricflow -msgpack==1.2.1 - # via mashumaro -networkx==3.6.1 - # via dbt-core -orderly-set==5.5.0 - # via deepdiff -orjson==3.11.8 - # via trino -packaging==26.1 - # via dbt-core -parsedatetime==2.6 - # via agate -pathspec==0.12.1 - # via - # dbt-common - # dbt-core -protobuf==6.33.6 - # via - # dbt-adapters - # dbt-common - # dbt-core - # dbt-protos -pydantic==2.13.3 - # via - # dbt-core - # metricflow -pydantic-core==2.46.3 - # via pydantic -python-dateutil==2.9.0.post0 - # via - # dbt-common - # metricflow - # trino -python-dotenv==1.2.2 - # via dbt-core -python-slugify==8.0.4 - # via agate -pytimeparse==1.1.8 - # via agate -pytz==2026.1.post1 - # via - # dbt-adapters - # dbt-core - # trino -pyyaml==6.0.3 - # via - # dbt-core - # metricflow -rapidfuzz==3.14.5 - # via metricflow -referencing==0.37.0 - # via - # jsonschema - # jsonschema-specifications - # metricflow -requests==2.33.1 - # via - # dbt-common - # dbt-core - # snowplow-tracker - # trino -rpds-py==0.30.0 - # via - # jsonschema - # referencing -six==1.17.0 - # via python-dateutil -snowplow-tracker==1.1.0 - # via dbt-core -sqlglot==30.14.0 - # via metricflow -sqlparse==0.5.5 - # via dbt-core -tabulate==0.10.0 - # via metricflow -text-unidecode==1.3 - # via python-slugify -trino==0.337.0 - # via dbt-trino -typing-extensions==4.15.0 - # via - # dbt-adapters - # dbt-common - # dbt-core - # mashumaro - # metricflow - # pydantic - # pydantic-core - # snowplow-tracker - # typing-inspection -typing-inspection==0.4.2 - # via pydantic -tzlocal==5.3.1 - # via trino -urllib3==2.7.0 - # via requests -zipp==3.23.1 - # via importlib-metadata -zstandard==0.25.0 - # via trino From ab47bd5dbb7509c60b1058cf770a3cc967dc7a90 Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Thu, 13 Aug 2026 11:45:00 +0100 Subject: [PATCH 6/8] Correct precision of parse_utc_timestamp https://github.com/ISISNeutronMuon/analytics-data-platform/pull/429#discussion_r3767815254 It's unlikely we need to handle sub millisecond precision timestamps represented by strings, so this should be fine for now --- .../facility_ops/transform/macros/parse_utc_timestamp.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql b/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql index 35502409..10015821 100644 --- a/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql +++ b/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql @@ -9,7 +9,7 @@ cast( parse_datetime({{ adapter.quote(date_col) }} || ' ' || {{ adapter.quote(time_col) }}, '{{ date_format ~ ' ' ~ time_format }}') - as timestamp(6) + as timestamp(3) ), '{{ src_timezone }}' ) at time zone 'UTC' From 973f5b57a7c94eb90cadc1443de62ddf54699ac5 Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Thu, 13 Aug 2026 12:07:07 +0100 Subject: [PATCH 7/8] Apply timezone during parsing https://github.com/ISISNeutronMuon/analytics-data-platform/pull/429#discussion_r3767815273 Without the tz information during parse_datetime, the tz of the trino session is used. This means that during daylight savings there would be an hour difference applied to any data collected as UTC This does now mean there's a problem in the other direction (if the data was collected during daylight savings), but I guess anyone storing daylight savings times without timezones is unlikely to miss an hour here or there. --- .../facility_ops/transform/macros/parse_utc_timestamp.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql b/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql index 10015821..84bdfaef 100644 --- a/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql +++ b/elt-pipelines/facility_ops/transform/macros/parse_utc_timestamp.sql @@ -1,5 +1,5 @@ -- Parse separate date and time strings into a single UTC timestamp -{%- macro parse_utc_timestamp(date_col, date_format, time_col, time_format='HH:mm:ss', src_timezone='UTC') -%} +{%- macro parse_utc_timestamp(date_col, date_format, time_col, time_format='HH:mm:ss ZZZ', src_timezone='UTC') -%} {{ return(adapter.dispatch('parse_utc_timestamp')(date_col, date_format, time_col, time_format, src_timezone)) }} {% endmacro %} @@ -7,7 +7,7 @@ {%- macro trino__parse_utc_timestamp(date_col, date_format, time_col, time_format, src_timezone) -%} with_timezone( cast( - parse_datetime({{ adapter.quote(date_col) }} || ' ' || {{ adapter.quote(time_col) }}, + parse_datetime({{ adapter.quote(date_col) }} || ' ' || {{ adapter.quote(time_col) }} || ' {{ src_timezone }}', '{{ date_format ~ ' ' ~ time_format }}') as timestamp(3) ), From b2182c8e8f806c463642cc4c2a7fcedc2b70ab2d Mon Sep 17 00:00:00 2001 From: Will Taylor Date: Thu, 13 Aug 2026 12:10:32 +0100 Subject: [PATCH 8/8] Hardcode https for dbt remote configuration --- elt-pipelines/facility_ops/transform/profiles.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elt-pipelines/facility_ops/transform/profiles.yml b/elt-pipelines/facility_ops/transform/profiles.yml index d4dcc190..115f8367 100644 --- a/elt-pipelines/facility_ops/transform/profiles.yml +++ b/elt-pipelines/facility_ops/transform/profiles.yml @@ -21,9 +21,9 @@ remote: trino: type: trino method: ldap + http_scheme: https user: "{{ env_var('DBT_TRINO_USER') }}" password: "{{ env_var('DBT_TRINO_PASSWORD') }}" - http_scheme: "{{ env_var('DBT_TRINO_HTTP_SCHEME') }}" host: "{{ env_var('DBT_TRINO_HOST') }}" port: "{{ env_var('DBT_TRINO_PORT') | int }}" database: "{{ env_var('DBT_TRINO_CATALOG') }}"