@backstage-community/plugin-copilot-backend 0.15.0 → 0.15.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @backstage-community/plugin-copilot-backend
2
2
 
3
+ ## 0.15.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 1b7adc8: Fixed a mismatch between the backend requiring copilot.host and the config declaration stating it was optional.
8
+
9
+ ## 0.15.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 3fd5298: Fixed an issue causing incorrect metrics to be displayed due to duplicate database entries for the same day.
14
+
3
15
  ## 0.15.0
4
16
 
5
17
  ### Minor Changes
package/config.d.ts CHANGED
@@ -37,6 +37,6 @@ export interface Config {
37
37
  /**
38
38
  * The host for GitHub Copilot integration.
39
39
  */
40
- host?: string;
40
+ host: string;
41
41
  };
42
42
  }
@@ -35,7 +35,7 @@ class DatabaseHandler {
35
35
  return { minDate: minDate.day, maxDate: maxDate.day };
36
36
  }
37
37
  async getTeams(type, startDate, endDate) {
38
- const result = await this.db("copilot_metrics").where("type", type).whereBetween("day", [startDate, endDate]).whereNotNull("team_name").distinct("team_name").orderBy("team_name", "asc").select("team_name");
38
+ const result = await this.db("copilot_metrics").where("type", type).whereBetween("day", [startDate, endDate]).whereNot("team_name", "").distinct("team_name").orderBy("team_name", "asc").select("team_name");
39
39
  return result.map((x) => x.team_name);
40
40
  }
41
41
  async batchInsert(metrics) {
@@ -81,7 +81,7 @@ class DatabaseHandler {
81
81
  }
82
82
  async getMostRecentDayFromMetrics(type, teamName) {
83
83
  try {
84
- const query = await this.db("metrics").where("type", type).where("team_name", teamName ?? null).orderBy("day", "desc").first("day");
84
+ const query = await this.db("metrics").where("type", type).where("team_name", teamName ?? "").orderBy("day", "desc").first("day");
85
85
  return query ? query.day : void 0;
86
86
  } catch (e) {
87
87
  return void 0;
@@ -89,7 +89,7 @@ class DatabaseHandler {
89
89
  }
90
90
  async getMostRecentDayFromMetricsV2(type, teamName) {
91
91
  try {
92
- const query = this.db("copilot_metrics").where("type", type).where("team_name", teamName ?? null).orderBy("day", "desc").first("day");
92
+ const query = this.db("copilot_metrics").where("type", type).where("team_name", teamName ?? "").orderBy("day", "desc").first("day");
93
93
  const res = await query;
94
94
  return res ? res.day : void 0;
95
95
  } catch (e) {
@@ -98,7 +98,7 @@ class DatabaseHandler {
98
98
  }
99
99
  async getEarliestDayFromMetricsV2(type, teamName) {
100
100
  try {
101
- const query = this.db("copilot_metrics").where("type", type).where("team_name", teamName ?? null).orderBy("day", "asc").first("day");
101
+ const query = this.db("copilot_metrics").where("type", type).where("team_name", teamName ?? "").orderBy("day", "asc").first("day");
102
102
  const res = await query;
103
103
  return res ? res.day : void 0;
104
104
  } catch (e) {
@@ -106,10 +106,7 @@ class DatabaseHandler {
106
106
  }
107
107
  }
108
108
  async getMetrics(startDate, endDate, type, teamName) {
109
- if (teamName) {
110
- return await this.db("metrics").where("type", type).where("team_name", teamName).whereBetween("day", [startDate, endDate]);
111
- }
112
- return this.db("metrics").where("type", type).whereNull("team_name").whereBetween("day", [startDate, endDate]);
109
+ return await this.db("metrics").where("type", type).where("team_name", teamName ?? "").whereBetween("day", [startDate, endDate]);
113
110
  }
114
111
  async getSeatMetrics(startDate, endDate, type, teamName) {
115
112
  return await this.db("seats").where("type", type).where("team_name", teamName ?? "").whereBetween("day", [startDate, endDate]).orderBy("day", "asc");
@@ -182,7 +179,7 @@ class DatabaseHandler {
182
179
  return await query;
183
180
  }
184
181
  async getMetricsV2(startDate, endDate, type, teamName) {
185
- let query = this.db("copilot_metrics as cm").select(
182
+ const query = this.db("copilot_metrics as cm").select(
186
183
  "cm.day",
187
184
  "cm.type",
188
185
  "cm.team_name",
@@ -212,23 +209,17 @@ class DatabaseHandler {
212
209
  ),
213
210
  this.db.raw("'' as breakdown")
214
211
  ).join("ide_completions", (join) => {
215
- join.on("ide_completions.day", "=", "cm.day").andOn("ide_completions.type", "=", "cm.type");
216
- if (teamName) {
217
- join.andOn(
218
- "ide_completions.team_name",
219
- "=",
220
- this.db.raw("?", [teamName])
221
- );
222
- } else {
223
- join.andOnNull("ide_completions.team_name");
224
- }
212
+ join.on("ide_completions.day", "=", "cm.day").andOn("ide_completions.type", "=", "cm.type").andOn(
213
+ "ide_completions.team_name",
214
+ "=",
215
+ this.db.raw("?", [teamName ?? ""])
216
+ );
225
217
  }).join("ide_chats", (join) => {
226
- join.on("ide_chats.day", "=", "cm.day").andOn("ide_chats.type", "=", "cm.type");
227
- if (teamName) {
228
- join.andOn("ide_chats.team_name", "=", this.db.raw("?", [teamName]));
229
- } else {
230
- join.andOnNull("ide_chats.team_name");
231
- }
218
+ join.on("ide_chats.day", "=", "cm.day").andOn("ide_chats.type", "=", "cm.type").andOn(
219
+ "ide_chats.team_name",
220
+ "=",
221
+ this.db.raw("?", [teamName ?? ""])
222
+ );
232
223
  }).join(
233
224
  this.db.raw(
234
225
  `(SELECT day, type, team_name,
@@ -240,12 +231,7 @@ class DatabaseHandler {
240
231
  as icelm`
241
232
  ),
242
233
  (join) => {
243
- join.on("icelm.day", "=", "cm.day").andOn("icelm.type", "=", "cm.type");
244
- if (teamName) {
245
- join.andOn("icelm.team_name", "=", this.db.raw("?", [teamName]));
246
- } else {
247
- join.andOnNull("icelm.team_name");
248
- }
234
+ join.on("icelm.day", "=", "cm.day").andOn("icelm.type", "=", "cm.type").andOn("icelm.team_name", "=", this.db.raw("?", [teamName ?? ""]));
249
235
  }
250
236
  ).join(
251
237
  this.db.raw(
@@ -254,23 +240,13 @@ class DatabaseHandler {
254
240
  FROM ide_chat_editors_model GROUP BY day, type, team_name) as icem`
255
241
  ),
256
242
  (join) => {
257
- join.on("icem.day", "=", "cm.day").andOn("icem.type", "=", "cm.type");
258
- if (teamName) {
259
- join.andOn("icem.team_name", "=", this.db.raw("?", [teamName]));
260
- } else {
261
- join.andOnNull("icem.team_name");
262
- }
243
+ join.on("icem.day", "=", "cm.day").andOn("icem.type", "=", "cm.type").andOn("icem.team_name", "=", this.db.raw("?", [teamName ?? ""]));
263
244
  }
264
- ).where("cm.type", type).whereBetween("cm.day", [startDate, endDate]).groupBy("cm.day", "cm.type", "cm.team_name").orderBy("cm.day", "asc");
265
- if (teamName) {
266
- query = query.where("cm.team_name", teamName);
267
- } else {
268
- query = query.whereNull("cm.team_name");
269
- }
245
+ ).where("cm.type", type).where("cm.team_name", teamName ?? "").whereBetween("cm.day", [startDate, endDate]).groupBy("cm.day", "cm.type", "cm.team_name").orderBy("cm.day", "asc");
270
246
  return await query;
271
247
  }
272
248
  async getBreakdown(startDate, endDate, type, teamName) {
273
- let query = this.db("copilot_metrics as cm").select(
249
+ const query = this.db("copilot_metrics as cm").select(
274
250
  "cm.day",
275
251
  "icleml.editor as editor",
276
252
  "icleml.language as language",
@@ -292,22 +268,13 @@ class DatabaseHandler {
292
268
  ).join(
293
269
  "ide_completions_language_editors_model_language as icleml",
294
270
  (join) => {
295
- join.on("icleml.day", "=", "cm.day").andOn("icleml.type", "=", "cm.type");
296
- if (teamName) {
297
- join.andOn("icleml.team_name", "=", this.db.raw("?", [teamName]));
298
- } else {
299
- join.andOnNull("icleml.team_name");
300
- }
271
+ join.on("icleml.day", "=", "cm.day").andOn("icleml.type", "=", "cm.type").andOn("icleml.team_name", "=", this.db.raw("?", [teamName ?? ""]));
301
272
  }
302
- ).whereBetween("cm.day", [startDate, endDate]).where("icleml.model", "default").where("cm.type", type).groupBy("cm.day", "icleml.editor", "icleml.language").orderBy("cm.day", "asc");
303
- if (teamName) {
304
- query = query.where("cm.team_name", teamName);
305
- } else {
306
- query = query.whereNull("cm.team_name");
307
- }
273
+ ).whereBetween("cm.day", [startDate, endDate]).where("icleml.model", "default").where("cm.type", type).where("cm.team_name", teamName ?? "").groupBy("cm.day", "icleml.editor", "icleml.language").orderBy("cm.day", "asc");
308
274
  return await query;
309
275
  }
310
276
  }
311
277
 
312
278
  exports.DatabaseHandler = DatabaseHandler;
279
+ exports.migrationsDir = migrationsDir;
313
280
  //# sourceMappingURL=DatabaseHandler.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DatabaseHandler.cjs.js","sources":["../../src/db/DatabaseHandler.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n resolvePackagePath,\n DatabaseService,\n} from '@backstage/backend-plugin-api';\nimport {\n Metric,\n MetricsType,\n PeriodRange,\n CopilotIdeCodeCompletions,\n CopilotIdeLanguages,\n CopilotMetrics,\n CopilotEditors,\n CopilotModels,\n CopilotLanguages,\n CopilotChats,\n CopilotChatEditors,\n CopilotChatModels,\n EngagementMetrics,\n SeatAnalysis,\n} from '@backstage-community/plugin-copilot-common';\nimport { Knex } from 'knex';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage-community/plugin-copilot-backend',\n 'migrations',\n);\n\ntype Options = {\n database: DatabaseService;\n};\n\nexport type Breakdown = {\n day: string;\n editor: string;\n language: string;\n lines_accepted: number;\n lines_suggested: number;\n suggestions_count: number;\n acceptances_count: number;\n active_users: number;\n};\n\nexport type CopilotMetricsDb = Omit<\n CopilotMetrics,\n | 'date'\n | 'copilot_ide_code_completions'\n | 'copilot_ide_chat'\n | 'copilot_dotcom_chat'\n | 'copilot_dotcom_pull_requests'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n};\n\nexport type CopilotIdeCodeCompletionsDb = Omit<\n CopilotIdeCodeCompletions,\n 'editors' | 'languages'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n};\n\nexport type CopilotIdeCodeCompletionsLanguageDb = Omit<\n CopilotIdeLanguages,\n 'day' | 'name' | 'language'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n language: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorsDb = Omit<\n CopilotEditors,\n 'day' | 'name' | 'models'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n};\n\nexport type CopilotIdeChatsDb = Omit<CopilotChats, 'day' | 'editors'> & {\n day: string;\n};\n\nexport type CopilotIdeChatsEditorsDb = Omit<\n CopilotChatEditors,\n 'day' | 'name' | 'models' | 'editor'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n};\n\nexport type CopilotIdeChatsEditorModelDb = Omit<\n CopilotChatModels,\n 'name' | 'day' | 'model' | 'editor'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorModelsDb = Omit<\n CopilotModels,\n 'day' | 'editor' | 'model' | 'name' | 'languages'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorModelLanguagesDb = Omit<\n CopilotLanguages,\n 'day' | 'editor' | 'model' | 'name' | 'language'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n language: string;\n};\n\nexport type MetricDbRow = Omit<Metric, 'breakdown'> & {\n breakdown: string;\n};\n\nexport class DatabaseHandler {\n static async create(options: Options): Promise<DatabaseHandler> {\n const { database } = options;\n const client = await database.getClient();\n\n if (!database.migrations?.skip) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new DatabaseHandler(client);\n }\n\n private constructor(private readonly db: Knex) {}\n\n async getPeriodRange(type: MetricsType): Promise<PeriodRange | undefined> {\n const query = this.db<MetricDbRow>('metrics').where('type', type);\n\n const minDate = await query.orderBy('day', 'asc').first('day');\n const maxDate = await query.orderBy('day', 'desc').first('day');\n\n if (!minDate?.day || !maxDate?.day) return undefined;\n\n return { minDate: minDate.day, maxDate: maxDate.day };\n }\n\n async getPeriodRangeV2(type: MetricsType): Promise<PeriodRange | undefined> {\n const query = this.db('copilot_metrics').where('type', type);\n\n const minDate = await query.orderBy('day', 'asc').first('day');\n const maxDate = await query.orderBy('day', 'desc').first('day');\n\n if (!minDate?.day || !maxDate?.day) return undefined;\n\n return { minDate: minDate.day, maxDate: maxDate.day };\n }\n\n async getTeams(\n type: MetricsType,\n startDate: string,\n endDate: string,\n ): Promise<Array<string | undefined>> {\n const result = await this.db<MetricDbRow>('copilot_metrics')\n .where('type', type)\n .whereBetween('day', [startDate, endDate])\n .whereNotNull('team_name')\n .distinct('team_name')\n .orderBy('team_name', 'asc')\n .select('team_name');\n\n return result.map(x => x.team_name);\n }\n\n async batchInsert(metrics: MetricDbRow[]): Promise<void> {\n await this.db<MetricDbRow[]>('metrics')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertMetrics(metrics: CopilotMetricsDb[]): Promise<void> {\n await this.db<CopilotMetricsDb[]>('copilot_metrics')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeCompletions(\n metrics: CopilotIdeCodeCompletionsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsDb[]>('ide_completions')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsLanguages(\n metrics: CopilotIdeCodeCompletionsLanguageDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsLanguageDb[]>(\n 'ide_completions_language_users',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'language'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditors(\n metrics: CopilotIdeCodeCompletionsEditorsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorsDb[]>(\n 'ide_completions_language_editors',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditorModels(\n metrics: CopilotIdeCodeCompletionsEditorModelsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorModelsDb[]>(\n 'ide_completions_language_editors_model',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditorModelLanguages(\n metrics: CopilotIdeCodeCompletionsEditorModelLanguagesDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorModelLanguagesDb[]>(\n 'ide_completions_language_editors_model_language',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model', 'language'])\n .ignore();\n }\n\n async batchInsertIdeChats(metrics: CopilotIdeChatsDb[]): Promise<void> {\n await this.db<CopilotIdeChatsDb[]>('ide_chats')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeChatEditors(\n metrics: CopilotIdeChatsEditorsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeChatsEditorsDb[]>('ide_chat_editors')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor'])\n .ignore();\n }\n\n async batchInsertIdeChatEditorModels(\n metrics: CopilotIdeChatsEditorModelDb[],\n ): Promise<void> {\n await this.db<CopilotIdeChatsEditorModelDb[]>('ide_chat_editors_model')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model'])\n .ignore();\n }\n\n async insertSeatAnalysys(metric: SeatAnalysis): Promise<void> {\n await this.db<SeatAnalysis>('seats')\n .insert(metric)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async getMostRecentDayFromMetrics(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = await this.db<MetricDbRow>('metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'desc')\n .first('day');\n return query ? query.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getMostRecentDayFromMetricsV2(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = this.db('copilot_metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'desc')\n .first('day');\n const res = await query;\n return res ? res.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getEarliestDayFromMetricsV2(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = this.db('copilot_metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'asc')\n .first('day');\n const res = await query;\n return res ? res.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<MetricDbRow[]> {\n if (teamName) {\n return await this.db<MetricDbRow>('metrics')\n .where('type', type)\n .where('team_name', teamName)\n .whereBetween('day', [startDate, endDate]);\n }\n return this.db<MetricDbRow>('metrics')\n .where('type', type)\n .whereNull('team_name')\n .whereBetween('day', [startDate, endDate]);\n }\n\n async getSeatMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<SeatAnalysis[]> {\n return await this.db<SeatAnalysis>('seats')\n .where('type', type)\n .where('team_name', teamName ?? '')\n .whereBetween('day', [startDate, endDate])\n .orderBy('day', 'asc');\n }\n\n async getEngagementMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<EngagementMetrics[]> {\n let query = this.db('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'cm.type',\n 'cm.team_name',\n this.db.raw(\n 'CAST(MIN(cm.total_active_users) AS INTEGER) as total_active_users',\n ),\n this.db.raw(\n 'CAST(MIN(cm.total_engaged_users) AS INTEGER) as total_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_completions.total_engaged_users) AS INTEGER) as ide_completions_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_chats.total_engaged_users) AS INTEGER) as ide_chats_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(dotcom_chats.total_engaged_users) AS INTEGER) as dotcom_chats_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(dotcom_prs.total_engaged_users) AS INTEGER) as dotcom_prs_engaged_users',\n ),\n )\n .leftJoin('ide_completions', join => {\n join\n .on('ide_completions.day', '=', 'cm.day')\n .andOn('ide_completions.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'ide_completions.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('ide_completions.team_name');\n }\n })\n .leftJoin('ide_chats', join => {\n join\n .on('ide_chats.day', '=', 'cm.day')\n .andOn('ide_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('ide_chats.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('ide_chats.team_name');\n }\n })\n .leftJoin('dotcom_chats', join => {\n join\n .on('dotcom_chats.day', '=', 'cm.day')\n .andOn('dotcom_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'dotcom_chats.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('dotcom_chats.team_name');\n }\n })\n .leftJoin('dotcom_prs', join => {\n join\n .on('dotcom_prs.day', '=', 'cm.day')\n .andOn('dotcom_prs.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('dotcom_prs.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('dotcom_prs.team_name');\n }\n })\n .where('cm.type', type)\n .whereBetween('cm.day', [startDate, endDate])\n .groupBy('cm.day', 'cm.type', 'cm.team_name')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n\n async getMetricsV2(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<MetricDbRow[]> {\n let query = this.db('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'cm.type',\n 'cm.team_name',\n this.db.raw(\n 'CAST(MIN(cm.total_active_users) AS INTEGER) as total_active_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_chats.total_engaged_users) AS INTEGER) as total_active_chat_users',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_suggestions) AS INTEGER) as total_suggestions_count',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_acceptances) AS INTEGER) as total_acceptances_count',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_lines_suggested) AS INTEGER) as total_lines_suggested',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_lines_accepted) AS INTEGER) as total_lines_accepted',\n ),\n this.db.raw(\n 'CAST(SUM(icem.total_chats) AS INTEGER) as total_chat_turns',\n ),\n this.db.raw(\n 'CAST(SUM(icem.total_chat_copy_events) AS INTEGER) as total_chat_acceptances',\n ),\n this.db.raw(\"'' as breakdown\"),\n )\n .join('ide_completions', join => {\n join\n .on('ide_completions.day', '=', 'cm.day')\n .andOn('ide_completions.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'ide_completions.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('ide_completions.team_name');\n }\n })\n .join('ide_chats', join => {\n join\n .on('ide_chats.day', '=', 'cm.day')\n .andOn('ide_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('ide_chats.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('ide_chats.team_name');\n }\n })\n .join(\n this.db.raw(\n `(SELECT day, type, team_name,\n SUM(total_code_suggestions) as total_code_suggestions, \n SUM(total_code_acceptances) as total_code_acceptances, \n SUM(total_code_lines_suggested) as total_code_lines_suggested, \n SUM(total_code_lines_accepted) as total_code_lines_accepted \n FROM ide_completions_language_editors_model_language GROUP BY day, type, team_name) \n as icelm`,\n ),\n join => {\n join\n .on('icelm.day', '=', 'cm.day')\n .andOn('icelm.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icelm.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icelm.team_name');\n }\n },\n )\n .join(\n this.db.raw(\n `(SELECT day, type, team_name, SUM(total_chats) as total_chats, \n SUM(total_chat_copy_events) as total_chat_copy_events \n FROM ide_chat_editors_model GROUP BY day, type, team_name) as icem`,\n ),\n join => {\n join.on('icem.day', '=', 'cm.day').andOn('icem.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icem.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icem.team_name');\n }\n },\n )\n .where('cm.type', type)\n .whereBetween('cm.day', [startDate, endDate])\n .groupBy('cm.day', 'cm.type', 'cm.team_name')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n\n async getBreakdown(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<Breakdown[]> {\n let query = this.db<Breakdown>('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'icleml.editor as editor',\n 'icleml.language as language',\n this.db.raw(\n 'CAST(SUM(icleml.total_engaged_users) AS INTEGER) as active_users',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_lines_suggested) AS INTEGER) as lines_suggested',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_lines_accepted) AS INTEGER) as lines_accepted',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_suggestions) AS INTEGER) as suggestions_count',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_acceptances) AS INTEGER) as acceptances_count',\n ),\n )\n .join(\n 'ide_completions_language_editors_model_language as icleml',\n join => {\n join\n .on('icleml.day', '=', 'cm.day')\n .andOn('icleml.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icleml.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icleml.team_name');\n }\n },\n )\n .whereBetween('cm.day', [startDate, endDate])\n .where('icleml.model', 'default')\n .where('cm.type', type)\n .groupBy('cm.day', 'icleml.editor', 'icleml.language')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n}\n"],"names":["resolvePackagePath"],"mappings":"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"}
1
+ {"version":3,"file":"DatabaseHandler.cjs.js","sources":["../../src/db/DatabaseHandler.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n resolvePackagePath,\n DatabaseService,\n} from '@backstage/backend-plugin-api';\nimport {\n Metric,\n MetricsType,\n PeriodRange,\n CopilotIdeCodeCompletions,\n CopilotIdeLanguages,\n CopilotMetrics,\n CopilotEditors,\n CopilotModels,\n CopilotLanguages,\n CopilotChats,\n CopilotChatEditors,\n CopilotChatModels,\n EngagementMetrics,\n SeatAnalysis,\n} from '@backstage-community/plugin-copilot-common';\nimport { Knex } from 'knex';\n\nexport const migrationsDir = resolvePackagePath(\n '@backstage-community/plugin-copilot-backend',\n 'migrations',\n);\n\ntype Options = {\n database: DatabaseService;\n};\n\nexport type Breakdown = {\n day: string;\n editor: string;\n language: string;\n lines_accepted: number;\n lines_suggested: number;\n suggestions_count: number;\n acceptances_count: number;\n active_users: number;\n};\n\nexport type CopilotMetricsDb = Omit<\n CopilotMetrics,\n | 'date'\n | 'copilot_ide_code_completions'\n | 'copilot_ide_chat'\n | 'copilot_dotcom_chat'\n | 'copilot_dotcom_pull_requests'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n};\n\nexport type CopilotIdeCodeCompletionsDb = Omit<\n CopilotIdeCodeCompletions,\n 'editors' | 'languages'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n};\n\nexport type CopilotIdeCodeCompletionsLanguageDb = Omit<\n CopilotIdeLanguages,\n 'day' | 'name' | 'language'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n language: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorsDb = Omit<\n CopilotEditors,\n 'day' | 'name' | 'models'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n};\n\nexport type CopilotIdeChatsDb = Omit<CopilotChats, 'day' | 'editors'> & {\n day: string;\n};\n\nexport type CopilotIdeChatsEditorsDb = Omit<\n CopilotChatEditors,\n 'day' | 'name' | 'models' | 'editor'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n};\n\nexport type CopilotIdeChatsEditorModelDb = Omit<\n CopilotChatModels,\n 'name' | 'day' | 'model' | 'editor'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorModelsDb = Omit<\n CopilotModels,\n 'day' | 'editor' | 'model' | 'name' | 'languages'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorModelLanguagesDb = Omit<\n CopilotLanguages,\n 'day' | 'editor' | 'model' | 'name' | 'language'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n language: string;\n};\n\nexport type MetricDbRow = Omit<Metric, 'breakdown'> & {\n breakdown: string;\n};\n\nexport class DatabaseHandler {\n static async create(options: Options): Promise<DatabaseHandler> {\n const { database } = options;\n const client = await database.getClient();\n\n if (!database.migrations?.skip) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new DatabaseHandler(client);\n }\n\n private constructor(private readonly db: Knex) {}\n\n async getPeriodRange(type: MetricsType): Promise<PeriodRange | undefined> {\n const query = this.db<MetricDbRow>('metrics').where('type', type);\n\n const minDate = await query.orderBy('day', 'asc').first('day');\n const maxDate = await query.orderBy('day', 'desc').first('day');\n\n if (!minDate?.day || !maxDate?.day) return undefined;\n\n return { minDate: minDate.day, maxDate: maxDate.day };\n }\n\n async getPeriodRangeV2(type: MetricsType): Promise<PeriodRange | undefined> {\n const query = this.db('copilot_metrics').where('type', type);\n\n const minDate = await query.orderBy('day', 'asc').first('day');\n const maxDate = await query.orderBy('day', 'desc').first('day');\n\n if (!minDate?.day || !maxDate?.day) return undefined;\n\n return { minDate: minDate.day, maxDate: maxDate.day };\n }\n\n async getTeams(\n type: MetricsType,\n startDate: string,\n endDate: string,\n ): Promise<Array<string | undefined>> {\n const result = await this.db<MetricDbRow>('copilot_metrics')\n .where('type', type)\n .whereBetween('day', [startDate, endDate])\n .whereNot('team_name', '')\n .distinct('team_name')\n .orderBy('team_name', 'asc')\n .select('team_name');\n\n return result.map(x => x.team_name);\n }\n\n async batchInsert(metrics: MetricDbRow[]): Promise<void> {\n await this.db<MetricDbRow[]>('metrics')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertMetrics(metrics: CopilotMetricsDb[]): Promise<void> {\n await this.db<CopilotMetricsDb[]>('copilot_metrics')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeCompletions(\n metrics: CopilotIdeCodeCompletionsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsDb[]>('ide_completions')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsLanguages(\n metrics: CopilotIdeCodeCompletionsLanguageDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsLanguageDb[]>(\n 'ide_completions_language_users',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'language'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditors(\n metrics: CopilotIdeCodeCompletionsEditorsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorsDb[]>(\n 'ide_completions_language_editors',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditorModels(\n metrics: CopilotIdeCodeCompletionsEditorModelsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorModelsDb[]>(\n 'ide_completions_language_editors_model',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditorModelLanguages(\n metrics: CopilotIdeCodeCompletionsEditorModelLanguagesDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorModelLanguagesDb[]>(\n 'ide_completions_language_editors_model_language',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model', 'language'])\n .ignore();\n }\n\n async batchInsertIdeChats(metrics: CopilotIdeChatsDb[]): Promise<void> {\n await this.db<CopilotIdeChatsDb[]>('ide_chats')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeChatEditors(\n metrics: CopilotIdeChatsEditorsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeChatsEditorsDb[]>('ide_chat_editors')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor'])\n .ignore();\n }\n\n async batchInsertIdeChatEditorModels(\n metrics: CopilotIdeChatsEditorModelDb[],\n ): Promise<void> {\n await this.db<CopilotIdeChatsEditorModelDb[]>('ide_chat_editors_model')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model'])\n .ignore();\n }\n\n async insertSeatAnalysys(metric: SeatAnalysis): Promise<void> {\n await this.db<SeatAnalysis>('seats')\n .insert(metric)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async getMostRecentDayFromMetrics(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = await this.db<MetricDbRow>('metrics')\n .where('type', type)\n .where('team_name', teamName ?? '')\n .orderBy('day', 'desc')\n .first('day');\n return query ? query.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getMostRecentDayFromMetricsV2(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = this.db('copilot_metrics')\n .where('type', type)\n .where('team_name', teamName ?? '')\n .orderBy('day', 'desc')\n .first('day');\n const res = await query;\n return res ? res.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getEarliestDayFromMetricsV2(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = this.db('copilot_metrics')\n .where('type', type)\n .where('team_name', teamName ?? '')\n .orderBy('day', 'asc')\n .first('day');\n const res = await query;\n return res ? res.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<MetricDbRow[]> {\n return await this.db<MetricDbRow>('metrics')\n .where('type', type)\n .where('team_name', teamName ?? '')\n .whereBetween('day', [startDate, endDate]);\n }\n\n async getSeatMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<SeatAnalysis[]> {\n return await this.db<SeatAnalysis>('seats')\n .where('type', type)\n .where('team_name', teamName ?? '')\n .whereBetween('day', [startDate, endDate])\n .orderBy('day', 'asc');\n }\n\n async getEngagementMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<EngagementMetrics[]> {\n let query = this.db('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'cm.type',\n 'cm.team_name',\n this.db.raw(\n 'CAST(MIN(cm.total_active_users) AS INTEGER) as total_active_users',\n ),\n this.db.raw(\n 'CAST(MIN(cm.total_engaged_users) AS INTEGER) as total_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_completions.total_engaged_users) AS INTEGER) as ide_completions_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_chats.total_engaged_users) AS INTEGER) as ide_chats_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(dotcom_chats.total_engaged_users) AS INTEGER) as dotcom_chats_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(dotcom_prs.total_engaged_users) AS INTEGER) as dotcom_prs_engaged_users',\n ),\n )\n .leftJoin('ide_completions', join => {\n join\n .on('ide_completions.day', '=', 'cm.day')\n .andOn('ide_completions.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'ide_completions.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('ide_completions.team_name');\n }\n })\n .leftJoin('ide_chats', join => {\n join\n .on('ide_chats.day', '=', 'cm.day')\n .andOn('ide_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('ide_chats.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('ide_chats.team_name');\n }\n })\n .leftJoin('dotcom_chats', join => {\n join\n .on('dotcom_chats.day', '=', 'cm.day')\n .andOn('dotcom_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'dotcom_chats.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('dotcom_chats.team_name');\n }\n })\n .leftJoin('dotcom_prs', join => {\n join\n .on('dotcom_prs.day', '=', 'cm.day')\n .andOn('dotcom_prs.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('dotcom_prs.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('dotcom_prs.team_name');\n }\n })\n .where('cm.type', type)\n .whereBetween('cm.day', [startDate, endDate])\n .groupBy('cm.day', 'cm.type', 'cm.team_name')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n\n async getMetricsV2(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<MetricDbRow[]> {\n const query = this.db('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'cm.type',\n 'cm.team_name',\n this.db.raw(\n 'CAST(MIN(cm.total_active_users) AS INTEGER) as total_active_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_chats.total_engaged_users) AS INTEGER) as total_active_chat_users',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_suggestions) AS INTEGER) as total_suggestions_count',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_acceptances) AS INTEGER) as total_acceptances_count',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_lines_suggested) AS INTEGER) as total_lines_suggested',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_lines_accepted) AS INTEGER) as total_lines_accepted',\n ),\n this.db.raw(\n 'CAST(SUM(icem.total_chats) AS INTEGER) as total_chat_turns',\n ),\n this.db.raw(\n 'CAST(SUM(icem.total_chat_copy_events) AS INTEGER) as total_chat_acceptances',\n ),\n this.db.raw(\"'' as breakdown\"),\n )\n .join('ide_completions', join => {\n join\n .on('ide_completions.day', '=', 'cm.day')\n .andOn('ide_completions.type', '=', 'cm.type')\n .andOn(\n 'ide_completions.team_name',\n '=',\n this.db.raw('?', [teamName ?? '']),\n );\n })\n .join('ide_chats', join => {\n join\n .on('ide_chats.day', '=', 'cm.day')\n .andOn('ide_chats.type', '=', 'cm.type')\n .andOn(\n 'ide_chats.team_name',\n '=',\n this.db.raw('?', [teamName ?? '']),\n );\n })\n .join(\n this.db.raw(\n `(SELECT day, type, team_name,\n SUM(total_code_suggestions) as total_code_suggestions, \n SUM(total_code_acceptances) as total_code_acceptances, \n SUM(total_code_lines_suggested) as total_code_lines_suggested, \n SUM(total_code_lines_accepted) as total_code_lines_accepted \n FROM ide_completions_language_editors_model_language GROUP BY day, type, team_name) \n as icelm`,\n ),\n join => {\n join\n .on('icelm.day', '=', 'cm.day')\n .andOn('icelm.type', '=', 'cm.type')\n .andOn('icelm.team_name', '=', this.db.raw('?', [teamName ?? '']));\n },\n )\n .join(\n this.db.raw(\n `(SELECT day, type, team_name, SUM(total_chats) as total_chats, \n SUM(total_chat_copy_events) as total_chat_copy_events \n FROM ide_chat_editors_model GROUP BY day, type, team_name) as icem`,\n ),\n join => {\n join\n .on('icem.day', '=', 'cm.day')\n .andOn('icem.type', '=', 'cm.type')\n .andOn('icem.team_name', '=', this.db.raw('?', [teamName ?? '']));\n },\n )\n .where('cm.type', type)\n .where('cm.team_name', teamName ?? '')\n .whereBetween('cm.day', [startDate, endDate])\n .groupBy('cm.day', 'cm.type', 'cm.team_name')\n .orderBy('cm.day', 'asc');\n\n return await query;\n }\n\n async getBreakdown(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<Breakdown[]> {\n const query = this.db<Breakdown>('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'icleml.editor as editor',\n 'icleml.language as language',\n this.db.raw(\n 'CAST(SUM(icleml.total_engaged_users) AS INTEGER) as active_users',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_lines_suggested) AS INTEGER) as lines_suggested',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_lines_accepted) AS INTEGER) as lines_accepted',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_suggestions) AS INTEGER) as suggestions_count',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_acceptances) AS INTEGER) as acceptances_count',\n ),\n )\n .join(\n 'ide_completions_language_editors_model_language as icleml',\n join => {\n join\n .on('icleml.day', '=', 'cm.day')\n .andOn('icleml.type', '=', 'cm.type')\n .andOn('icleml.team_name', '=', this.db.raw('?', [teamName ?? '']));\n },\n )\n .whereBetween('cm.day', [startDate, endDate])\n .where('icleml.model', 'default')\n .where('cm.type', type)\n .where('cm.team_name', teamName ?? '')\n .groupBy('cm.day', 'icleml.editor', 'icleml.language')\n .orderBy('cm.day', 'asc');\n\n return await query;\n }\n}\n"],"names":["resolvePackagePath"],"mappings":"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"}
@@ -10,7 +10,7 @@ const getCopilotConfig = (config) => {
10
10
  const githubConfig = integrations.github.byHost(host)?.config;
11
11
  if (!githubConfig) {
12
12
  throw new Error(
13
- `GitHub configuration for host "${host}" is missing or incomplete. Please check the integretions configuration section.`
13
+ `GitHub configuration for host "${host}" is missing or incomplete. Please check the integrations configuration section.`
14
14
  );
15
15
  }
16
16
  if (enterprise && !githubConfig.token) {
@@ -1 +1 @@
1
- {"version":3,"file":"GithubUtils.cjs.js","sources":["../../src/utils/GithubUtils.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport {\n DefaultGithubCredentialsProvider,\n GithubCredentials,\n ScmIntegrations,\n} from '@backstage/integration';\n\nexport type CopilotCredentials = {\n enterprise?: GithubCredentials;\n organization?: GithubCredentials;\n};\n\nexport type CopilotConfig = {\n host: string;\n enterprise?: string;\n organization?: string;\n apiBaseUrl: string;\n};\n\nexport const getCopilotConfig = (config: Config): CopilotConfig => {\n const host = config.getString('copilot.host');\n const enterprise = config.getOptionalString('copilot.enterprise');\n const organization = config.getOptionalString('copilot.organization');\n\n const integrations = ScmIntegrations.fromConfig(config);\n\n const githubConfig = integrations.github.byHost(host)?.config;\n\n if (!githubConfig) {\n throw new Error(\n `GitHub configuration for host \"${host}\" is missing or incomplete. Please check the integretions configuration section.`,\n );\n }\n\n if (enterprise && !githubConfig.token) {\n throw new Error(\n `Enterprise API for copilot only works with \"classic PAT\" tokens. No token is configured for \"${host}\" in the config.`,\n );\n }\n\n if (organization && !(githubConfig.token || githubConfig.apps)) {\n throw new Error(\n `Organization API for copilot works with both classic and fine grained PAT tokens or GitHub apps. No token or app is configured for \"${host}\" in the config.`,\n );\n }\n\n return {\n host,\n enterprise,\n organization,\n apiBaseUrl: githubConfig.apiBaseUrl ?? 'https://api.github.com',\n };\n};\n\nexport const getGithubCredentials = async (\n config: Config,\n copilotConfig: CopilotConfig,\n): Promise<CopilotCredentials> => {\n const integrations = ScmIntegrations.fromConfig(config);\n const { host, enterprise, organization } = copilotConfig;\n\n const githubConfig = integrations.github.byHost(host)?.config;\n\n if (!githubConfig) {\n throw new Error(\n `GitHub configuration for host \"${host}\" is missing or incomplete.`,\n );\n }\n\n const credentials: CopilotCredentials = {\n enterprise: undefined,\n organization: undefined,\n };\n\n if (enterprise) {\n if (!githubConfig.token) {\n throw new Error(\n `Enterprise API for copilot only works with \"classic PAT\" tokens. No token is configured for \"${host}\" in the config.`,\n );\n } else {\n credentials.enterprise = {\n type: 'token',\n headers: { Authorization: `Bearer ${githubConfig.token}` },\n token: githubConfig.token,\n };\n }\n }\n\n if (organization) {\n if (githubConfig.apps) {\n const githubCredentialsProvider =\n DefaultGithubCredentialsProvider.fromIntegrations(integrations);\n\n credentials.organization = await githubCredentialsProvider.getCredentials(\n {\n url: `https://${host}/${organization}`,\n },\n );\n } else if (githubConfig.token) {\n credentials.organization = {\n type: 'token',\n headers: { Authorization: `Bearer ${githubConfig.token}` },\n token: githubConfig.token,\n };\n } else {\n throw new Error(\n `Organization API for copilot works with both classic and fine grained PAT tokens or GitHub apps. No token or app is configured for \"${host}\" in the config.`,\n );\n }\n }\n\n return credentials;\n};\n"],"names":["ScmIntegrations","DefaultGithubCredentialsProvider"],"mappings":";;;;AAmCa,MAAA,gBAAA,GAAmB,CAAC,MAAkC,KAAA;AACjE,EAAM,MAAA,IAAA,GAAO,MAAO,CAAA,SAAA,CAAU,cAAc,CAAA;AAC5C,EAAM,MAAA,UAAA,GAAa,MAAO,CAAA,iBAAA,CAAkB,oBAAoB,CAAA;AAChE,EAAM,MAAA,YAAA,GAAe,MAAO,CAAA,iBAAA,CAAkB,sBAAsB,CAAA;AAEpE,EAAM,MAAA,YAAA,GAAeA,2BAAgB,CAAA,UAAA,CAAW,MAAM,CAAA;AAEtD,EAAA,MAAM,YAAe,GAAA,YAAA,CAAa,MAAO,CAAA,MAAA,CAAO,IAAI,CAAG,EAAA,MAAA;AAEvD,EAAA,IAAI,CAAC,YAAc,EAAA;AACjB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,kCAAkC,IAAI,CAAA,gFAAA;AAAA,KACxC;AAAA;AAGF,EAAI,IAAA,UAAA,IAAc,CAAC,YAAA,CAAa,KAAO,EAAA;AACrC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,gGAAgG,IAAI,CAAA,gBAAA;AAAA,KACtG;AAAA;AAGF,EAAA,IAAI,YAAgB,IAAA,EAAE,YAAa,CAAA,KAAA,IAAS,aAAa,IAAO,CAAA,EAAA;AAC9D,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,uIAAuI,IAAI,CAAA,gBAAA;AAAA,KAC7I;AAAA;AAGF,EAAO,OAAA;AAAA,IACL,IAAA;AAAA,IACA,UAAA;AAAA,IACA,YAAA;AAAA,IACA,UAAA,EAAY,aAAa,UAAc,IAAA;AAAA,GACzC;AACF;AAEa,MAAA,oBAAA,GAAuB,OAClC,MAAA,EACA,aACgC,KAAA;AAChC,EAAM,MAAA,YAAA,GAAeA,2BAAgB,CAAA,UAAA,CAAW,MAAM,CAAA;AACtD,EAAA,MAAM,EAAE,IAAA,EAAM,UAAY,EAAA,YAAA,EAAiB,GAAA,aAAA;AAE3C,EAAA,MAAM,YAAe,GAAA,YAAA,CAAa,MAAO,CAAA,MAAA,CAAO,IAAI,CAAG,EAAA,MAAA;AAEvD,EAAA,IAAI,CAAC,YAAc,EAAA;AACjB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,kCAAkC,IAAI,CAAA,2BAAA;AAAA,KACxC;AAAA;AAGF,EAAA,MAAM,WAAkC,GAAA;AAAA,IACtC,UAAY,EAAA,KAAA,CAAA;AAAA,IACZ,YAAc,EAAA,KAAA;AAAA,GAChB;AAEA,EAAA,IAAI,UAAY,EAAA;AACd,IAAI,IAAA,CAAC,aAAa,KAAO,EAAA;AACvB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,gGAAgG,IAAI,CAAA,gBAAA;AAAA,OACtG;AAAA,KACK,MAAA;AACL,MAAA,WAAA,CAAY,UAAa,GAAA;AAAA,QACvB,IAAM,EAAA,OAAA;AAAA,QACN,SAAS,EAAE,aAAA,EAAe,CAAU,OAAA,EAAA,YAAA,CAAa,KAAK,CAAG,CAAA,EAAA;AAAA,QACzD,OAAO,YAAa,CAAA;AAAA,OACtB;AAAA;AACF;AAGF,EAAA,IAAI,YAAc,EAAA;AAChB,IAAA,IAAI,aAAa,IAAM,EAAA;AACrB,MAAM,MAAA,yBAAA,GACJC,4CAAiC,CAAA,gBAAA,CAAiB,YAAY,CAAA;AAEhE,MAAY,WAAA,CAAA,YAAA,GAAe,MAAM,yBAA0B,CAAA,cAAA;AAAA,QACzD;AAAA,UACE,GAAK,EAAA,CAAA,QAAA,EAAW,IAAI,CAAA,CAAA,EAAI,YAAY,CAAA;AAAA;AACtC,OACF;AAAA,KACF,MAAA,IAAW,aAAa,KAAO,EAAA;AAC7B,MAAA,WAAA,CAAY,YAAe,GAAA;AAAA,QACzB,IAAM,EAAA,OAAA;AAAA,QACN,SAAS,EAAE,aAAA,EAAe,CAAU,OAAA,EAAA,YAAA,CAAa,KAAK,CAAG,CAAA,EAAA;AAAA,QACzD,OAAO,YAAa,CAAA;AAAA,OACtB;AAAA,KACK,MAAA;AACL,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,uIAAuI,IAAI,CAAA,gBAAA;AAAA,OAC7I;AAAA;AACF;AAGF,EAAO,OAAA,WAAA;AACT;;;;;"}
1
+ {"version":3,"file":"GithubUtils.cjs.js","sources":["../../src/utils/GithubUtils.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport {\n DefaultGithubCredentialsProvider,\n GithubCredentials,\n ScmIntegrations,\n} from '@backstage/integration';\n\nexport type CopilotCredentials = {\n enterprise?: GithubCredentials;\n organization?: GithubCredentials;\n};\n\nexport type CopilotConfig = {\n host: string;\n enterprise?: string;\n organization?: string;\n apiBaseUrl: string;\n};\n\nexport const getCopilotConfig = (config: Config): CopilotConfig => {\n const host = config.getString('copilot.host');\n const enterprise = config.getOptionalString('copilot.enterprise');\n const organization = config.getOptionalString('copilot.organization');\n\n const integrations = ScmIntegrations.fromConfig(config);\n\n const githubConfig = integrations.github.byHost(host)?.config;\n\n if (!githubConfig) {\n throw new Error(\n `GitHub configuration for host \"${host}\" is missing or incomplete. Please check the integrations configuration section.`,\n );\n }\n\n if (enterprise && !githubConfig.token) {\n throw new Error(\n `Enterprise API for copilot only works with \"classic PAT\" tokens. No token is configured for \"${host}\" in the config.`,\n );\n }\n\n if (organization && !(githubConfig.token || githubConfig.apps)) {\n throw new Error(\n `Organization API for copilot works with both classic and fine grained PAT tokens or GitHub apps. No token or app is configured for \"${host}\" in the config.`,\n );\n }\n\n return {\n host,\n enterprise,\n organization,\n apiBaseUrl: githubConfig.apiBaseUrl ?? 'https://api.github.com',\n };\n};\n\nexport const getGithubCredentials = async (\n config: Config,\n copilotConfig: CopilotConfig,\n): Promise<CopilotCredentials> => {\n const integrations = ScmIntegrations.fromConfig(config);\n const { host, enterprise, organization } = copilotConfig;\n\n const githubConfig = integrations.github.byHost(host)?.config;\n\n if (!githubConfig) {\n throw new Error(\n `GitHub configuration for host \"${host}\" is missing or incomplete.`,\n );\n }\n\n const credentials: CopilotCredentials = {\n enterprise: undefined,\n organization: undefined,\n };\n\n if (enterprise) {\n if (!githubConfig.token) {\n throw new Error(\n `Enterprise API for copilot only works with \"classic PAT\" tokens. No token is configured for \"${host}\" in the config.`,\n );\n } else {\n credentials.enterprise = {\n type: 'token',\n headers: { Authorization: `Bearer ${githubConfig.token}` },\n token: githubConfig.token,\n };\n }\n }\n\n if (organization) {\n if (githubConfig.apps) {\n const githubCredentialsProvider =\n DefaultGithubCredentialsProvider.fromIntegrations(integrations);\n\n credentials.organization = await githubCredentialsProvider.getCredentials(\n {\n url: `https://${host}/${organization}`,\n },\n );\n } else if (githubConfig.token) {\n credentials.organization = {\n type: 'token',\n headers: { Authorization: `Bearer ${githubConfig.token}` },\n token: githubConfig.token,\n };\n } else {\n throw new Error(\n `Organization API for copilot works with both classic and fine grained PAT tokens or GitHub apps. No token or app is configured for \"${host}\" in the config.`,\n );\n }\n }\n\n return credentials;\n};\n"],"names":["ScmIntegrations","DefaultGithubCredentialsProvider"],"mappings":"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"}
@@ -15,7 +15,7 @@ function filterBaseMetrics(metrics, type, team) {
15
15
  return metrics.map((metric) => ({
16
16
  day: metric.date,
17
17
  type,
18
- team_name: team,
18
+ team_name: team ?? "",
19
19
  total_engaged_users: metric.total_engaged_users,
20
20
  total_active_users: metric.total_active_users
21
21
  })).filter((metric) => metric.total_engaged_users > 0);
@@ -24,7 +24,7 @@ function filterIdeCompletionMetrics(metrics, type, team) {
24
24
  return metrics.map((metric) => ({
25
25
  day: metric.date,
26
26
  type,
27
- team_name: team,
27
+ team_name: team ?? "",
28
28
  total_engaged_users: metric.copilot_ide_code_completions.total_engaged_users
29
29
  })).filter((completion) => completion.total_engaged_users > 0);
30
30
  }
@@ -33,7 +33,7 @@ function filterIdeCompletionLanguageMetrics(metrics, type, team) {
33
33
  (metric) => metric.copilot_ide_code_completions.languages?.map((language) => ({
34
34
  day: metric.date,
35
35
  type,
36
- team_name: team,
36
+ team_name: team ?? "",
37
37
  language: language.name,
38
38
  total_engaged_users: language.total_engaged_users
39
39
  })) || []
@@ -44,7 +44,7 @@ function filterIdeCompletionEditorMetrics(metrics, type, team) {
44
44
  (metric) => metric.copilot_ide_code_completions.editors?.map((editor) => ({
45
45
  day: metric.date,
46
46
  type,
47
- team_name: team,
47
+ team_name: team ?? "",
48
48
  editor: editor.name,
49
49
  total_engaged_users: editor.total_engaged_users
50
50
  })) || []
@@ -56,7 +56,7 @@ function filterIdeCompletionEditorModelMetrics(metrics, type, team) {
56
56
  (editor) => editor.models.map((model) => ({
57
57
  day: metric.date,
58
58
  type,
59
- team_name: team,
59
+ team_name: team ?? "",
60
60
  editor: editor.name,
61
61
  model: model.name,
62
62
  total_engaged_users: model.total_engaged_users
@@ -71,7 +71,7 @@ function filterIdeCompletionEditorModelLanguageMetrics(metrics, type, team) {
71
71
  (model) => model.languages.map((language) => ({
72
72
  day: metric.date,
73
73
  type,
74
- team_name: team,
74
+ team_name: team ?? "",
75
75
  editor: editor.name,
76
76
  model: model.name,
77
77
  language: language.name,
@@ -89,7 +89,7 @@ function filterIdeChatMetrics(metrics, type, team) {
89
89
  return metrics.map((metric) => ({
90
90
  day: metric.date,
91
91
  type,
92
- team_name: team,
92
+ team_name: team ?? "",
93
93
  total_engaged_users: metric.copilot_ide_chat.total_engaged_users
94
94
  })).filter((chat) => chat.total_engaged_users > 0);
95
95
  }
@@ -98,7 +98,7 @@ function filterIdeEditorMetrics(metrics, type, team) {
98
98
  (metric) => metric.copilot_ide_chat.editors?.map((editor) => ({
99
99
  day: metric.date,
100
100
  type,
101
- team_name: team,
101
+ team_name: team ?? "",
102
102
  editor: editor.name,
103
103
  total_engaged_users: editor.total_engaged_users
104
104
  })) || []
@@ -110,7 +110,7 @@ function filterIdeChatEditorModelMetrics(metrics, type, team) {
110
110
  (editor) => editor.models.map((model) => ({
111
111
  day: metric.date,
112
112
  type,
113
- team_name: team,
113
+ team_name: team ?? "",
114
114
  editor: editor.name,
115
115
  model: model.name,
116
116
  total_engaged_users: model.total_engaged_users,
@@ -1 +1 @@
1
- {"version":3,"file":"metricHelpers.cjs.js","sources":["../../src/utils/metricHelpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { DateTime } from 'luxon';\nimport {\n CopilotIdeChatsDb,\n CopilotIdeChatsEditorModelDb,\n CopilotIdeChatsEditorsDb,\n CopilotIdeCodeCompletionsDb,\n CopilotIdeCodeCompletionsEditorModelLanguagesDb,\n CopilotIdeCodeCompletionsEditorModelsDb,\n CopilotIdeCodeCompletionsEditorsDb,\n CopilotIdeCodeCompletionsLanguageDb,\n CopilotMetricsDb,\n MetricDbRow,\n} from '../db/DatabaseHandler';\nimport {\n Metric,\n MetricsType,\n CopilotMetrics,\n CopilotSeats,\n SeatAnalysis,\n} from '@backstage-community/plugin-copilot-common';\n\nexport function filterNewMetricsV2(\n metrics: CopilotMetrics[],\n lastDay?: string,\n): CopilotMetrics[] {\n return metrics\n .sort(\n (a, b) =>\n DateTime.fromISO(a.date).toMillis() -\n DateTime.fromISO(b.date).toMillis(),\n )\n .filter(metric => {\n const metricDate = DateTime.fromISO(metric.date);\n\n const lastDayDate = lastDay\n ? DateTime.fromJSDate(new Date(lastDay))\n : null;\n\n return !lastDay || (lastDayDate?.isValid && metricDate > lastDayDate);\n });\n}\n\nexport function filterBaseMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotMetricsDb[] {\n return metrics\n .map(metric => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users: metric.total_engaged_users,\n total_active_users: metric.total_active_users,\n }))\n .filter(metric => metric.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsDb[] {\n return metrics\n .map(metric => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users:\n metric.copilot_ide_code_completions.total_engaged_users,\n }))\n .filter(completion => completion.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionLanguageMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsLanguageDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.languages?.map(language => ({\n day: metric.date,\n type: type,\n team_name: team,\n language: language.name,\n total_engaged_users: language.total_engaged_users,\n })) || [],\n )\n .filter(language => language.total_engaged_users > 0);\n}\nexport function filterIdeCompletionEditorMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.map(editor => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n total_engaged_users: editor.total_engaged_users,\n })) || [],\n )\n .filter(editor => editor.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionEditorModelMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorModelsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.flatMap(editor =>\n editor.models.map(model => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n total_engaged_users: model.total_engaged_users,\n })),\n ) || [],\n )\n .filter(model => model.total_engaged_users > 0);\n}\nexport function filterIdeCompletionEditorModelLanguageMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorModelLanguagesDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.flatMap(editor =>\n editor.models.flatMap(model =>\n model.languages.map(language => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n language: language.name,\n total_engaged_users: language.total_engaged_users,\n total_code_acceptances: language.total_code_acceptances,\n total_code_suggestions: language.total_code_suggestions,\n total_code_lines_accepted: language.total_code_lines_accepted,\n total_code_lines_suggested: language.total_code_lines_suggested,\n })),\n ),\n ) || [],\n )\n .filter(language => language.total_engaged_users > 0);\n}\n\nexport function filterIdeChatMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsDb[] {\n return metrics\n .map((metric: CopilotMetrics) => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users: metric.copilot_ide_chat.total_engaged_users,\n }))\n .filter(chat => chat.total_engaged_users > 0);\n}\n\nexport function filterIdeEditorMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsEditorsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_chat.editors?.map(editor => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n total_engaged_users: editor.total_engaged_users,\n })) || [],\n )\n .filter(editor => editor.total_engaged_users > 0);\n}\n\nexport function filterIdeChatEditorModelMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsEditorModelDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_chat.editors?.flatMap(editor =>\n editor.models.map(model => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n total_engaged_users: model.total_engaged_users,\n total_chat_copy_events: model.total_chat_copy_events,\n total_chats: model.total_chats,\n total_chat_insertion_events: model.total_chat_insertion_events,\n })),\n ) || [],\n )\n .filter(model => model.total_engaged_users > 0);\n}\n\nexport function prepareMetricsForInsert(\n metrics: Metric[],\n type: MetricsType,\n team_name?: string,\n): MetricDbRow[] {\n return metrics.map(({ breakdown, ...rest }) => ({\n ...rest,\n type,\n team_name,\n breakdown: JSON.stringify(breakdown),\n })) as MetricDbRow[];\n}\n\nexport function convertToSeatAnalysis(\n metrics: CopilotSeats,\n type: MetricsType,\n team?: string,\n): SeatAnalysis {\n const totalSeats = metrics.total_seats;\n const today = DateTime.now().startOf('day');\n\n // Count seats with no activity\n const seatsNeverUsed = metrics.seats.filter(\n seat => !seat.last_activity_at,\n ).length;\n\n // Count seats with no activity in the last 7, 14, and 28 days\n const seatsInactive7Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 7;\n }).length;\n\n const seatsInactive14Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 14;\n }).length;\n\n const seatsInactive28Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 28;\n }).length;\n\n return {\n day: today.toISODate(),\n type,\n team_name: team ?? '',\n total_seats: totalSeats,\n seats_never_used: seatsNeverUsed,\n seats_inactive_7_days: seatsInactive7Days,\n seats_inactive_14_days: seatsInactive14Days,\n seats_inactive_28_days: seatsInactive28Days,\n };\n}\n\nexport function convertToTeamSeatAnalysis(\n metrics: CopilotSeats,\n type: MetricsType,\n team: string,\n): SeatAnalysis {\n const teamSeatMetrics = metrics.seats.filter(\n seat => seat.assigning_team?.slug === team,\n );\n const teamMetrics = {\n total_seats: teamSeatMetrics.length,\n seats: teamSeatMetrics,\n };\n\n return convertToSeatAnalysis(teamMetrics, type, team);\n}\n"],"names":["DateTime"],"mappings":";;;;AAoCgB,SAAA,kBAAA,CACd,SACA,OACkB,EAAA;AAClB,EAAA,OAAO,OACJ,CAAA,IAAA;AAAA,IACC,CAAC,CAAA,EAAG,CACF,KAAAA,cAAA,CAAS,QAAQ,CAAE,CAAA,IAAI,CAAE,CAAA,QAAA,KACzBA,cAAS,CAAA,OAAA,CAAQ,CAAE,CAAA,IAAI,EAAE,QAAS;AAAA,GACtC,CACC,OAAO,CAAU,MAAA,KAAA;AAChB,IAAA,MAAM,UAAa,GAAAA,cAAA,CAAS,OAAQ,CAAA,MAAA,CAAO,IAAI,CAAA;AAE/C,IAAM,MAAA,WAAA,GAAc,UAChBA,cAAS,CAAA,UAAA,CAAW,IAAI,IAAK,CAAA,OAAO,CAAC,CACrC,GAAA,IAAA;AAEJ,IAAA,OAAO,CAAC,OAAA,IAAY,WAAa,EAAA,OAAA,IAAW,UAAa,GAAA,WAAA;AAAA,GAC1D,CAAA;AACL;AAEgB,SAAA,iBAAA,CACd,OACA,EAAA,IAAA,EACA,IACoB,EAAA;AACpB,EAAO,OAAA,OAAA,CACJ,IAAI,CAAW,MAAA,MAAA;AAAA,IACd,KAAK,MAAO,CAAA,IAAA;AAAA,IACZ,IAAA;AAAA,IACA,SAAW,EAAA,IAAA;AAAA,IACX,qBAAqB,MAAO,CAAA,mBAAA;AAAA,IAC5B,oBAAoB,MAAO,CAAA;AAAA,IAC3B,CACD,CAAA,MAAA,CAAO,CAAU,MAAA,KAAA,MAAA,CAAO,sBAAsB,CAAC,CAAA;AACpD;AAEgB,SAAA,0BAAA,CACd,OACA,EAAA,IAAA,EACA,IAC+B,EAAA;AAC/B,EAAO,OAAA,OAAA,CACJ,IAAI,CAAW,MAAA,MAAA;AAAA,IACd,KAAK,MAAO,CAAA,IAAA;AAAA,IACZ,IAAA;AAAA,IACA,SAAW,EAAA,IAAA;AAAA,IACX,mBAAA,EACE,OAAO,4BAA6B,CAAA;AAAA,IACtC,CACD,CAAA,MAAA,CAAO,CAAc,UAAA,KAAA,UAAA,CAAW,sBAAsB,CAAC,CAAA;AAC5D;AAEgB,SAAA,kCAAA,CACd,OACA,EAAA,IAAA,EACA,IACuC,EAAA;AACvC,EAAA,OAAO,OACJ,CAAA,OAAA;AAAA,IACC,CAAC,MACC,KAAA,MAAA,CAAO,4BAA6B,CAAA,SAAA,EAAW,IAAI,CAAa,QAAA,MAAA;AAAA,MAC9D,KAAK,MAAO,CAAA,IAAA;AAAA,MACZ,IAAA;AAAA,MACA,SAAW,EAAA,IAAA;AAAA,MACX,UAAU,QAAS,CAAA,IAAA;AAAA,MACnB,qBAAqB,QAAS,CAAA;AAAA,KAChC,CAAE,KAAK;AAAC,GAEX,CAAA,MAAA,CAAO,CAAY,QAAA,KAAA,QAAA,CAAS,sBAAsB,CAAC,CAAA;AACxD;AACgB,SAAA,gCAAA,CACd,OACA,EAAA,IAAA,EACA,IACsC,EAAA;AACtC,EAAA,OAAO,OACJ,CAAA,OAAA;AAAA,IACC,CAAC,MACC,KAAA,MAAA,CAAO,4BAA6B,CAAA,OAAA,EAAS,IAAI,CAAW,MAAA,MAAA;AAAA,MAC1D,KAAK,MAAO,CAAA,IAAA;AAAA,MACZ,IAAA;AAAA,MACA,SAAW,EAAA,IAAA;AAAA,MACX,QAAQ,MAAO,CAAA,IAAA;AAAA,MACf,qBAAqB,MAAO,CAAA;AAAA,KAC9B,CAAE,KAAK;AAAC,GAEX,CAAA,MAAA,CAAO,CAAU,MAAA,KAAA,MAAA,CAAO,sBAAsB,CAAC,CAAA;AACpD;AAEgB,SAAA,qCAAA,CACd,OACA,EAAA,IAAA,EACA,IAC2C,EAAA;AAC3C,EAAA,OAAO,OACJ,CAAA,OAAA;AAAA,IACC,CAAC,MAAA,KACC,MAAO,CAAA,4BAAA,CAA6B,OAAS,EAAA,OAAA;AAAA,MAAQ,CACnD,MAAA,KAAA,MAAA,CAAO,MAAO,CAAA,GAAA,CAAI,CAAU,KAAA,MAAA;AAAA,QAC1B,KAAK,MAAO,CAAA,IAAA;AAAA,QACZ,IAAA;AAAA,QACA,SAAW,EAAA,IAAA;AAAA,QACX,QAAQ,MAAO,CAAA,IAAA;AAAA,QACf,OAAO,KAAM,CAAA,IAAA;AAAA,QACb,qBAAqB,KAAM,CAAA;AAAA,OAC3B,CAAA;AAAA,SACC;AAAC,GAET,CAAA,MAAA,CAAO,CAAS,KAAA,KAAA,KAAA,CAAM,sBAAsB,CAAC,CAAA;AAClD;AACgB,SAAA,6CAAA,CACd,OACA,EAAA,IAAA,EACA,IACmD,EAAA;AACnD,EAAA,OAAO,OACJ,CAAA,OAAA;AAAA,IACC,CAAC,MAAA,KACC,MAAO,CAAA,4BAAA,CAA6B,OAAS,EAAA,OAAA;AAAA,MAAQ,CAAA,MAAA,KACnD,OAAO,MAAO,CAAA,OAAA;AAAA,QAAQ,CACpB,KAAA,KAAA,KAAA,CAAM,SAAU,CAAA,GAAA,CAAI,CAAa,QAAA,MAAA;AAAA,UAC/B,KAAK,MAAO,CAAA,IAAA;AAAA,UACZ,IAAA;AAAA,UACA,SAAW,EAAA,IAAA;AAAA,UACX,QAAQ,MAAO,CAAA,IAAA;AAAA,UACf,OAAO,KAAM,CAAA,IAAA;AAAA,UACb,UAAU,QAAS,CAAA,IAAA;AAAA,UACnB,qBAAqB,QAAS,CAAA,mBAAA;AAAA,UAC9B,wBAAwB,QAAS,CAAA,sBAAA;AAAA,UACjC,wBAAwB,QAAS,CAAA,sBAAA;AAAA,UACjC,2BAA2B,QAAS,CAAA,yBAAA;AAAA,UACpC,4BAA4B,QAAS,CAAA;AAAA,SACrC,CAAA;AAAA;AACJ,SACG;AAAC,GAET,CAAA,MAAA,CAAO,CAAY,QAAA,KAAA,QAAA,CAAS,sBAAsB,CAAC,CAAA;AACxD;AAEgB,SAAA,oBAAA,CACd,OACA,EAAA,IAAA,EACA,IACqB,EAAA;AACrB,EAAO,OAAA,OAAA,CACJ,GAAI,CAAA,CAAC,MAA4B,MAAA;AAAA,IAChC,KAAK,MAAO,CAAA,IAAA;AAAA,IACZ,IAAA;AAAA,IACA,SAAW,EAAA,IAAA;AAAA,IACX,mBAAA,EAAqB,OAAO,gBAAiB,CAAA;AAAA,IAC7C,CACD,CAAA,MAAA,CAAO,CAAQ,IAAA,KAAA,IAAA,CAAK,sBAAsB,CAAC,CAAA;AAChD;AAEgB,SAAA,sBAAA,CACd,OACA,EAAA,IAAA,EACA,IAC4B,EAAA;AAC5B,EAAA,OAAO,OACJ,CAAA,OAAA;AAAA,IACC,CAAC,MACC,KAAA,MAAA,CAAO,gBAAiB,CAAA,OAAA,EAAS,IAAI,CAAW,MAAA,MAAA;AAAA,MAC9C,KAAK,MAAO,CAAA,IAAA;AAAA,MACZ,IAAA;AAAA,MACA,SAAW,EAAA,IAAA;AAAA,MACX,QAAQ,MAAO,CAAA,IAAA;AAAA,MACf,qBAAqB,MAAO,CAAA;AAAA,KAC9B,CAAE,KAAK;AAAC,GAEX,CAAA,MAAA,CAAO,CAAU,MAAA,KAAA,MAAA,CAAO,sBAAsB,CAAC,CAAA;AACpD;AAEgB,SAAA,+BAAA,CACd,OACA,EAAA,IAAA,EACA,IACgC,EAAA;AAChC,EAAA,OAAO,OACJ,CAAA,OAAA;AAAA,IACC,CAAC,MAAA,KACC,MAAO,CAAA,gBAAA,CAAiB,OAAS,EAAA,OAAA;AAAA,MAAQ,CACvC,MAAA,KAAA,MAAA,CAAO,MAAO,CAAA,GAAA,CAAI,CAAU,KAAA,MAAA;AAAA,QAC1B,KAAK,MAAO,CAAA,IAAA;AAAA,QACZ,IAAA;AAAA,QACA,SAAW,EAAA,IAAA;AAAA,QACX,QAAQ,MAAO,CAAA,IAAA;AAAA,QACf,OAAO,KAAM,CAAA,IAAA;AAAA,QACb,qBAAqB,KAAM,CAAA,mBAAA;AAAA,QAC3B,wBAAwB,KAAM,CAAA,sBAAA;AAAA,QAC9B,aAAa,KAAM,CAAA,WAAA;AAAA,QACnB,6BAA6B,KAAM,CAAA;AAAA,OACnC,CAAA;AAAA,SACC;AAAC,GAET,CAAA,MAAA,CAAO,CAAS,KAAA,KAAA,KAAA,CAAM,sBAAsB,CAAC,CAAA;AAClD;AAegB,SAAA,qBAAA,CACd,OACA,EAAA,IAAA,EACA,IACc,EAAA;AACd,EAAA,MAAM,aAAa,OAAQ,CAAA,WAAA;AAC3B,EAAA,MAAM,KAAQ,GAAAA,cAAA,CAAS,GAAI,EAAA,CAAE,QAAQ,KAAK,CAAA;AAG1C,EAAM,MAAA,cAAA,GAAiB,QAAQ,KAAM,CAAA,MAAA;AAAA,IACnC,CAAA,IAAA,KAAQ,CAAC,IAAK,CAAA;AAAA,GACd,CAAA,MAAA;AAGF,EAAA,MAAM,kBAAqB,GAAA,OAAA,CAAQ,KAAM,CAAA,MAAA,CAAO,CAAQ,IAAA,KAAA;AACtD,IAAI,IAAA,CAAC,IAAK,CAAA,gBAAA,EAAyB,OAAA,KAAA;AACnC,IAAA,MAAM,gBAAmB,GAAAA,cAAA,CAAS,OAAQ,CAAA,IAAA,CAAK,gBAAgB,CAAA;AAC/D,IAAA,OAAO,KAAM,CAAA,IAAA,CAAK,gBAAkB,EAAA,MAAM,EAAE,IAAQ,IAAA,CAAA;AAAA,GACrD,CAAE,CAAA,MAAA;AAEH,EAAA,MAAM,mBAAsB,GAAA,OAAA,CAAQ,KAAM,CAAA,MAAA,CAAO,CAAQ,IAAA,KAAA;AACvD,IAAI,IAAA,CAAC,IAAK,CAAA,gBAAA,EAAyB,OAAA,KAAA;AACnC,IAAA,MAAM,gBAAmB,GAAAA,cAAA,CAAS,OAAQ,CAAA,IAAA,CAAK,gBAAgB,CAAA;AAC/D,IAAA,OAAO,KAAM,CAAA,IAAA,CAAK,gBAAkB,EAAA,MAAM,EAAE,IAAQ,IAAA,EAAA;AAAA,GACrD,CAAE,CAAA,MAAA;AAEH,EAAA,MAAM,mBAAsB,GAAA,OAAA,CAAQ,KAAM,CAAA,MAAA,CAAO,CAAQ,IAAA,KAAA;AACvD,IAAI,IAAA,CAAC,IAAK,CAAA,gBAAA,EAAyB,OAAA,KAAA;AACnC,IAAA,MAAM,gBAAmB,GAAAA,cAAA,CAAS,OAAQ,CAAA,IAAA,CAAK,gBAAgB,CAAA;AAC/D,IAAA,OAAO,KAAM,CAAA,IAAA,CAAK,gBAAkB,EAAA,MAAM,EAAE,IAAQ,IAAA,EAAA;AAAA,GACrD,CAAE,CAAA,MAAA;AAEH,EAAO,OAAA;AAAA,IACL,GAAA,EAAK,MAAM,SAAU,EAAA;AAAA,IACrB,IAAA;AAAA,IACA,WAAW,IAAQ,IAAA,EAAA;AAAA,IACnB,WAAa,EAAA,UAAA;AAAA,IACb,gBAAkB,EAAA,cAAA;AAAA,IAClB,qBAAuB,EAAA,kBAAA;AAAA,IACvB,sBAAwB,EAAA,mBAAA;AAAA,IACxB,sBAAwB,EAAA;AAAA,GAC1B;AACF;AAEgB,SAAA,yBAAA,CACd,OACA,EAAA,IAAA,EACA,IACc,EAAA;AACd,EAAM,MAAA,eAAA,GAAkB,QAAQ,KAAM,CAAA,MAAA;AAAA,IACpC,CAAA,IAAA,KAAQ,IAAK,CAAA,cAAA,EAAgB,IAAS,KAAA;AAAA,GACxC;AACA,EAAA,MAAM,WAAc,GAAA;AAAA,IAClB,aAAa,eAAgB,CAAA,MAAA;AAAA,IAC7B,KAAO,EAAA;AAAA,GACT;AAEA,EAAO,OAAA,qBAAA,CAAsB,WAAa,EAAA,IAAA,EAAM,IAAI,CAAA;AACtD;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"metricHelpers.cjs.js","sources":["../../src/utils/metricHelpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { DateTime } from 'luxon';\nimport {\n CopilotIdeChatsDb,\n CopilotIdeChatsEditorModelDb,\n CopilotIdeChatsEditorsDb,\n CopilotIdeCodeCompletionsDb,\n CopilotIdeCodeCompletionsEditorModelLanguagesDb,\n CopilotIdeCodeCompletionsEditorModelsDb,\n CopilotIdeCodeCompletionsEditorsDb,\n CopilotIdeCodeCompletionsLanguageDb,\n CopilotMetricsDb,\n} from '../db/DatabaseHandler';\nimport {\n MetricsType,\n CopilotMetrics,\n CopilotSeats,\n SeatAnalysis,\n} from '@backstage-community/plugin-copilot-common';\n\nexport function filterNewMetricsV2(\n metrics: CopilotMetrics[],\n lastDay?: string,\n): CopilotMetrics[] {\n return metrics\n .sort(\n (a, b) =>\n DateTime.fromISO(a.date).toMillis() -\n DateTime.fromISO(b.date).toMillis(),\n )\n .filter(metric => {\n const metricDate = DateTime.fromISO(metric.date);\n\n const lastDayDate = lastDay\n ? DateTime.fromJSDate(new Date(lastDay))\n : null;\n\n return !lastDay || (lastDayDate?.isValid && metricDate > lastDayDate);\n });\n}\n\nexport function filterBaseMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotMetricsDb[] {\n return metrics\n .map(metric => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n total_engaged_users: metric.total_engaged_users,\n total_active_users: metric.total_active_users,\n }))\n .filter(metric => metric.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsDb[] {\n return metrics\n .map(metric => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n total_engaged_users:\n metric.copilot_ide_code_completions.total_engaged_users,\n }))\n .filter(completion => completion.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionLanguageMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsLanguageDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.languages?.map(language => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n language: language.name,\n total_engaged_users: language.total_engaged_users,\n })) || [],\n )\n .filter(language => language.total_engaged_users > 0);\n}\nexport function filterIdeCompletionEditorMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.map(editor => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n editor: editor.name,\n total_engaged_users: editor.total_engaged_users,\n })) || [],\n )\n .filter(editor => editor.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionEditorModelMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorModelsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.flatMap(editor =>\n editor.models.map(model => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n editor: editor.name,\n model: model.name,\n total_engaged_users: model.total_engaged_users,\n })),\n ) || [],\n )\n .filter(model => model.total_engaged_users > 0);\n}\nexport function filterIdeCompletionEditorModelLanguageMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorModelLanguagesDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.flatMap(editor =>\n editor.models.flatMap(model =>\n model.languages.map(language => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n editor: editor.name,\n model: model.name,\n language: language.name,\n total_engaged_users: language.total_engaged_users,\n total_code_acceptances: language.total_code_acceptances,\n total_code_suggestions: language.total_code_suggestions,\n total_code_lines_accepted: language.total_code_lines_accepted,\n total_code_lines_suggested: language.total_code_lines_suggested,\n })),\n ),\n ) || [],\n )\n .filter(language => language.total_engaged_users > 0);\n}\n\nexport function filterIdeChatMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsDb[] {\n return metrics\n .map((metric: CopilotMetrics) => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n total_engaged_users: metric.copilot_ide_chat.total_engaged_users,\n }))\n .filter(chat => chat.total_engaged_users > 0);\n}\n\nexport function filterIdeEditorMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsEditorsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_chat.editors?.map(editor => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n editor: editor.name,\n total_engaged_users: editor.total_engaged_users,\n })) || [],\n )\n .filter(editor => editor.total_engaged_users > 0);\n}\n\nexport function filterIdeChatEditorModelMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsEditorModelDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_chat.editors?.flatMap(editor =>\n editor.models.map(model => ({\n day: metric.date,\n type: type,\n team_name: team ?? '',\n editor: editor.name,\n model: model.name,\n total_engaged_users: model.total_engaged_users,\n total_chat_copy_events: model.total_chat_copy_events,\n total_chats: model.total_chats,\n total_chat_insertion_events: model.total_chat_insertion_events,\n })),\n ) || [],\n )\n .filter(model => model.total_engaged_users > 0);\n}\n\nexport function convertToSeatAnalysis(\n metrics: CopilotSeats,\n type: MetricsType,\n team?: string,\n): SeatAnalysis {\n const totalSeats = metrics.total_seats;\n const today = DateTime.now().startOf('day');\n\n // Count seats with no activity\n const seatsNeverUsed = metrics.seats.filter(\n seat => !seat.last_activity_at,\n ).length;\n\n // Count seats with no activity in the last 7, 14, and 28 days\n const seatsInactive7Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 7;\n }).length;\n\n const seatsInactive14Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 14;\n }).length;\n\n const seatsInactive28Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 28;\n }).length;\n\n return {\n day: today.toISODate(),\n type,\n team_name: team ?? '',\n total_seats: totalSeats,\n seats_never_used: seatsNeverUsed,\n seats_inactive_7_days: seatsInactive7Days,\n seats_inactive_14_days: seatsInactive14Days,\n seats_inactive_28_days: seatsInactive28Days,\n };\n}\n\nexport function convertToTeamSeatAnalysis(\n metrics: CopilotSeats,\n type: MetricsType,\n team: string,\n): SeatAnalysis {\n const teamSeatMetrics = metrics.seats.filter(\n seat => seat.assigning_team?.slug === team,\n );\n const teamMetrics = {\n total_seats: teamSeatMetrics.length,\n seats: teamSeatMetrics,\n };\n\n return convertToSeatAnalysis(teamMetrics, type, team);\n}\n"],"names":["DateTime"],"mappings":"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"}
@@ -0,0 +1,296 @@
1
+ /*
2
+ * Copyright 2024 The Backstage Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * @param {import('knex').Knex} knex
18
+ */
19
+ exports.up = async function up(knex) {
20
+ await deleteDuplicates('copilot_metrics', ['day', 'type']);
21
+
22
+ await deleteDuplicates('ide_completions', ['day', 'type']);
23
+
24
+ await deleteDuplicates('ide_completions_language_users', [
25
+ 'day',
26
+ 'language',
27
+ 'type',
28
+ ]);
29
+
30
+ await deleteDuplicates('ide_completions_language_editors', [
31
+ 'day',
32
+ 'editor',
33
+ 'type',
34
+ ]);
35
+
36
+ await deleteDuplicates('ide_completions_language_editors_model', [
37
+ 'day',
38
+ 'editor',
39
+ 'model',
40
+ 'type',
41
+ ]);
42
+
43
+ await deleteDuplicates('ide_completions_language_editors_model_language', [
44
+ 'day',
45
+ 'editor',
46
+ 'model',
47
+ 'language',
48
+ 'type',
49
+ ]);
50
+
51
+ await deleteDuplicates('ide_chats', ['day', 'type']);
52
+
53
+ await deleteDuplicates('ide_chat_editors', ['day', 'editor', 'type']);
54
+
55
+ await deleteDuplicates('ide_chat_editors_model', [
56
+ 'day',
57
+ 'editor',
58
+ 'model',
59
+ 'type',
60
+ ]);
61
+
62
+ await deleteDuplicates('dotcom_chats', ['day', 'type']);
63
+
64
+ await deleteDuplicates('dotcom_chat_models', ['day', 'model', 'type']);
65
+
66
+ await deleteDuplicates('dotcom_prs', ['day', 'type']);
67
+
68
+ await deleteDuplicates('dotcom_pr_repositories', [
69
+ 'day',
70
+ 'repository',
71
+ 'type',
72
+ ]);
73
+
74
+ await deleteDuplicates('dotcom_pr_repositories_models', [
75
+ 'day',
76
+ 'repository',
77
+ 'model',
78
+ 'type',
79
+ ]);
80
+
81
+ // Update all remaining null team_name values to empty strings
82
+ await knex('copilot_metrics')
83
+ .where('team_name', null)
84
+ .update({ team_name: '' });
85
+
86
+ await knex('ide_completions')
87
+ .where('team_name', null)
88
+ .update({ team_name: '' });
89
+
90
+ await knex('ide_completions_language_users')
91
+ .where('team_name', null)
92
+ .update({ team_name: '' });
93
+
94
+ await knex('ide_completions_language_editors')
95
+ .where('team_name', null)
96
+ .update({ team_name: '' });
97
+
98
+ await knex('ide_completions_language_editors_model')
99
+ .where('team_name', null)
100
+ .update({ team_name: '' });
101
+
102
+ await knex('ide_completions_language_editors_model_language')
103
+ .where('team_name', null)
104
+ .update({ team_name: '' });
105
+
106
+ await knex('ide_chats').where('team_name', null).update({ team_name: '' });
107
+
108
+ await knex('ide_chat_editors')
109
+ .where('team_name', null)
110
+ .update({ team_name: '' });
111
+
112
+ await knex('ide_chat_editors_model')
113
+ .where('team_name', null)
114
+ .update({ team_name: '' });
115
+
116
+ await knex('dotcom_chats').where('team_name', null).update({ team_name: '' });
117
+
118
+ await knex('dotcom_chat_models')
119
+ .where('team_name', null)
120
+ .update({ team_name: '' });
121
+
122
+ await knex('dotcom_prs').where('team_name', null).update({ team_name: '' });
123
+
124
+ await knex('dotcom_pr_repositories')
125
+ .where('team_name', null)
126
+ .update({ team_name: '' });
127
+
128
+ await knex('dotcom_pr_repositories_models')
129
+ .where('team_name', null)
130
+ .update({ team_name: '' });
131
+
132
+ // Finally, update all the team_name columns to not null
133
+ await knex.schema.table('copilot_metrics', table => {
134
+ table.string('team_name').notNullable().alter();
135
+ });
136
+ await knex.schema.table('ide_completions', table => {
137
+ table.string('team_name').notNullable().alter();
138
+ });
139
+ await knex.schema.table('ide_completions_language_users', table => {
140
+ table.string('team_name').notNullable().alter();
141
+ });
142
+ await knex.schema.table('ide_completions_language_editors', table => {
143
+ table.string('team_name').notNullable().alter();
144
+ });
145
+ await knex.schema.table('ide_completions_language_editors_model', table => {
146
+ table.string('team_name').notNullable().alter();
147
+ });
148
+ await knex.schema.table(
149
+ 'ide_completions_language_editors_model_language',
150
+ table => {
151
+ table.string('team_name').notNullable().alter();
152
+ },
153
+ );
154
+ await knex.schema.table('ide_chats', table => {
155
+ table.string('team_name').notNullable().alter();
156
+ });
157
+ await knex.schema.table('ide_chat_editors', table => {
158
+ table.string('team_name').notNullable().alter();
159
+ });
160
+ await knex.schema.table('ide_chat_editors_model', table => {
161
+ table.string('team_name').notNullable().alter();
162
+ });
163
+ await knex.schema.table('dotcom_chats', table => {
164
+ table.string('team_name').notNullable().alter();
165
+ });
166
+ await knex.schema.table('dotcom_chat_models', table => {
167
+ table.string('team_name').notNullable().alter();
168
+ });
169
+ await knex.schema.table('dotcom_prs', table => {
170
+ table.string('team_name').notNullable().alter();
171
+ });
172
+ await knex.schema.table('dotcom_pr_repositories', table => {
173
+ table.string('team_name').notNullable().alter();
174
+ });
175
+ await knex.schema.table('dotcom_pr_repositories_models', table => {
176
+ table.string('team_name').notNullable().alter();
177
+ });
178
+
179
+ // Delete duplicate rows keeping the one with highest total_engaged_users
180
+ // When there are ties (same total_engaged_users), keep only one row
181
+ async function deleteDuplicates(tableName, partitionColumns) {
182
+ const isSqlite = knex.client.config.client.includes('sqlite3');
183
+ const rowIdColumn = isSqlite ? 'rowid' : 'ctid';
184
+
185
+ const partitionBy = partitionColumns.join(', ');
186
+
187
+ await knex.raw(`
188
+ DELETE FROM ${tableName}
189
+ WHERE ${rowIdColumn} IN (
190
+ SELECT ${rowIdColumn}
191
+ FROM (
192
+ SELECT ${rowIdColumn},
193
+ ROW_NUMBER() OVER (
194
+ PARTITION BY ${partitionBy}
195
+ ORDER BY total_engaged_users DESC, ${rowIdColumn}
196
+ ) as row_num
197
+ FROM ${tableName}
198
+ WHERE team_name IS NULL
199
+ ) ranked
200
+ WHERE row_num > 1
201
+ )
202
+ `);
203
+ }
204
+ };
205
+
206
+ /**
207
+ * @param {import('knex').Knex} knex
208
+ */
209
+ exports.down = async function down(knex) {
210
+ // a reverse migration is to update all the team_name columns to nullable
211
+ await knex.schema.table('copilot_metrics', table => {
212
+ table.string('team_name').nullable().alter();
213
+ });
214
+ await knex.schema.table('ide_completions', table => {
215
+ table.string('team_name').nullable().alter();
216
+ });
217
+ await knex.schema.table('ide_completions_language_users', table => {
218
+ table.string('team_name').nullable().alter();
219
+ });
220
+ await knex.schema.table('ide_completions_language_editors', table => {
221
+ table.string('team_name').nullable().alter();
222
+ });
223
+ await knex.schema.table('ide_completions_language_editors_model', table => {
224
+ table.string('team_name').nullable().alter();
225
+ });
226
+ await knex.schema.table(
227
+ 'ide_completions_language_editors_model_language',
228
+ table => {
229
+ table.string('team_name').nullable().alter();
230
+ },
231
+ );
232
+ await knex.schema.table('ide_chats', table => {
233
+ table.string('team_name').nullable().alter();
234
+ });
235
+ await knex.schema.table('ide_chat_editors', table => {
236
+ table.string('team_name').nullable().alter();
237
+ });
238
+ await knex.schema.table('ide_chat_editors_model', table => {
239
+ table.string('team_name').nullable().alter();
240
+ });
241
+ await knex.schema.table('dotcom_chats', table => {
242
+ table.string('team_name').nullable().alter();
243
+ });
244
+ await knex.schema.table('dotcom_chat_models', table => {
245
+ table.string('team_name').nullable().alter();
246
+ });
247
+ await knex.schema.table('dotcom_prs', table => {
248
+ table.string('team_name').nullable().alter();
249
+ });
250
+ await knex.schema.table('dotcom_pr_repositories', table => {
251
+ table.string('team_name').nullable().alter();
252
+ });
253
+ await knex.schema.table('dotcom_pr_repositories_models', table => {
254
+ table.string('team_name').nullable().alter();
255
+ });
256
+
257
+ // Then update all the rows with team_name = '' to null
258
+ // This makes all duplicates gone, but the possibillity of having
259
+ // more duplicates until the next migration
260
+ await knex('copilot_metrics')
261
+ .where('team_name', '')
262
+ .update({ team_name: null });
263
+ await knex('ide_completions')
264
+ .where('team_name', '')
265
+ .update({ team_name: null });
266
+ await knex('ide_completions_language_users')
267
+ .where('team_name', '')
268
+ .update({ team_name: null });
269
+ await knex('ide_completions_language_editors')
270
+ .where('team_name', '')
271
+ .update({ team_name: null });
272
+ await knex('ide_completions_language_editors_model')
273
+ .where('team_name', '')
274
+ .update({ team_name: null });
275
+ await knex('ide_completions_language_editors_model_language')
276
+ .where('team_name', '')
277
+ .update({ team_name: null });
278
+ await knex('ide_chats').where('team_name', '').update({ team_name: null });
279
+ await knex('ide_chat_editors')
280
+ .where('team_name', '')
281
+ .update({ team_name: null });
282
+ await knex('ide_chat_editors_model')
283
+ .where('team_name', '')
284
+ .update({ team_name: null });
285
+ await knex('dotcom_chats').where('team_name', '').update({ team_name: null });
286
+ await knex('dotcom_chat_models')
287
+ .where('team_name', '')
288
+ .update({ team_name: null });
289
+ await knex('dotcom_prs').where('team_name', '').update({ team_name: null });
290
+ await knex('dotcom_pr_repositories')
291
+ .where('team_name', '')
292
+ .update({ team_name: null });
293
+ await knex('dotcom_pr_repositories_models')
294
+ .where('team_name', '')
295
+ .update({ team_name: null });
296
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage-community/plugin-copilot-backend",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "homepage": "https://backstage.io",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.cjs.js",