Day 1 Homework and Wrap-Up - Modeling, Testing & Macros¶
This pulls together everything from 1.2-modeling-refresh.md, 1.3-testing.md, and 1.4-jinja-and-macros.md into one closing checklist, plus two new requirements: a documentation/test-coverage health check, and a final PR.
Work on your dpg/<your_name>/day1 branch. Keep committing as you go - don't save it all for one giant commit at the end; a real commit history makes the PR review (and your own reasoning trail) much easier to follow, especially for the duplicate-article_id bug you diagnose but deliberately don't fix in section 1.
1. Finish the outstanding exercise steps¶
From 1.2-modeling-refresh.md¶
- Trace the
dim_showslineage; write one sentence describing each model's job in the chain - Check how
stg_podcasts__showsanddim_showsare materialized indbt_project.yml; write one or two sentences on why staging is typically aviewwhile marts are typicallytable - Read
int_shows_clean_string_columns; add a comment at the top of the file documenting its purpose and any known string patterns it cleans - Point
dim_showsatint_shows_clean_string_columnsinstead ofstg_podcasts__showsdirectly, so those transformations actually carry through - Document
int_shows_clean_string_columnsin_int__models.ymlby hand, matching the style already used for other models in that file (a good real-world candidate for dbt Wizard - written manually for this training) - Review everything dependent on the
podcastssource and identify misplaced or repeated logic - specifically: does the staging model convert datetime columns from strings, doesstg_podcasts__listensjoin two sources together at the staging layer, and is a seed used anywhere in staging - Refactor: move the
episodes+listensjoin out of staging into a new intermediate model that computescompletion_rateper individual listen row - Build a mart on top of that intermediate model calculating average duration listened and average completion rate per episode
- Bucket the averaged completion rate into
no_listens/completed(>90%) /partial/dropped_before_content(<10%) - applied after the aggregation, not inside the intermediate model
From 1.3-testing.md¶
- Open
_ads__models.yml; list every column acrossstg_ads__campaigns,stg_ads__impressions, andstg_ads__spendwith nodata_testsat all - For each untested column, decide whether it needs a test and which one (
not_null,unique,accepted_values,relationships) - note any column you deliberately leave untested and why - Add the tests you decided on to
_ads__models.yml - Run the new tests selectively with
--selectbefore running the whole project - Open
stg_ads__campaigns.sql; note that it selects directly frommediapulse_raw.ads.campaignsrather thansource(), and write down what that costs in terms of what dbt can test and document about the raw table - Add a
uniquetest onarticle_idin_news__models.yml(alongside the existingnot_null); run it and confirm it fails - Query
mediapulse_raw.news.articlesdirectly (not the staged model) and investigate the duplicatearticle_ids, paying particular attention toupdated_at - Write down whether this looks like random dirty data or an explainable pattern (versioned articles), and how that distinction changes what the right fix actually is
- In prose only - no SQL yet - describe what you'd change in
stg_news__articles.sqlto make theuniquetest pass, and which row perarticle_idshould be treated as "current"
From 1.4-jinja-and-macros.md¶
- Find the safe-division
caseexpression already inline infct_podcasts_listens(completion_rate) - Write a
safe_divide(numerator, denominator, precision=4)macro that returns0on a zero denominator and otherwise the rounded division, with a default value forprecision - Replace the inline
caseinfct_podcasts_listenswith a call tosafe_divide; confirm the model builds and the column's values are unchanged - Add a
+schemaconfig for thestreamingandpodcastsmodel folders indbt_project.yml; run the affected models and check where they actually landed - is it the schema name you configured, or something else? - Open
mediapulse_base/macros/generate_schema_name.sqland identify its two branches (custom_schema_namenot set vs. set) - compare against dbt's defaultgenerate_schema_namemacro to see exactly what's being overridden - Extend the existing macro (don't write a new one) with a condition on the current target's name, so dev builds land in one flat schema (e.g.
dbt_yourname) while prod gets the fulltarget-schema_custom-schemanaming (e.g.prod_streaming) - Write a
normalize_score_columnsmacro that loops over a list of(score_col, response_col)pairs, emitting the sentinel-cleaningcasestatement for both columns in each pair - Write a
weighted_scoremacro that loops over a list of(score_col, response_col, rescale_factor)triples, emitting the weighted-score expression for each - Update
stg_news__articlesto callnormalize_score_columnsinstead of the ten longhandcasestatements - Update the scoring mart to call
weighted_scoreinstead of the five longhand weighting blocks - Run
dbt compileon both models before and after the change and diff the output - confirm the compiled SQL (andtotal_weighted_scoreper article) is unchanged
2. Project health check - documentation & test coverage¶
Don't aim for 100% coverage on either front - that's a vanity metric, not a quality bar. Instead, confirm the following are all true before moving on:
Documentation:
- Every model you touched today has a top-level
description- includingint_shows_clean_string_columns, the new completion-rate intermediate model, and the new episode-level mart - The new intermediate model and mart from the modeling refresh extension are documented in the appropriate
_models.yml, following the existing structure and style rather than inventing a new pattern -
safe_dividehas a description of what it does and whatprecisioncontrols -
normalize_score_columnsandweighted_scoreeach have descriptions - in particular, document the expected shape of the list each macro takes (pairs vs. triples), since that's exactly the kind of thing a future caller will get wrong without it written down - Any column whose meaning isn't obvious from its name has a
description- this especially applies to the four completion-rate buckets (no_listens/completed/partial/dropped_before_content) and the exact threshold that defines each one -
dbt docs generateruns clean with no missing-doc warnings on anything you touched today
Test coverage:
- The ads-domain tests from section 1 are in place and passing
- The
uniquetest onarticle_idis in place and still failing, as expected - this is intentional (the fix is deferred to a later exercise), not something you forgot to close. Call this out explicitly in your PR rather than leaving a reviewer to wonder if you missed it. -
fct_podcasts_listensstill passes its existing tests oncompletion_rateafter thesafe_divideswap - The new completion-rate intermediate model and mart have at least basic tests (e.g.
not_null/uniqueon whatever defines their grain) - the exercise didn't explicitly require this, so use judgment on what's actually worth testing here -
dbt buildruns clean - zero unexpected failures - across everything you touched today. Thearticle_iduniqueness test is the one deliberate exception; everything else should be green.
If a fresh pass over the models you touched today still shows gaps, use judgment: a gap on a business-critical column is worth fixing; a gap on a free-text or clearly cosmetic column usually isn't. Write one line in your PR description on any gap you deliberately chose to leave open and why.
3. Open a PR¶
Open a PR from your dpg/<your_name>/day1 branch. The description should cover, at minimum:
- Summary - one or two sentences on what today's work accomplished overall
- Modeling refactor - the
dim_showschain you traced, howint_shows_clean_string_columnsis now documented and actually wired intodim_shows, and what you moved out of thepodcastsstaging layer (and where it landed instead) - The new completion-rate mart - briefly describe the intermediate/mart split you built and how the four buckets are defined
- Testing coverage - what gaps you found and closed in the ads domain, and what you deliberately left untested and why
- The
article_idbug - this is the centerpiece of today's diagnostic work, so don't undersell it: explain what you found querying the raw table, why it looks like versioned articles rather than random dirty data, and what you'd change instg_news__articles.sqlto fix it. Be explicit that you have not implemented this fix - theuniquetest is left intentionally failing, and the actual fix belongs to a later exercise. - Macros - a short note on
safe_divide, thegenerate_schema_namedev/prod branch, andnormalize_score_columns/weighted_score- including how you confirmed the compiled SQL was unchanged after switching the news-scoring models over to the macros
Tag the PR for review as if a teammate picking this up tomorrow needs to understand, from the description alone, exactly what's fixed, what's only diagnosed, and what's still broken on purpose.