@ken-e/dataform-youtube 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/README.md +4 -0
  3. package/eslint.config.js +11 -0
  4. package/includes/column_descriptions.js +95 -0
  5. package/includes/constants.js +5 -0
  6. package/includes/definitions/sources/stg_ytc_annotation.js +91 -0
  7. package/includes/definitions/sources/stg_ytc_basic.js +109 -0
  8. package/includes/definitions/sources/stg_ytc_cards.js +89 -0
  9. package/includes/definitions/sources/stg_ytc_combined.js +97 -0
  10. package/includes/definitions/sources/stg_ytc_demographics.js +84 -0
  11. package/includes/definitions/sources/stg_ytc_device_os.js +90 -0
  12. package/includes/definitions/sources/stg_ytc_end_screens.js +69 -0
  13. package/includes/definitions/sources/stg_ytc_list_basic.js +91 -0
  14. package/includes/definitions/sources/stg_ytc_list_combined.js +92 -0
  15. package/includes/definitions/sources/stg_ytc_list_device_os.js +90 -0
  16. package/includes/definitions/sources/stg_ytc_list_playback.js +89 -0
  17. package/includes/definitions/sources/stg_ytc_list_province.js +91 -0
  18. package/includes/definitions/sources/stg_ytc_list_traffic_source.js +89 -0
  19. package/includes/definitions/sources/stg_ytc_lu_annotation_type.js +37 -0
  20. package/includes/definitions/sources/stg_ytc_lu_card_type.js +35 -0
  21. package/includes/definitions/sources/stg_ytc_lu_device_types.js +33 -0
  22. package/includes/definitions/sources/stg_ytc_lu_end_screen_element_type.js +36 -0
  23. package/includes/definitions/sources/stg_ytc_lu_operating_systems.js +57 -0
  24. package/includes/definitions/sources/stg_ytc_lu_playback_location.js +34 -0
  25. package/includes/definitions/sources/stg_ytc_lu_sharing_services.js +94 -0
  26. package/includes/definitions/sources/stg_ytc_lu_traffic_sources.js +50 -0
  27. package/includes/definitions/sources/stg_ytc_playback.js +90 -0
  28. package/includes/definitions/sources/stg_ytc_province.js +103 -0
  29. package/includes/definitions/sources/stg_ytc_share_platform.js +82 -0
  30. package/includes/definitions/sources/stg_ytc_subtitles.js +89 -0
  31. package/includes/definitions/sources/stg_ytc_traffic_source.js +107 -0
  32. package/includes/definitions/ytc_annotation.js +65 -0
  33. package/includes/definitions/ytc_basic.js +74 -0
  34. package/includes/definitions/ytc_cards.js +66 -0
  35. package/includes/definitions/ytc_combined.js +113 -0
  36. package/includes/definitions/ytc_demographics.js +69 -0
  37. package/includes/definitions/ytc_demographics_views.js +80 -0
  38. package/includes/definitions/ytc_device_os.js +80 -0
  39. package/includes/definitions/ytc_end_screens.js +65 -0
  40. package/includes/definitions/ytc_list_basic.js +84 -0
  41. package/includes/definitions/ytc_list_combined.js +97 -0
  42. package/includes/definitions/ytc_list_device_os.js +90 -0
  43. package/includes/definitions/ytc_list_playback.js +86 -0
  44. package/includes/definitions/ytc_list_province.js +83 -0
  45. package/includes/definitions/ytc_list_traffic_source.js +91 -0
  46. package/includes/definitions/ytc_playback.js +73 -0
  47. package/includes/definitions/ytc_province.js +71 -0
  48. package/includes/definitions/ytc_share_platform.js +76 -0
  49. package/includes/definitions/ytc_subtitles.js +69 -0
  50. package/includes/definitions/ytc_traffic_source.js +73 -0
  51. package/includes/helpers.js +8 -0
  52. package/includes/project_variables.js +15 -0
  53. package/index.js +312 -0
  54. package/package.json +30 -0
  55. package/prettier.config.js +14 -0
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Copyright (C) 2025 by KEN-E, LLC
3
+ */
4
+
5
+ module.exports = (config) => {
6
+ // eslint-disable-next-line no-undef
7
+ return publish("stg_ytc_traffic_source", {
8
+ type: "incremental",
9
+ database: config.target.database,
10
+ schema: config.datasetStaging,
11
+ protected: config.protected,
12
+ tags: ["youtube", "source", "staging", "daily"],
13
+ bigquery: {
14
+ partitionBy: "interaction_date",
15
+ clusterBy: ["video_id", "traffic_source_type"],
16
+ },
17
+ assertions: {
18
+ // make sure rows have unique dimensions
19
+ uniqueKeys: [
20
+ [
21
+ "interaction_date",
22
+ "channel_id",
23
+ "video_id",
24
+ "live_or_on_demand",
25
+ "subscribed_status",
26
+ "country_code",
27
+ "traffic_source_type",
28
+ "traffic_source_detail",
29
+ ],
30
+ ],
31
+ // make sure source partition and data dates match
32
+ rowConditions: ["interaction_date = source_partition_date"],
33
+ },
34
+ description: "Staging table for YouTube Channel Traffic Source data",
35
+ assertions: {
36
+ // make sure rows have unique dimensions
37
+ uniqueKeys: [
38
+ [
39
+ "interaction_date",
40
+ "channel_id",
41
+ "video_id",
42
+ "live_or_on_demand",
43
+ "subscribed_status",
44
+ "country_code",
45
+ "traffic_source_type",
46
+ "traffic_source_detail",
47
+ ],
48
+ ],
49
+ // make sure source partition and data dates match
50
+ rowConditions: ["interaction_date = source_partition_date"],
51
+ },
52
+ description: "Staging table for YouTube Channel Traffic Source data",
53
+ })
54
+ .preOps(
55
+ (ctx) => `
56
+ declare source_date_checkpoint default (
57
+ select date("${config.startDate}")
58
+ );
59
+
60
+ --Set the incremental update checkpoint based on current max partition value minus lookback.
61
+ set source_date_checkpoint = (
62
+ ${ctx.when(
63
+ ctx.incremental(),
64
+ `select
65
+ least(
66
+ (select date_sub(current_date(), interval ${config.daysBack} day)),
67
+ (select date_sub(max(source_partition_date), interval ${config.daysBack} day) from ${ctx.self()})
68
+ )`,
69
+ `select date("${config.startDate}")`,
70
+ )}
71
+ );
72
+
73
+ ${ctx.when(
74
+ ctx.incremental(),
75
+ `delete ${ctx.self()} where source_partition_date > source_date_checkpoint`,
76
+ )}
77
+ `,
78
+ )
79
+ .query((ctx) =>
80
+ config.sources
81
+ .map((t) => {
82
+ return `
83
+ select
84
+ _PARTITIONDATE as source_partition_date,
85
+ parse_date('%Y%m%d', date) as interaction_date,
86
+ "${t.schema}" as site_nm,
87
+ current_timestamp() as updated_at,
88
+ channel_id,
89
+ video_id,
90
+ live_or_on_demand,
91
+ subscribed_status,
92
+ country_code,
93
+ traffic_source_type,
94
+ traffic_source_detail,
95
+ views,
96
+ watch_time_minutes,
97
+ average_view_duration_seconds,
98
+ average_view_duration_percentage,
99
+ red_views,
100
+ red_watch_time_minutes
101
+ from ${ctx.ref(t.database, t.schema, "p_channel_traffic_source_a2_" + t.suffix)}
102
+ where _PARTITIONDATE > source_date_checkpoint
103
+ `;
104
+ })
105
+ .join(" union all "),
106
+ );
107
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Copyright (C) 2025 by KEN-E, LLC
3
+ */
4
+
5
+ const column_descriptions = require("../column_descriptions");
6
+
7
+ module.exports = (config) => {
8
+ // eslint-disable-next-line no-undef
9
+ return publish("ytc_annotation", {
10
+ type: "incremental",
11
+ schema: config.datasetIntermediate,
12
+ tags: ["youtube", "output", "daily"],
13
+ bigquery: {
14
+ partitionBy: "interaction_date",
15
+ clusterBy: ["video_id", "annotation_type"],
16
+ },
17
+ columns: column_descriptions.column_descriptions,
18
+ description: "YT Channel Annotation Report Table - Intermediate",
19
+ })
20
+ .preOps(
21
+ (ctx) => `
22
+ declare interaction_date_checkpoint default (
23
+ select date("${config.startDate}")
24
+ );
25
+
26
+ --Set the incremental update checkpoint based on current max partition value minus lookback.
27
+ set interaction_date_checkpoint = (
28
+ ${ctx.when(
29
+ ctx.incremental(),
30
+ `select
31
+ least(
32
+ (select date_sub(current_date(), interval ${config.daysBack} day)),
33
+ (select date_sub(max(interaction_date), interval ${config.daysBack} day) from ${ctx.self()})
34
+ )`,
35
+ `select date("${config.startDate}")`,
36
+ )}
37
+ );
38
+
39
+ ${ctx.when(
40
+ ctx.incremental(),
41
+ `delete ${ctx.self()} where interaction_date > interaction_date_checkpoint`,
42
+ )}
43
+ `,
44
+ )
45
+ .query(
46
+ (ctx) => `
47
+
48
+ select
49
+ stg.*,
50
+ annot.annotation_type_name,
51
+ ifnull(titles.video_title, stg.video_id) as video_title
52
+ from ${ctx.ref("stg_ytc_annotation")} as stg
53
+ left join ${ctx.ref("stg_ytc_lu_annotation_type")} as annot
54
+ using (annotation_type)
55
+ left join ${ctx.ref(
56
+ config.titlesProject,
57
+ config.titlesDataset,
58
+ config.titlesTable,
59
+ )} as titles
60
+ using (video_id)
61
+ where interaction_date > interaction_date_checkpoint
62
+
63
+ `,
64
+ );
65
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Copyright (C) 2025 by KEN-E, LLC
3
+ */
4
+
5
+ const column_descriptions = require("../column_descriptions");
6
+
7
+ module.exports = (config) => {
8
+ // eslint-disable-next-line no-undef
9
+ return publish("ytc_basic", {
10
+ type: "incremental",
11
+ schema: config.datasetIntermediate,
12
+ tags: ["youtube", "output", "daily"],
13
+ bigquery: {
14
+ partitionBy: "interaction_date",
15
+ clusterBy: ["video_id"],
16
+ },
17
+ columns: column_descriptions.column_descriptions,
18
+ description: "YT Channel Basic Report Table - Intermediate",
19
+ })
20
+ .preOps(
21
+ (ctx) => `
22
+ -- Calculate date checkpoint for incremental updates.
23
+
24
+ declare interaction_date_checkpoint default (
25
+ select date("${config.startDate}")
26
+ );
27
+
28
+ --Set the incremental update checkpoint based on current max partition value minus lookback.
29
+ set interaction_date_checkpoint = (
30
+ ${ctx.when(
31
+ ctx.incremental(),
32
+ `select
33
+ least(
34
+ (select date_sub(current_date(), interval ${config.daysBack} day)),
35
+ (select date_sub(max(interaction_date), interval ${config.daysBack} day) from ${ctx.self()})
36
+ )`,
37
+ `select date("${config.startDate}")`,
38
+ )}
39
+ );
40
+
41
+ ${ctx.when(
42
+ ctx.incremental(),
43
+ `delete ${ctx.self()} where interaction_date > interaction_date_checkpoint`,
44
+ )}
45
+ `,
46
+ )
47
+ .query(
48
+ (ctx) => `
49
+
50
+ -- determine max possible viewing seconds per row so that percent viewed can be calculated downstream
51
+ with int_ex_titles as (
52
+ select
53
+ * except (source_partition_date),
54
+ views * safe_divide(average_view_duration_seconds, average_view_duration_percentage) as row_max_duration_seconds,
55
+ subscribers_gained - subscribers_lost as subscribers_net,
56
+ from ${ctx.ref("stg_ytc_basic")}
57
+ where interaction_date > interaction_date_checkpoint
58
+ )
59
+
60
+ select
61
+ int_ex_titles.*,
62
+ ifnull(titles.video_title, int_ex_titles.video_id) as video_title
63
+ from int_ex_titles
64
+ left join ${ctx.ref(
65
+ config.titlesProject,
66
+ config.titlesDataset,
67
+ config.titlesTable,
68
+ )} as titles
69
+ using
70
+ (video_id)
71
+
72
+ `,
73
+ );
74
+ };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Copyright (C) 2025 by KEN-E, LLC
3
+ */
4
+
5
+ const column_descriptions = require("../column_descriptions");
6
+
7
+ module.exports = (config) => {
8
+ // eslint-disable-next-line no-undef
9
+ return publish("ytc_cards", {
10
+ type: "incremental",
11
+ schema: config.datasetIntermediate,
12
+ tags: ["youtube", "output", "daily"],
13
+ bigquery: {
14
+ partitionBy: "interaction_date",
15
+ clusterBy: ["video_id", "card_type"],
16
+ },
17
+ columns: column_descriptions.column_descriptions,
18
+ description: "YT Channel Cards Report Table - Intermediate",
19
+ })
20
+ .preOps(
21
+ (ctx) => `
22
+ declare interaction_date_checkpoint default (
23
+ select date("${config.startDate}")
24
+ );
25
+
26
+ --Set the incremental update checkpoint based on current max partition value minus lookback.
27
+ set interaction_date_checkpoint = (
28
+ ${ctx.when(
29
+ ctx.incremental(),
30
+ `select
31
+ least(
32
+ (select date_sub(current_date(), interval ${config.daysBack} day)),
33
+ (select date_sub(max(interaction_date), interval ${config.daysBack} day) from ${ctx.self()})
34
+ )`,
35
+ `select date("${config.startDate}")`,
36
+ )}
37
+ );
38
+
39
+ ${ctx.when(
40
+ ctx.incremental(),
41
+ `delete ${ctx.self()} where interaction_date > interaction_date_checkpoint`,
42
+ )}
43
+ `,
44
+ )
45
+ .query(
46
+ (ctx) => `
47
+
48
+ select
49
+ stg.* except (source_partition_date),
50
+ ct.card_type_name,
51
+ ifnull(titles.video_title, stg.video_id) as video_title
52
+ from ${ctx.ref("stg_ytc_cards")} as stg
53
+ left join ${ctx.ref("stg_ytc_lu_card_type")} as ct
54
+ using (card_type)
55
+ left join ${ctx.ref(
56
+ config.titlesProject,
57
+ config.titlesDataset,
58
+ config.titlesTable,
59
+ )} as titles
60
+ using (video_id)
61
+ where interaction_date > interaction_date_checkpoint
62
+
63
+
64
+ `,
65
+ );
66
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Copyright (C) 2025 by KEN-E, LLC
3
+ */
4
+
5
+ const column_descriptions = require("../column_descriptions");
6
+
7
+ module.exports = (config) => {
8
+ // eslint-disable-next-line no-undef
9
+ return publish("ytc_combined", {
10
+ type: "incremental",
11
+ schema: config.datasetIntermediate,
12
+ tags: ["youtube", "output", "daily"],
13
+ bigquery: {
14
+ partitionBy: "interaction_date",
15
+ clusterBy: ["video_id"],
16
+ },
17
+ columns: column_descriptions.column_descriptions,
18
+ description: "YT Channel Combined Report Table - Intermediate",
19
+ })
20
+ .preOps(
21
+ (ctx) => `
22
+ -- Calculate date checkpoint for incremental updates.
23
+
24
+
25
+ declare interaction_date_checkpoint default (
26
+ select date("${config.startDate}")
27
+ );
28
+
29
+ --Set the incremental update checkpoint based on current max partition value minus lookback.
30
+ set interaction_date_checkpoint = (
31
+ ${ctx.when(
32
+ ctx.incremental(),
33
+ `select
34
+ least(
35
+ (select date_sub(current_date(), interval ${config.daysBack} day)),
36
+ (select date_sub(max(interaction_date), interval ${config.daysBack} day) from ${ctx.self()})
37
+ )`,
38
+ `select date("${config.startDate}")`,
39
+ )}
40
+ );
41
+
42
+ ${ctx.when(
43
+ ctx.incremental(),
44
+ `delete ${ctx.self()} where interaction_date > interaction_date_checkpoint`,
45
+ )}
46
+ `,
47
+ )
48
+ .query(
49
+ (ctx) => `
50
+
51
+ with int_ex_titles as (
52
+ select
53
+ * except (source_partition_date),
54
+ views * safe_divide(average_view_duration_seconds, average_view_duration_percentage) as row_max_duration_seconds,
55
+ from ${ctx.ref("stg_ytc_combined")}
56
+ where interaction_date > interaction_date_checkpoint
57
+ ),
58
+
59
+ int_with_titles as (
60
+ select
61
+ int_ex_titles.*,
62
+ ifnull(titles.video_title, int_ex_titles.video_id) as video_title
63
+ from int_ex_titles
64
+ left join ${ctx.ref(
65
+ config.titlesProject,
66
+ config.titlesDataset,
67
+ config.titlesTable,
68
+ )} as titles
69
+ using
70
+ (video_id)
71
+ ),
72
+
73
+ int_with_playback_location as (
74
+ select
75
+ int_with_titles.*,
76
+ playback_location.playback_location_name
77
+ from int_with_titles
78
+ left join ${ctx.ref("stg_ytc_lu_playback_location")} as playback_location
79
+ using
80
+ (playback_location_type)
81
+ ),
82
+
83
+ int_with_device_type as (
84
+ select
85
+ int_with_playback_location.*,
86
+ device_types.device_name
87
+ from int_with_playback_location
88
+ left join ${ctx.ref("stg_ytc_lu_device_types")} as device_types
89
+ using
90
+ (device_type)
91
+ ),
92
+
93
+ int_with_traffic_source as (
94
+ select
95
+ int_with_device_type.*,
96
+ traffic_sources.traffic_source_name
97
+ from int_with_device_type
98
+ left join ${ctx.ref("stg_ytc_lu_traffic_sources")} as traffic_sources
99
+ using
100
+ (traffic_source_type)
101
+ )
102
+
103
+ select
104
+ int_with_traffic_source.*,
105
+ operating_systems.operating_system_name
106
+ from int_with_traffic_source
107
+ left join ${ctx.ref("stg_ytc_lu_operating_systems")} as operating_systems
108
+ using
109
+ (operating_system)
110
+
111
+ `,
112
+ );
113
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Copyright (C) 2025 by KEN-E, LLC
3
+ */
4
+
5
+ const column_descriptions = require("../column_descriptions");
6
+
7
+ module.exports = (config) => {
8
+ // eslint-disable-next-line no-undef
9
+ return publish("ytc_demographics", {
10
+ type: "incremental",
11
+ schema: config.datasetIntermediate,
12
+ tags: ["youtube", "output", "daily"],
13
+ bigquery: {
14
+ partitionBy: "interaction_date",
15
+ clusterBy: ["video_id", "age_group", "gender"],
16
+ },
17
+ columns: column_descriptions.column_descriptions,
18
+ description: "YT Channel Demographics Report Table - Intermediate",
19
+ })
20
+ .preOps(
21
+ (ctx) => `
22
+ declare interaction_date_checkpoint default (
23
+ select date("${config.startDate}")
24
+ );
25
+
26
+ --Set the incremental update checkpoint based on current max partition value minus lookback.
27
+ set interaction_date_checkpoint = (
28
+ ${ctx.when(
29
+ ctx.incremental(),
30
+ `select
31
+ least(
32
+ (select date_sub(current_date(), interval ${config.daysBack} day)),
33
+ (select date_sub(max(interaction_date), interval ${config.daysBack} day) from ${ctx.self()})
34
+ )`,
35
+ `select date("${config.startDate}")`,
36
+ )}
37
+ );
38
+
39
+ ${ctx.when(
40
+ ctx.incremental(),
41
+ `delete ${ctx.self()} where interaction_date > interaction_date_checkpoint`,
42
+ )}
43
+ `,
44
+ )
45
+ .query(
46
+ (ctx) => `
47
+
48
+ -- determine max possible viewing seconds per row so that percent viewed can be calculated downstream
49
+ with int_ex_titles as (
50
+ select
51
+ * except (source_partition_date)
52
+ from ${ctx.ref("stg_ytc_demographics")}
53
+ where interaction_date > interaction_date_checkpoint
54
+ )
55
+
56
+ select
57
+ int_ex_titles.*,
58
+ ifnull(titles.video_title, int_ex_titles.video_id) as video_title
59
+ from int_ex_titles
60
+ left join ${ctx.ref(
61
+ config.titlesProject,
62
+ config.titlesDataset,
63
+ config.titlesTable,
64
+ )} as titles
65
+ using (video_id)
66
+
67
+ `,
68
+ );
69
+ };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Copyright (C) 2025 by KEN-E, LLC
3
+ */
4
+
5
+ const column_descriptions = require("../column_descriptions");
6
+
7
+ module.exports = (config) => {
8
+ // eslint-disable-next-line no-undef
9
+ return publish("ytc_demographics_views", {
10
+ type: "incremental",
11
+ schema: config.datasetIntermediate,
12
+ tags: ["youtube", "output", "daily"],
13
+ bigquery: {
14
+ partitionBy: "interaction_date",
15
+ clusterBy: ["video_id", "age_group", "gender"],
16
+ },
17
+ columns: column_descriptions.column_descriptions,
18
+ description: "YT Channel Demographics Report Table - Intermediate",
19
+ })
20
+ .preOps(
21
+ (ctx) => `
22
+ declare interaction_date_checkpoint default (
23
+ select date("${config.startDate}")
24
+ );
25
+
26
+ --Set the incremental update checkpoint based on current max partition value minus lookback.
27
+ set interaction_date_checkpoint = (
28
+ ${ctx.when(
29
+ ctx.incremental(),
30
+ `select
31
+ least(
32
+ (select date_sub(current_date(), interval ${config.daysBack} day)),
33
+ (select date_sub(max(interaction_date), interval ${config.daysBack} day) from ${ctx.self()})
34
+ )`,
35
+ `select date("${config.startDate}")`,
36
+ )}
37
+ );
38
+
39
+ ${ctx.when(
40
+ ctx.incremental(),
41
+ `delete ${ctx.self()} where interaction_date > interaction_date_checkpoint`,
42
+ )}
43
+ `,
44
+ )
45
+ .query(
46
+ (ctx) => `
47
+
48
+ -- determine max possible viewing seconds per row so that percent viewed can be calculated downstream
49
+ with int_ex_titles as (
50
+ select
51
+ stg.* except (source_partition_date),
52
+ ytc_basic.views * stg.views_percentage/100 as views_with_demographics,
53
+ ytc_basic.views,
54
+ from ${ctx.ref("stg_ytc_demographics")} as stg
55
+ left join ${ctx.ref("ytc_basic")} as ytc_basic
56
+ using (
57
+ interaction_date,
58
+ channel_id,
59
+ video_id,
60
+ live_or_on_demand,
61
+ subscribed_status,
62
+ country_code
63
+ )
64
+ where interaction_date > interaction_date_checkpoint
65
+ )
66
+
67
+ select
68
+ int_ex_titles.*,
69
+ ifnull(titles.video_title, int_ex_titles.video_id) as video_title
70
+ from int_ex_titles
71
+ left join ${ctx.ref(
72
+ config.titlesProject,
73
+ config.titlesDataset,
74
+ config.titlesTable,
75
+ )} as titles
76
+ using (video_id)
77
+
78
+ `,
79
+ );
80
+ };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * This model has been prepared as part of the MARKETING PROPELLER project.
3
+ * Copyright (C) 2024 by DiveTeam, LLC
4
+ *
5
+ * Marketing Propeller models are not officially maintained, and are distributed on an
6
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7
+ */
8
+
9
+ const column_descriptions = require("../column_descriptions");
10
+
11
+ module.exports = (config) => {
12
+ // eslint-disable-next-line no-undef
13
+ return publish("ytc_device_os", {
14
+ type: "incremental",
15
+ schema: config.datasetIntermediate,
16
+ tags: ["youtube", "output", "daily"],
17
+ bigquery: {
18
+ partitionBy: "interaction_date",
19
+ clusterBy: ["video_id", "device_type", "operating_system"],
20
+ },
21
+ columns: column_descriptions.column_descriptions,
22
+ description: "YT Channel Device and OS Report Table - Intermediate",
23
+ })
24
+ .preOps(
25
+ (ctx) => `
26
+ declare interaction_date_checkpoint default (
27
+ select date("${config.startDate}")
28
+ );
29
+
30
+ --Set the incremental update checkpoint based on current max partition value minus lookback.
31
+ set interaction_date_checkpoint = (
32
+ ${ctx.when(
33
+ ctx.incremental(),
34
+ `select
35
+ least(
36
+ (select date_sub(current_date(), interval ${config.daysBack} day)),
37
+ (select date_sub(max(interaction_date), interval ${config.daysBack} day) from ${ctx.self()})
38
+ )`,
39
+ `select date("${config.startDate}")`,
40
+ )}
41
+ );
42
+
43
+ ${ctx.when(
44
+ ctx.incremental(),
45
+ `delete ${ctx.self()} where interaction_date > interaction_date_checkpoint`,
46
+ )}
47
+ `,
48
+ )
49
+ .query(
50
+ (ctx) => `
51
+
52
+ -- determine max possible viewing seconds per row so that percent viewed can be calculated downstream
53
+ with int_ex_titles as (
54
+ select
55
+ * except (source_partition_date),
56
+ views * safe_divide(average_view_duration_seconds, average_view_duration_percentage) as row_max_duration_seconds
57
+ from ${ctx.ref("stg_ytc_device_os")}
58
+ where interaction_date > interaction_date_checkpoint
59
+ )
60
+
61
+ select
62
+ int_ex_titles.*,
63
+ dt.device_name,
64
+ os.operating_system_name,
65
+ ifnull(titles.video_title, int_ex_titles.video_id) as video_title
66
+ from int_ex_titles
67
+ left join ${ctx.ref("stg_ytc_lu_device_types")} as dt
68
+ using (device_type)
69
+ left join ${ctx.ref("stg_ytc_lu_operating_systems")} as os
70
+ using (operating_system)
71
+ left join ${ctx.ref(
72
+ config.titlesProject,
73
+ config.titlesDataset,
74
+ config.titlesTable,
75
+ )} as titles
76
+ using (video_id)
77
+
78
+ `,
79
+ );
80
+ };