Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug/missing-sla-policies #164

Merged
merged 19 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 17 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
2 changes: 1 addition & 1 deletion .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ steps:
commands: |
bash .buildkite/scripts/run_models.sh redshift

- label: ":bricks: Run Tests - Databricks"
- label: ":databricks: Run Tests - Databricks"
key: "run_dbt_databricks"
plugins:
- docker#v3.13.0:
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# dbt_zendesk v0.17.0

## Bug Fixes
- Fixed an issue in the `zendesk__sla_policies` model where tickets that were opened and solved outside of scheduled hours were not being reported.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we somewhat mention it in the logic changes within relevant models, but can we explicitly callout here that this issue was identified and addressed for requester_wait_time and agent_work_time slas.

- Resolved by adjusting the join logic in models `int_zendesk__agent_work_time_business_hours` and `int_zendesk__requester_wait_time_business_hours`. ([#164](https://github.com/fivetran/dbt_zendesk/pull/164), [#156](https://github.com/fivetran/dbt_zendesk/pull/156))
- Fixed as issue in the `zendesk__ticket_metrics` model where certain tickets had miscalculated metrics.
- Resolved by adjusting the join logic in models `int_zendesk__ticket_work_time_business`, `int_zendesk__ticket_first_resolution_time_business`, and `int_zendesk__ticket_full_resolution_time_business`. ([#167](https://github.com/fivetran/dbt_zendesk/pull/167))

## Under the hood
- Added integrity validations:
- Test to ensure `zendesk__sla_policies` and `zendesk__ticket_metrics` models produce consistent time results. ([#164](https://github.com/fivetran/dbt_zendesk/pull/164))
- Test to ensure `zendesk__ticket_metrics` contains all the tickets found in `stg_zendesk__ticket`.
- Reduced the weeks looking ahead from 208 to 52 for performance. ([#156](https://github.com/fivetran/dbt_zendesk/pull/156), [#167](https://github.com/fivetran/dbt_zendesk/pull/167))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love this suggestion, but want to add a bit more context here as to why we are making this change.

Suggested change
- Reduced the weeks looking ahead from 208 to 52 for performance. ([#156](https://github.com/fivetran/dbt_zendesk/pull/156), [#167](https://github.com/fivetran/dbt_zendesk/pull/167))
- Reduced the weeks looking ahead from 208 to 52 for to improve performance. This decision was made as it was found to be unnecessary to track ticket SLAs going out more than a year. ([#156](https://github.com/fivetran/dbt_zendesk/pull/156), [#167](https://github.com/fivetran/dbt_zendesk/pull/167))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this? G came up with it of course. 😄
Reduced the weeks looking ahead from 208 to 52 to improve performance, as tracking ticket SLAs beyond one year was unnecessary.


# dbt_zendesk v0.16.0
## 🚨 Minor Upgrade 🚨
Although this update is not a breaking change, it will likely impact the output of the `zendesk__sla_policies` and `zendesk__sla_metrics` models. [PR #154](https://github.com/fivetran/dbt_zendesk/pull/154) includes the following changes:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Include the following zendesk package version in your `packages.yml` file:
```yml
packages:
- package: fivetran/zendesk
version: [">=0.16.0", "<0.17.0"]
version: [">=0.17.0", "<0.18.0"]

```
> **Note**: Do not include the Zendesk Support source package. The Zendesk Support transform package already has a dependency on the source in its own `packages.yml` file.
Expand Down
2 changes: 1 addition & 1 deletion dbt_project.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'zendesk'
version: '0.16.0'
version: '0.17.0'


config-version: 2
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/dbt_project.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
config-version: 2

name: 'zendesk_integration_tests'
version: '0.16.0'
version: '0.17.0'

profile: 'integration_tests'

Expand Down
62 changes: 41 additions & 21 deletions integration_tests/tests/consistency/consistency_sla_policies.sql
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this test since the prior version did not detect when the dev schema had extra tickets not present in the prod schema and should cause a failure unless omitted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,63 @@
) }}

with prod as (
select
select
ticket_id,
metric,
sla_policy_name,
metric,
sla_applied_at,
sla_elapsed_time,
target,
in_business_hours,
sla_breach_at,
round(sla_elapsed_time, -1) as sla_elapsed_time, --round to the nearest tens
is_active_sla,
is_sla_breach
from {{ target.schema }}_zendesk_prod.zendesk__sla_policies
),

dev as (
select
ticket_id,
metric,
sla_policy_name,
metric,
sla_applied_at,
sla_elapsed_time,
target,
in_business_hours,
sla_breach_at,
round(sla_elapsed_time, -1) as sla_elapsed_time, --round to the nearest tens
is_active_sla,
is_sla_breach
from {{ target.schema }}_zendesk_dev.zendesk__sla_policies
),

prod_not_in_dev as (
-- rows from prod not found in dev
select * from prod
except distinct
select * from dev
),

dev_not_in_prod as (
-- rows from dev not found in prod
select * from dev
except distinct
select * from prod
),

final as (
select
prod.ticket_id,
prod.metric,
prod.sla_applied_at,
prod.sla_elapsed_time as prod_sla_elapsed_time,
dev.sla_elapsed_time as dev_sla_elapsed_time,
prod.is_sla_breach as prod_is_sla_breach,
dev.is_sla_breach as dev_is_sla_breach
from prod
full outer join dev
on dev.ticket_id = prod.ticket_id
and dev.metric = prod.metric
and dev.sla_applied_at = prod.sla_applied_at
select
*,
'from prod' as source
from prod_not_in_dev

union all -- union since we only care if rows are produced

select
*,
'from dev' as source
from dev_not_in_prod
)

select *
from final
where (abs(prod_sla_elapsed_time - dev_sla_elapsed_time) >= 5
or prod_is_sla_breach != dev_is_sla_breach)
{{ "and prod.ticket_id not in " ~ var('fivetran_consistency_sla_policies_exclusion_tickets',[]) ~ "" if var('fivetran_consistency_sla_policies_exclusion_tickets',[]) }}
{{ "where ticket_id not in " ~ var('fivetran_consistency_sla_policies_exclusion_tickets',[]) ~ "" if var('fivetran_consistency_sla_policies_exclusion_tickets',[]) }}
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,32 @@

with prod as (
select
1 as join_key,
ticket_id,
count(*) as total_slas
from {{ target.schema }}_zendesk_prod.zendesk__sla_policies
{{ "where ticket_id not in " ~ var('fivetran_consistency_sla_policy_count_exclusion_tickets',[]) ~ "" if var('fivetran_consistency_sla_policy_count_exclusion_tickets',[]) }}
group by 1
),

dev as (
select
1 as join_key,
ticket_id,
count(*) as total_slas
from {{ target.schema }}_zendesk_dev.zendesk__sla_policies
{{ "where ticket_id not in " ~ var('fivetran_consistency_sla_policy_count_exclusion_tickets',[]) ~ "" if var('fivetran_consistency_sla_policy_count_exclusion_tickets',[]) }}
group by 1
),

final as (
select
prod.join_key,
prod.ticket_id,
prod.total_slas as prod_sla_total,
dev.total_slas as dev_sla_total
from prod
full outer join dev
on dev.join_key = prod.join_key
on dev.ticket_id = prod.ticket_id
)

select *
from final
where prod_sla_total != dev_sla_total
where prod_sla_total != dev_sla_total
24 changes: 24 additions & 0 deletions integration_tests/tests/integrity/metrics_count_match.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

{{ config(
tags="fivetran_validations",
enabled=var('fivetran_validation_tests_enabled', false)
) }}

-- check that all the tickets are accounted for in the metrics
with stg_count as (
select
count(*) as stg_ticket_count
from {{ ref('stg_zendesk__ticket') }}
),

metric_count as (
select
count(*) as metric_ticket_count
from source
from {{ ref('zendesk__ticket_metrics') }}
)

select *
from stg_count
join metric_count
on stg_ticket_count != metric_ticket_count
36 changes: 36 additions & 0 deletions integration_tests/tests/integrity/sla_metrics_parity.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{{ config(
tags="fivetran_validations",
enabled=var('fivetran_validation_tests_enabled', false)
) }}

/*
This test is to ensure the sla_elapsed_time from zendesk__sla_policies matches the corresponding time in zendesk__ticket_metrics.
*/

with dev_slas as (
select *
from {{ target.schema }}_zendesk_dev.zendesk__sla_policies
where in_business_hours

), dev_metrics as (
select *
from {{ target.schema }}_zendesk_dev.zendesk__ticket_metrics

), dev_compare as (
select
dev_slas.ticket_id,
dev_slas.metric,
cast(dev_slas.sla_elapsed_time as {{ dbt.type_int() }}) as time_from_slas,
case when metric = 'agent_work_time' then dev_metrics.agent_work_time_in_business_minutes
when metric = 'requester_wait_time' then dev_metrics.requester_wait_time_in_business_minutes
when metric = 'first_reply_time' then dev_metrics.first_reply_time_business_minutes
end as time_from_metrics
from dev_slas
left join dev_metrics
on dev_metrics.ticket_id = dev_slas.ticket_id
)

select *
from dev_compare
where abs(time_from_slas - time_from_metrics) >= 5
{{ "and ticket_id not in " ~ var('fivetran_integrity_sla_metric_parity_exclusion_tickets',[]) ~ "" if var('fivetran_integrity_sla_metric_parity_exclusion_tickets',[]) }}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ with ticket_historical_status as (

), weeks as (

{{ dbt_utils.generate_series(208) }}
{{ dbt_utils.generate_series(52) }}

), weeks_cross_ticket_full_solved_time as (
-- because time is reported in minutes since the beginning of the week, we have to split up time spent on the ticket into calendar weeks
Expand Down Expand Up @@ -99,14 +99,14 @@ with ticket_historical_status as (
schedule.end_time_utc as schedule_end_time,
least(ticket_week_end_time, schedule.end_time_utc) - greatest(weekly_periods.ticket_week_start_time, schedule.start_time_utc) as scheduled_minutes
from weekly_periods
join schedule on
ticket_week_start_time <= schedule.end_time_utc
join schedule
on ticket_week_start_time <= schedule.end_time_utc
and ticket_week_end_time >= schedule.start_time_utc
and weekly_periods.schedule_id = schedule.schedule_id
-- this chooses the Daylight Savings Time or Standard Time version of the schedule
-- We have everything calculated within a week, so take us to the appropriate week first by adding the week_number * minutes-in-a-week to the minute-mark where we start and stop counting for the week
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_end_time', from_date_or_timestamp='start_week_date') }} as {{ dbt.type_timestamp() }}) > cast(schedule.valid_from as {{ dbt.type_timestamp() }})
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_start_time', from_date_or_timestamp='start_week_date') }} as {{ dbt.type_timestamp() }}) < cast(schedule.valid_until as {{ dbt.type_timestamp() }})
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_end_time', from_date_or_timestamp='start_week_date') }} as date) > cast(schedule.valid_from as date)
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_start_time', from_date_or_timestamp='start_week_date') }} as date) < cast(schedule.valid_until as date)

), business_minutes as (

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ with ticket_reply_times as (

), weeks as (

{{ dbt_utils.generate_series(208) }}
{{ dbt_utils.generate_series(52) }}

), weeks_cross_ticket_first_reply as (
-- because time is reported in minutes since the beginning of the week, we have to split up time spent on the ticket into calendar weeks
Expand Down Expand Up @@ -102,6 +102,6 @@ with ticket_reply_times as (
)

select ticket_id,
sum(scheduled_minutes) as first_reply_time_business_minutes
sum(scheduled_minutes) as first_reply_time_business_minutes
from intercepted_periods
group by 1
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ with ticket_resolution_times_calendar as (

), weeks as (

{{ dbt_utils.generate_series(208) }}
{{ dbt_utils.generate_series(52) }}

), weeks_cross_ticket_first_resolution_time as (
-- because time is reported in minutes since the beginning of the week, we have to split up time spent on the ticket into calendar weeks
Expand All @@ -72,22 +72,24 @@ with ticket_resolution_times_calendar as (

), intercepted_periods as (

select ticket_id,
week_number,
weekly_periods.schedule_id,
ticket_week_start_time,
ticket_week_end_time,
schedule.start_time_utc as schedule_start_time,
schedule.end_time_utc as schedule_end_time,
least(ticket_week_end_time, schedule.end_time_utc) - greatest(ticket_week_start_time, schedule.start_time_utc) as scheduled_minutes
select
ticket_id,
week_number,
weekly_periods.schedule_id,
ticket_week_start_time,
ticket_week_end_time,
schedule.start_time_utc as schedule_start_time,
schedule.end_time_utc as schedule_end_time,
least(ticket_week_end_time, schedule.end_time_utc) - greatest(ticket_week_start_time, schedule.start_time_utc) as scheduled_minutes
from weekly_periods
join schedule on ticket_week_start_time <= schedule.end_time_utc
join schedule
on ticket_week_start_time <= schedule.end_time_utc
and ticket_week_end_time >= schedule.start_time_utc
and weekly_periods.schedule_id = schedule.schedule_id
-- this chooses the Daylight Savings Time or Standard Time version of the schedule
-- We have everything calculated within a week, so take us to the appropriate week first by adding the week_number * minutes-in-a-week to the minute-mark where we start and stop counting for the week
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_end_time', from_date_or_timestamp='start_week_date') }} as {{ dbt.type_timestamp() }}) > cast(schedule.valid_from as {{ dbt.type_timestamp() }})
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_start_time', from_date_or_timestamp='start_week_date') }} as {{ dbt.type_timestamp() }}) < cast(schedule.valid_until as {{ dbt.type_timestamp() }})
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_end_time', from_date_or_timestamp='start_week_date') }} as date) > cast(schedule.valid_from as date)
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_start_time', from_date_or_timestamp='start_week_date') }} as date) < cast(schedule.valid_until as date)

)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ with ticket_resolution_times_calendar as (

), weeks as (

{{ dbt_utils.generate_series(208) }}
{{ dbt_utils.generate_series(52) }}

), weeks_cross_ticket_full_resolution_time as (
-- because time is reported in minutes since the beginning of the week, we have to split up time spent on the ticket into calendar weeks
Expand Down Expand Up @@ -85,8 +85,8 @@ with ticket_resolution_times_calendar as (
and weekly_periods.schedule_id = schedule.schedule_id
-- this chooses the Daylight Savings Time or Standard Time version of the schedule
-- We have everything calculated within a week, so take us to the appropriate week first by adding the week_number * minutes-in-a-week to the minute-mark where we start and stop counting for the week
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_end_time', from_date_or_timestamp='start_week_date') }} as {{ dbt.type_timestamp() }}) > cast(schedule.valid_from as {{ dbt.type_timestamp() }})
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_start_time', from_date_or_timestamp='start_week_date') }} as {{ dbt.type_timestamp() }}) < cast(schedule.valid_until as {{ dbt.type_timestamp() }})
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_end_time', from_date_or_timestamp='start_week_date') }} as date) > cast(schedule.valid_from as date)
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_start_time', from_date_or_timestamp='start_week_date') }} as date) < cast(schedule.valid_until as date)

)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ with agent_work_time_filtered_statuses as (

), weeks as (

{{ dbt_utils.generate_series(208) }}
{{ dbt_utils.generate_series(52) }}

), weeks_cross_ticket_full_solved_time as (
-- because time is reported in minutes since the beginning of the week, we have to split up time spent on the ticket into calendar weeks
Expand Down Expand Up @@ -120,17 +120,21 @@ with agent_work_time_filtered_statuses as (
weekly_period_agent_work_time.week_number,
weekly_period_agent_work_time.ticket_week_start_time_minute,
weekly_period_agent_work_time.ticket_week_end_time_minute,
schedule.start_time_utc as schedule_start_time,
coalesce(schedule.start_time_utc, 0) as schedule_start_time,
schedule.end_time_utc as schedule_end_time,
least(ticket_week_end_time_minute, schedule.end_time_utc) - greatest(weekly_period_agent_work_time.ticket_week_start_time_minute, schedule.start_time_utc) as scheduled_minutes
coalesce(
least(ticket_week_end_time_minute, schedule.end_time_utc)
- greatest(weekly_period_agent_work_time.ticket_week_start_time_minute, schedule.start_time_utc),
0) as scheduled_minutes
from weekly_period_agent_work_time
join schedule on ticket_week_start_time_minute <= schedule.end_time_utc
left join schedule
on ticket_week_start_time_minute <= schedule.end_time_utc
and ticket_week_end_time_minute >= schedule.start_time_utc
and weekly_period_agent_work_time.schedule_id = schedule.schedule_id
-- this chooses the Daylight Savings Time or Standard Time version of the schedule
-- We have everything calculated within a week, so take us to the appropriate week first by adding the week_number * minutes-in-a-week to the minute-mark where we start and stop counting for the week
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_end_time_minute', from_date_or_timestamp='start_week_date') }} as {{ dbt.type_timestamp() }}) > cast(schedule.valid_from as {{ dbt.type_timestamp() }})
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_start_time_minute', from_date_or_timestamp='start_week_date') }} as {{ dbt.type_timestamp() }}) < cast(schedule.valid_until as {{ dbt.type_timestamp() }})
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_end_time_minute', from_date_or_timestamp='start_week_date') }} as date) > cast(schedule.valid_from as date)
and cast( {{ dbt.dateadd(datepart='minute', interval='week_number * (7*24*60) + ticket_week_start_time_minute', from_date_or_timestamp='start_week_date') }} as date) < cast(schedule.valid_until as date)

), intercepted_periods_with_running_total as (

Expand All @@ -152,7 +156,7 @@ with agent_work_time_filtered_statuses as (
lag(target - running_total_scheduled_minutes) over
(partition by ticket_id, sla_applied_at order by valid_starting_at, week_number, schedule_end_time) as lag_check,
case when (target - running_total_scheduled_minutes) = 0 then true
when (target - running_total_scheduled_minutes) < 0
when (target - running_total_scheduled_minutes) < 0
and
(lag(target - running_total_scheduled_minutes) over
(partition by ticket_id, sla_applied_at order by valid_starting_at, week_number, schedule_end_time) > 0
Expand Down
Loading