Solutions — Homework 14
Lesson 14 · Data Engineering with Python
Task 1 — Most expensive team last week
DuckDB form
SELECT regexp_extract(tag, 'team=([^;]+)', 1) AS team,
SUM(elapsed_s) AS total_seconds
FROM _query_costs
WHERE ts >= now() - INTERVAL 7 DAY
AND tag LIKE 'team=%'
GROUP BY 1
ORDER BY total_seconds DESC
LIMIT 1;
Snowflake form
SELECT team, total_credits
FROM (
SELECT
REGEXP_SUBSTR(query_tag, 'team=([^;]+)', 1, 1, 'e', 1) AS team,
SUM(credits_used_cloud_services) AS total_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE start_time >= DATEADD(DAY, -7, CURRENT_TIMESTAMP)
AND query_tag LIKE 'team=%'
GROUP BY 1
)
ORDER BY total_credits DESC
LIMIT 1;
Common mistake: filtering by a literal date string such as
WHERE ts >= '2026-05-19'. The report must roll forward automatically;
use a relative date expression (now() - INTERVAL 7 DAY or
DATEADD(DAY, -7, CURRENT_TIMESTAMP)) so it works correctly every time
it runs, not just on the day you wrote it.
Task 2 — New data_scientist role
Add the following block inside the roles: mapping in governance/grants.yml:
data_scientist:
description: "ML feature engineering — read-only on facts + features"
grants:
- { table: fact_events, privileges: [SELECT] }
- { table: feature_store, privileges: [SELECT] }
The full roles: section after the change should look like this:
roles:
analytics_reader:
description: "Read-only on dim_ and fact_ tables"
grants:
- { table: dim_events, privileges: [SELECT] }
- { table: dim_actors, privileges: [SELECT] }
- { table: fact_events, privileges: [SELECT] }
pipeline_writer:
description: "Pipeline runner — writes to dim_/fact_ tables, no DDL"
grants:
- { table: dim_events, privileges: [SELECT, INSERT, UPDATE, DELETE] }
- { table: dim_actors, privileges: [SELECT, INSERT, UPDATE, DELETE] }
- { table: fact_events, privileges: [SELECT, INSERT, UPDATE, DELETE] }
pii_admin:
description: "Only role allowed to read PII columns or run GDPR deletes"
grants:
- { table: dim_actors, privileges: [SELECT, DELETE] }
- { table: events_raw, privileges: [SELECT, DELETE] }
data_scientist:
description: "ML feature engineering — read-only on facts + features"
grants:
- { table: fact_events, privileges: [SELECT] }
- { table: feature_store, privileges: [SELECT] }
Common mistake: granting
[SELECT, INSERT] “to be
safe”. Least privilege means exactly that — the least access needed to do
the job. A data scientist reading feature data has no reason to write to it, and
over-provisioning creates an audit finding the moment it appears in a review.
Task 3 — Declined deletion request
import json, datetime, os
entry = {
"ts": datetime.datetime.now(datetime.timezone.utc).isoformat(),
"action": "gdpr_decline",
"subject": "octocat-public",
"reason": "Subject is a fictional public-figure mascot; data falls under journalistic exemption per GDPR Art. 85",
"legal_basis": "Art. 85 + national press law",
"operator": "data-protection-officer",
}
os.makedirs("governance/gdpr", exist_ok=True)
with open("governance/gdpr/deletion_log.jsonl", "a") as f:
f.write(json.dumps(entry) + "\n")
print("Logged decline for:", entry["subject"])
Common mistake: not logging why the request was declined.
A decline is a legal decision, not a system action, and it must be defensible to a
supervisory authority on request. If your log entry says only
"action":
"gdpr_decline" without a reason and a legal_basis,
you cannot demonstrate that the decision was justified. Every decline entry in the
deletion log must include both fields.
Course complete. The sample-pipeline now covers the full production-grade
ETL lifecycle: extract → transform → quality → orchestration → SQL
→ dbt → DuckDB → Iceberg → warehouse adapters → observability
→ schema governance → cost & GDPR. Streaming is the natural next step.
See the sibling Cloud Infrastructure & DevOps course currently in
development on this platform.