Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions yardi_demo/macros/create_prospect_semantic_view.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{% macro create_prospect_semantic_view() %}
{#-
create_prospect_semantic_view
=============================
Issues the native Snowflake CREATE OR REPLACE SEMANTIC VIEW DDL over
fct_prospects. Invoked as a post_hook on the sem_prospect_funnel control
model so the object is (re)created on every dbt run, AFTER fct_prospects
is built. The DDL lives here so it is fully version-controlled in the dbt
repo rather than applied manually in Snowflake.

Target object: {database}.MARTS.SEM_PROSPECT_FUNNEL
Grain of base table: one row per prospect (guest card).
-#}

{% set fq_fact = ref('fct_prospects') %}
{% set view_name = (target.database ~ '.MARTS.SEM_PROSPECT_FUNNEL') %}

create or replace semantic view {{ view_name }}

tables (
prospects as {{ fq_fact }}
primary key (prospect_id)
with synonyms ('leads', 'guest cards', 'leasing funnel')
comment = 'Leasing-funnel guest cards, one row per prospect'
)

facts (
prospects.toured as (case when reached_tour then 1 else 0 end),
prospects.applied as (case when reached_application then 1 else 0 end),
prospects.approved as (case when reached_approval then 1 else 0 end),
prospects.leased as (case when reached_lease then 1 else 0 end),
prospects.days_to_apply_fact as days_to_apply,
prospects.days_to_movein_fact as days_to_movein,
prospects.desired_rent_fact as desired_rent
)

dimensions (
prospects.prospect_status as prospect_status
with synonyms ('funnel stage','stage')
comment = 'new/contacted/toured/applied/approved/denied/waitlist/leased',
prospects.lead_source as lead_source
with synonyms ('marketing channel','source'),
prospects.desired_bedrooms as desired_bedrooms,
prospects.leasing_agent as leasing_agent,
prospects.property_name as property_name,
prospects.property_region as property_region with synonyms ('region'),
prospects.property_fund as property_fund with synonyms ('fund'),
prospects.contact_date as contact_date,
prospects.contact_month as contact_month
)

metrics (
prospects.total_prospects AS COUNT(prospects.prospect_id)
comment = 'Count of prospects (leads)',
prospects.leased_prospects AS SUM(prospects.leased)
comment = 'Count of converted prospects',
prospects.tour_rate AS SUM(prospects.toured) / NULLIF(COUNT(prospects.prospect_id), 0)
comment = 'Share of prospects that toured',
prospects.application_rate AS SUM(prospects.applied) / NULLIF(COUNT(prospects.prospect_id), 0)
comment = 'Share of prospects that applied',
prospects.approval_rate AS SUM(prospects.approved) / NULLIF(SUM(prospects.applied), 0)
comment = 'Approved share of applicants',
prospects.lead_to_lease_conversion AS SUM(prospects.leased) / NULLIF(COUNT(prospects.prospect_id), 0)
comment = 'Overall lead-to-lease conversion',
prospects.avg_days_to_apply AS SUM(prospects.days_to_apply_fact) / NULLIF(SUM(prospects.applied), 0)
comment = 'Average days from contact to application',
prospects.avg_days_to_movein AS SUM(prospects.days_to_movein_fact) / NULLIF(SUM(prospects.leased), 0)
comment = 'Average days from application to move-in',
prospects.avg_desired_rent AS AVG(prospects.desired_rent_fact)
comment = 'Average desired monthly rent'
)

comment = 'Prospect leasing funnel semantic view (dbt-managed; built over fct_prospects)'

{% endmacro %}
23 changes: 23 additions & 0 deletions yardi_demo/models/semantic_views/_sem_prospect_funnel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: 2

models:
- name: sem_prospect_funnel
description: >
Control model for the native Snowflake SEMANTIC VIEW
YARDI.MARTS.SEM_PROSPECT_FUNNEL. This model itself is a tiny one-row
anchor table; its post_hook runs create_prospect_semantic_view(), which
issues CREATE OR REPLACE SEMANTIC VIEW over fct_prospects. The ref()
on fct_prospects guarantees dbt builds the fact first.

The semantic view exposes governed leasing-funnel metrics
(total_prospects, tour_rate, application_rate, approval_rate,
lead_to_lease_conversion, avg_days_to_apply, avg_days_to_movein,
avg_desired_rent) to Snowflake Cortex Analyst and any
SEMANTIC_VIEW(...) query.
columns:
- name: built_over
description: "Fully-qualified fact table the semantic view is built on"
- name: prospect_rows
description: "Row count of fct_prospects at build time (sanity check)"
tests:
- not_null
33 changes: 33 additions & 0 deletions yardi_demo/models/semantic_views/sem_prospect_funnel.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{{
config(
materialized = 'table',
schema = 'marts',
tags = ['semantic_view'],
post_hook = [ "{{ create_prospect_semantic_view() }}" ]
)
}}

{#-
sem_prospect_funnel (control model)
====================================
dbt does NOT natively materialize a Snowflake SEMANTIC VIEW, so this model
is the version-controlled control point for the native object:

- This SELECT builds a tiny one-row "anchor" table whose ONLY job is to
(a) create a dependency on fct_prospects via ref(), so dbt schedules
the semantic view AFTER the fact table is built, and
(b) give the post_hook a model to attach to.
- The post_hook calls the create_prospect_semantic_view() macro, which
runs CREATE OR REPLACE SEMANTIC VIEW ... over fct_prospects.

Net effect: `dbt run --select sem_prospect_funnel` (or a full run) creates
/ refreshes the native Snowflake SEMANTIC VIEW
YARDI.MARTS.SEM_PROSPECT_FUNNEL, with the DDL fully controlled from this
dbt repo. Cortex Analyst / SHOW SEMANTIC VIEWS then see the object.
-#}

select
'{{ ref('fct_prospects') }}' as built_over,
count(*) as prospect_rows,
current_timestamp() as _dbt_built_at
from {{ ref('fct_prospects') }}