@adobe/spacecat-shared-athena-client 1.4.1 → 1.5.0

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [@adobe/spacecat-shared-athena-client-v1.5.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-athena-client-v1.4.1...@adobe/spacecat-shared-athena-client-v1.5.0) (2025-11-10)
2
+
3
+
4
+ ### Features
5
+
6
+ * added trend info to PTAv2 query ([#1109](https://github.com/adobe/spacecat-shared/issues/1109)) ([051a12a](https://github.com/adobe/spacecat-shared/commit/051a12a20fd10641b1e80dd31a3102a72c931bf9))
7
+
1
8
  # [@adobe/spacecat-shared-athena-client-v1.4.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-athena-client-v1.4.0...@adobe/spacecat-shared-athena-client-v1.4.1) (2025-11-08)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-athena-client",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "Shared modules of the Spacecat Services - AWS Athena Client",
5
5
  "type": "module",
6
6
  "engines": {
package/src/index.d.ts CHANGED
@@ -82,3 +82,50 @@ export declare class AWSAthenaClient {
82
82
  }
83
83
 
84
84
  export type Context = Parameters<typeof AWSAthenaClient.fromContext>[0];
85
+
86
+ // PTA2 Query Types
87
+ export interface PTAQueryParams {
88
+ siteId: string;
89
+ tableName: string;
90
+ week?: number;
91
+ month?: number;
92
+ year: number;
93
+ }
94
+
95
+ export interface PTASummaryData {
96
+ pageviews: number;
97
+ click_rate: number;
98
+ engagement_rate: number;
99
+ bounce_rate: number;
100
+ }
101
+
102
+ export interface PTATrendData {
103
+ pageviews: number;
104
+ click_rate: number;
105
+ engagement_rate: number;
106
+ bounce_rate: number;
107
+ trends: {
108
+ pageviews: number | null;
109
+ click_rate: number | null;
110
+ engagement_rate: number | null;
111
+ bounce_rate: number | null;
112
+ } | null;
113
+ }
114
+
115
+ export declare function getPreviousPeriod(params: {
116
+ week?: number;
117
+ month?: number;
118
+ year: number;
119
+ }): { week?: number; month?: number; year: number };
120
+
121
+ export declare function getPTASummaryQuery(params: PTAQueryParams): string;
122
+
123
+ export declare function getPTASummaryWithTrendQuery(params: PTAQueryParams): string;
124
+
125
+ export declare const PTASummaryResponseDto: {
126
+ toJSON: (data: Record<string, unknown>) => PTASummaryData;
127
+ };
128
+
129
+ export declare const PTASummaryWithTrendResponseDto: {
130
+ toJSON: (data: Array<Record<string, unknown>>) => PTATrendData;
131
+ };
package/src/index.js CHANGED
@@ -265,4 +265,10 @@ export {
265
265
  export { TrafficDataResponseDto } from './traffic-analysis/traffic-data-base-response.js';
266
266
  export { TrafficDataWithCWVDto } from './traffic-analysis/traffic-data-with-cwv.js';
267
267
 
268
- export { getPTASummaryQuery, PTASummaryResponseDto } from './pta2/queries.js';
268
+ export {
269
+ getPreviousPeriod,
270
+ getPTASummaryQuery,
271
+ getPTASummaryWithTrendQuery,
272
+ PTASummaryResponseDto,
273
+ PTASummaryWithTrendResponseDto,
274
+ } from './pta2/queries.js';
@@ -12,6 +12,40 @@
12
12
 
13
13
  import { getTemporalCondition } from '@adobe/spacecat-shared-utils';
14
14
 
15
+ /**
16
+ * Calculates the previous period (week or month) for trend comparison.
17
+ * @param {Object} params - Period parameters
18
+ * @param {number} params.week - Week number (optional)
19
+ * @param {number} params.month - Month number (optional)
20
+ * @param {number} params.year - Year number
21
+ * @returns {Object} Previous period with week/month and year
22
+ */
23
+ export function getPreviousPeriod({ week, month, year }) {
24
+ if (week !== undefined) {
25
+ // Calculate previous week
26
+ const prevWeek = week - 1;
27
+ if (prevWeek < 1) {
28
+ // Move to previous year's last week
29
+ const prevYear = year - 1;
30
+ // Approximate last week (52 or 53)
31
+ return { week: 52, year: prevYear };
32
+ }
33
+ return { week: prevWeek, year };
34
+ }
35
+
36
+ if (month !== undefined) {
37
+ // Calculate previous month
38
+ const prevMonth = month - 1;
39
+ if (prevMonth < 1) {
40
+ // Move to previous year's December
41
+ return { month: 12, year: year - 1 };
42
+ }
43
+ return { month: prevMonth, year };
44
+ }
45
+
46
+ throw new Error('Either week or month must be provided');
47
+ }
48
+
15
49
  /**
16
50
  * Generates the PTA summary query template using template literals.
17
51
  * @param {Object} params - Template parameters
@@ -56,6 +90,61 @@ export function getPTASummaryQuery({
56
90
  `;
57
91
  }
58
92
 
93
+ /**
94
+ * Generates a PTA summary query that includes both current and previous period data
95
+ * for trend analysis. More efficient than making two separate queries.
96
+ * @param {Object} params - Template parameters
97
+ * @param {string} params.siteId - Site ID
98
+ * @param {string} params.tableName - Table name
99
+ * @param {number} params.week - Week number (either week or month must be provided)
100
+ * @param {number} params.month - Month number (either week or month must be provided)
101
+ * @param {number} params.year - Year number
102
+ * @returns {string} The SQL query string with both current and previous period data
103
+ */
104
+ export function getPTASummaryWithTrendQuery({
105
+ siteId,
106
+ tableName,
107
+ week,
108
+ month,
109
+ year,
110
+ }) {
111
+ if (!siteId || !tableName) {
112
+ throw new Error('Missing required parameters: siteId, or tableName');
113
+ }
114
+
115
+ if ((!week && !month) || !year) {
116
+ throw new Error('Missing required parameters: week, month or year');
117
+ }
118
+
119
+ const currentTemporalCondition = getTemporalCondition({ week, month, year });
120
+ const previousPeriod = getPreviousPeriod({ week, month, year });
121
+ const previousTemporalCondition = getTemporalCondition(previousPeriod);
122
+
123
+ return `
124
+ SELECT
125
+ period,
126
+ CAST(SUM(pageviews) AS BIGINT) AS total_pageviews,
127
+ CAST(SUM(clicked) AS BIGINT) AS total_clicks,
128
+ CAST(SUM(engaged) AS BIGINT) AS total_engaged,
129
+ COUNT(*) AS total_rows,
130
+ CAST(SUM(clicked) AS DOUBLE) / NULLIF(COUNT(*), 0) AS click_rate,
131
+ CAST(SUM(engaged) AS DOUBLE) / NULLIF(COUNT(*), 0) AS engagement_rate,
132
+ 1 - CAST(SUM(engaged) AS DOUBLE) / NULLIF(COUNT(*), 0) AS bounce_rate
133
+ FROM (
134
+ SELECT *, 'current' as period FROM ${tableName}
135
+ WHERE siteid = '${siteId}'
136
+ AND (${currentTemporalCondition})
137
+ AND trf_type = 'paid'
138
+ UNION ALL
139
+ SELECT *, 'previous' as period FROM ${tableName}
140
+ WHERE siteid = '${siteId}'
141
+ AND (${previousTemporalCondition})
142
+ AND trf_type = 'paid'
143
+ )
144
+ GROUP BY period
145
+ `;
146
+ }
147
+
59
148
  export const PTASummaryResponseDto = {
60
149
  /**
61
150
  * Converts a traffic data object into a JSON object.
@@ -74,3 +163,64 @@ export const PTASummaryResponseDto = {
74
163
  bounce_rate: data.bounce_rate,
75
164
  }),
76
165
  };
166
+
167
+ export const PTASummaryWithTrendResponseDto = {
168
+ /**
169
+ * Converts trend data (array with current and previous periods)
170
+ * into a JSON object with current metrics and trends.
171
+ * @param {Array<object>} data - Array of traffic data objects with 'period' field.
172
+ * @returns {{
173
+ * pageviews: number,
174
+ * click_rate: number,
175
+ * engagement_rate: number,
176
+ * bounce_rate: number,
177
+ * trends: {
178
+ * pageviews: number,
179
+ * click_rate: number,
180
+ * engagement_rate: number,
181
+ * bounce_rate: number
182
+ * }
183
+ * }} JSON object with current metrics and trend percentages.
184
+ */
185
+ toJSON: (data) => {
186
+ if (!Array.isArray(data) || data.length === 0) {
187
+ throw new Error('Expected an array with at least one period');
188
+ }
189
+
190
+ const currentData = data.find((row) => row.period === 'current');
191
+ const previousData = data.find((row) => row.period === 'previous');
192
+
193
+ if (!currentData) {
194
+ throw new Error('Current period data not found');
195
+ }
196
+
197
+ const current = {
198
+ pageviews: currentData.total_pageviews,
199
+ click_rate: currentData.click_rate,
200
+ engagement_rate: currentData.engagement_rate,
201
+ bounce_rate: currentData.bounce_rate,
202
+ };
203
+
204
+ // Calculate trend percentages (null if no previous data)
205
+ const trends = previousData ? {
206
+ pageviews: previousData.total_pageviews > 0
207
+ ? ((current.pageviews - previousData.total_pageviews) / previousData.total_pageviews) * 100
208
+ : null,
209
+ click_rate: previousData.click_rate > 0
210
+ ? ((current.click_rate - previousData.click_rate) / previousData.click_rate) * 100
211
+ : null,
212
+ engagement_rate: previousData.engagement_rate > 0
213
+ ? ((current.engagement_rate - previousData.engagement_rate)
214
+ / previousData.engagement_rate) * 100
215
+ : null,
216
+ bounce_rate: previousData.bounce_rate > 0
217
+ ? ((current.bounce_rate - previousData.bounce_rate) / previousData.bounce_rate) * 100
218
+ : null,
219
+ } : null;
220
+
221
+ return {
222
+ ...current,
223
+ trends,
224
+ };
225
+ },
226
+ };