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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
## Feature Updates
- Adds DuckDB as a supported destination.

# dbt_intercom Unreleased

## Feature Updates
- Adds `time_to_first_response_sec` to `intercom__conversation_metrics`, computed with the same `dbt.datediff` source as `time_to_first_response_minutes` to preserve sub-minute response times.
- Adds `first_human_admin_response_at` to `int_intercom__conversation_part_aggregates` and passes the field through to `intercom__conversation_metrics`. Supports excluding bot or automated admin author IDs via the `intercom__first_response_excluded_admin_author_ids` project variable (defaults to an empty list).
- Updates `time_to_first_response_minutes` to cast the second-based `dbt.datediff` result to numeric before dividing by 60, preventing sub-60-second responses from truncating to zero on warehouses with integer division.

# dbt_intercom v1.7.0

[PR #84](https://github.com/fivetran/dbt_intercom/pull/84) and [PR #85](https://github.com/fivetran/dbt_intercom/pull/85) includes the following updates:
Expand Down
1 change: 1 addition & 0 deletions dbt_project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ vars:
intercom__contact_history_pass_through_columns: []
intercom__conversation_history_pass_through_columns: []
intercom__article_history_pass_through_columns: []
intercom__first_response_excluded_admin_author_ids: []
clean-targets:
- target
- dbt_modules
Expand Down
7 changes: 7 additions & 0 deletions models/intercom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,15 @@ models:
description: The time difference (not factoring in business hours) between the conversation creation and the first_assignment_at.
- name: first_admin_response_at
description: The time when the conversation part author of type admin first commented in a conversation.
- name: time_to_first_response_sec
description: The time difference in seconds (not factoring in business hours) between the conversation creation and the first_admin_response_at.
- name: time_to_first_response_minutes
description: The time difference (not factoring in business hours) between the conversation creation and the first_admin_response_at.
- name: first_human_admin_response_at
description: >
The time when the first admin with a non-null message body commented in the conversation, optionally excluding
admin author IDs listed in the `intercom__first_response_excluded_admin_author_ids` variable. When no IDs are
excluded, this matches `first_admin_response_at`.
- name: first_admin_close_at
description: The time of the first conversation part where part_type was 'close' by an admin indicating the conversation was closed.
- name: first_close_at
Expand Down
4 changes: 3 additions & 1 deletion models/intercom__conversation_metrics.sql
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ final as (
conversation_part_aggregates.first_assignment_at,
round(cast(({{ dbt.datediff("conversation_enhanced.conversation_created_at", "conversation_part_aggregates.first_assignment_at", 'second') }}/60) as numeric), 2) as time_to_first_assignment_minutes,
conversation_part_aggregates.first_admin_response_at,
round(cast(({{ dbt.datediff("conversation_enhanced.conversation_created_at", "conversation_part_aggregates.first_admin_response_at", 'second') }}/60) as numeric), 2) as time_to_first_response_minutes,
{{ dbt.datediff("conversation_enhanced.conversation_created_at", "conversation_part_aggregates.first_admin_response_at", 'second') }} as time_to_first_response_sec,
round(cast({{ dbt.datediff("conversation_enhanced.conversation_created_at", "conversation_part_aggregates.first_admin_response_at", 'second') }} as numeric) / 60, 2) as time_to_first_response_minutes,
conversation_part_aggregates.first_human_admin_response_at,
round(cast(({{ dbt.datediff("conversation_part_aggregates.first_contact_reply_at", "conversation_enhanced.first_admin_close_at", 'second') }} /60) as numeric), 2) as time_to_admin_first_close_minutes,
round(cast(({{ dbt.datediff("conversation_part_aggregates.first_contact_reply_at", "conversation_enhanced.first_close_at", 'second') }} /60) as numeric), 2) as time_to_first_close_minutes,
conversation_part_aggregates.first_reopen_at,
Expand Down
16 changes: 16 additions & 0 deletions models/intermediate/int_intercom__conversation_part_aggregates.sql
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ final as (
min(case when latest_conversation_part.part_type = 'comment' and latest_conversation_part.author_type in ('lead','user') then latest_conversation_part.created_at else null end) as first_contact_reply_at,
min(case when latest_conversation_part.part_type like '%assignment%' then latest_conversation_part.created_at else null end) as first_assignment_at,
min(case when latest_conversation_part.part_type in ('comment','assignment') and latest_conversation_part.author_type = 'admin' and latest_conversation_part.body is not null then latest_conversation_part.created_at else null end) as first_admin_response_at,
min(
case
when latest_conversation_part.part_type in ('comment','assignment')
and latest_conversation_part.author_type = 'admin'
and latest_conversation_part.body is not null
{% if var('intercom__first_response_excluded_admin_author_ids', []) | length > 0 %}
and latest_conversation_part.author_id not in (
{%- for admin_author_id in var('intercom__first_response_excluded_admin_author_ids', []) -%}
{{ admin_author_id }}{%- if not loop.last -%},{%- endif -%}
{%- endfor -%}
)
{% endif %}
then latest_conversation_part.created_at
else null
end
) as first_human_admin_response_at,
min(case when latest_conversation_part.part_type = 'open' then latest_conversation_part.created_at else null end) as first_reopen_at,
max(case when latest_conversation_part.part_type like '%assignment%' then latest_conversation_part.created_at else null end) as last_assignment_at,
max(case when latest_conversation_part.part_type = 'comment' and latest_conversation_part.author_type in ('lead','user') then latest_conversation_part.created_at else null end) as last_contact_reply_at,
Expand Down