diff --git a/frontend/src/app/highcharts.theme.ts b/frontend/src/app/highcharts.theme.ts
index 1aa976e..04cf834 100644
--- a/frontend/src/app/highcharts.theme.ts
+++ b/frontend/src/app/highcharts.theme.ts
@@ -1,4 +1,4 @@
-import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
+import * as Highcharts from 'highcharts';
const tooltipHeaderFormat =
'
{point.key} |
';
@@ -36,7 +36,7 @@ const yAxisConfig: Highcharts.YAxisOptions = {
...xAxisConfig
};
-Highcharts.theme = {
+const theme: Highcharts.Options = {
colors: [
'var(--sys-primary)',
'var(--sys-secondary)',
@@ -49,7 +49,7 @@ Highcharts.theme = {
'var(--sys-on-error)'
],
chart: {
- backgroundColor: 'var(--sys-surface)',
+ backgroundColor: undefined, // 'var(--sys-surface)',
borderRadius: 16,
style: {
fontFamily: 'var(--sys-body-large-font)'
@@ -131,8 +131,7 @@ Highcharts.theme = {
color: 'var(--sys-on-surface)'
}
}
- },
- borderRadius: 4
+ }
},
separator: {
style: {
@@ -255,16 +254,18 @@ Highcharts.theme = {
pie: {
borderWidth: 0,
borderRadius: 4,
- dataLabels: { style: {
- font: 'var(--sys-label-large)',
- fontSize: '14px',
- opacity: 0.87,
- fontWeight: 'var(--sys-label-large-weight)',
- textOutline: 'none',
- },
- distance: 20,
- connectorWidth: 1,
- connectorColor: 'var(--sys-outline-variant)'
+ dataLabels: {
+ style: {
+ font: 'var(--sys-label-large)',
+ color: 'var(--sys-on-surface)',
+ fontSize: '14px',
+ opacity: 0.87,
+ fontWeight: 'var(--sys-label-large-weight)',
+ textOutline: 'none',
+ },
+ distance: 20,
+ connectorWidth: 1,
+ connectorColor: 'var(--sys-outline-variant)'
}
}
},
@@ -286,4 +287,4 @@ Highcharts.theme = {
enabled: false
}
};
-Highcharts.setOptions(Highcharts.theme);
+Highcharts.setOptions(theme);
diff --git a/frontend/src/app/main/copilot/copilot-dashboard/dashboard-card/dashboard-card-drilldown-bar-chart/dashboard-card-drilldown-bar-chart.component.ts b/frontend/src/app/main/copilot/copilot-dashboard/dashboard-card/dashboard-card-drilldown-bar-chart/dashboard-card-drilldown-bar-chart.component.ts
index e105e41..5886582 100644
--- a/frontend/src/app/main/copilot/copilot-dashboard/dashboard-card/dashboard-card-drilldown-bar-chart/dashboard-card-drilldown-bar-chart.component.ts
+++ b/frontend/src/app/main/copilot/copilot-dashboard/dashboard-card/dashboard-card-drilldown-bar-chart/dashboard-card-drilldown-bar-chart.component.ts
@@ -1,7 +1,8 @@
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
-import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
-import 'highcharts/es-modules/masters/modules/drilldown.src';
+import * as Highcharts from 'highcharts';
+import HC_drilldown from 'highcharts/modules/drilldown';
+HC_drilldown(Highcharts);
import { HighchartsChartModule } from 'highcharts-angular';
import { CommonModule } from '@angular/common';
import { HighchartsService } from '../../../../../services/highcharts.service';
diff --git a/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics-pie-chart/copilot-metrics-pie-chart.component.ts b/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics-pie-chart/copilot-metrics-pie-chart.component.ts
index bb08748..87384a7 100644
--- a/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics-pie-chart/copilot-metrics-pie-chart.component.ts
+++ b/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics-pie-chart/copilot-metrics-pie-chart.component.ts
@@ -1,6 +1,6 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, SimpleChanges } from '@angular/core';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges } from '@angular/core';
import { CopilotMetrics } from '../../../../services/metrics.service.interfaces';
-import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
+import * as Highcharts from 'highcharts';
import { HighchartsChartModule } from 'highcharts-angular';
import { HighchartsService } from '../../../../services/highcharts.service';
@@ -15,13 +15,10 @@ import { HighchartsService } from '../../../../services/highcharts.service';
// styleUrl: './copilot-metrics-ide-completion-pie-chart.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class CopilotMetricsPieChartComponent {
+export class CopilotMetricsPieChartComponent implements OnChanges {
Highcharts: typeof Highcharts = Highcharts;
@Input() metricsTotals?: CopilotMetrics;
chartOptions: Highcharts.Options = {
- chart: {
- type: 'pie'
- },
tooltip: {
headerFormat: '{series.name}
',
pointFormat: '{point.name}: ' +
@@ -33,212 +30,7 @@ export class CopilotMetricsPieChartComponent {
type: 'pie',
}],
drilldown: {
- series: [
- {
- type: 'pie',
- name: 'Chrome',
- id: 'Chrome',
- data: [
- [
- 'v97.0',
- 36.89
- ],
- [
- 'v96.0',
- 18.16
- ],
- [
- 'v95.0',
- 0.54
- ],
- [
- 'v94.0',
- 0.7
- ],
- [
- 'v93.0',
- 0.8
- ],
- [
- 'v92.0',
- 0.41
- ],
- [
- 'v91.0',
- 0.31
- ],
- [
- 'v90.0',
- 0.13
- ],
- [
- 'v89.0',
- 0.14
- ],
- [
- 'v88.0',
- 0.1
- ],
- [
- 'v87.0',
- 0.35
- ],
- [
- 'v86.0',
- 0.17
- ],
- [
- 'v85.0',
- 0.18
- ],
- [
- 'v84.0',
- 0.17
- ],
- [
- 'v83.0',
- 0.21
- ],
- [
- 'v81.0',
- 0.1
- ],
- [
- 'v80.0',
- 0.16
- ],
- [
- 'v79.0',
- 0.43
- ],
- [
- 'v78.0',
- 0.11
- ],
- [
- 'v76.0',
- 0.16
- ],
- [
- 'v75.0',
- 0.15
- ],
- [
- 'v72.0',
- 0.14
- ],
- [
- 'v70.0',
- 0.11
- ],
- [
- 'v69.0',
- 0.13
- ],
- [
- 'v56.0',
- 0.12
- ],
- [
- 'v49.0',
- 0.17
- ]
- ]
- },
- {
- type: 'pie',
- name: 'Safari',
- id: 'Safari',
- data: [
- [
- 'v15.3',
- 0.1
- ],
- [
- 'v15.2',
- 2.01
- ],
- [
- 'v15.1',
- 2.29
- ],
- [
- 'v15.0',
- 0.49
- ],
- [
- 'v14.1',
- 2.48
- ],
- [
- 'v14.0',
- 0.64
- ],
- [
- 'v13.1',
- 1.17
- ],
- [
- 'v13.0',
- 0.13
- ],
- [
- 'v12.1',
- 0.16
- ]
- ]
- },
- {
- type: 'pie',
- name: 'Edge',
- id: 'Edge',
- data: [
- [
- 'v97',
- 6.62
- ],
- [
- 'v96',
- 2.55
- ],
- [
- 'v95',
- 0.15
- ]
- ]
- },
- {
- type: 'pie',
- name: 'Firefox',
- id: 'Firefox',
- data: [
- [
- 'v96.0',
- 4.17
- ],
- [
- 'v95.0',
- 3.33
- ],
- [
- 'v94.0',
- 0.11
- ],
- [
- 'v91.0',
- 0.23
- ],
- [
- 'v78.0',
- 0.16
- ],
- [
- 'v52.0',
- 0.15
- ]
- ]
- }
- ]
+ series: []
}
};
_chartOptions?: Highcharts.Options;
@@ -247,18 +39,14 @@ export class CopilotMetricsPieChartComponent {
constructor(
private highchartsService: HighchartsService,
private cdr: ChangeDetectorRef
- ) {
- console.log(this.metricsTotals);
- }
+ ) { }
ngOnChanges() {
if (this.metricsTotals) {
- this._chartOptions = this.highchartsService.transformMetricsToPieDrilldown(this.metricsTotals);
this.chartOptions = {
...this.chartOptions,
- ...this._chartOptions
+ ...this.highchartsService.transformMetricsToPieDrilldown(this.metricsTotals)
};
- console.log('now', this.chartOptions);
this.updateFlag = true;
this.cdr.detectChanges();
}
diff --git a/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.html b/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.html
index b5f74c4..09b111b 100644
--- a/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.html
+++ b/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.html
@@ -2,11 +2,20 @@
-
-
-
+
+
+
-
+
+
+
+ IDE Completions
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.ts b/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.ts
index 4563985..c8e9a34 100644
--- a/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.ts
+++ b/frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.ts
@@ -3,16 +3,21 @@ import { DateRangeSelectComponent } from "../../../shared/date-range-select/date
import { MetricsService } from '../../../services/metrics.service';
import { CopilotMetrics } from '../../../services/metrics.service.interfaces';
import { CopilotMetricsPieChartComponent } from './copilot-metrics-pie-chart/copilot-metrics-pie-chart.component';
+import { MatCardModule } from '@angular/material/card';
@Component({
selector: 'app-metrics',
standalone: true,
imports: [
DateRangeSelectComponent,
- CopilotMetricsPieChartComponent
+ CopilotMetricsPieChartComponent,
+ MatCardModule
],
templateUrl: './copilot-metrics.component.html',
- styleUrl: './copilot-metrics.component.scss'
+ styleUrls: [
+ './copilot-metrics.component.scss',
+ '../copilot-dashboard/dashboard.component.scss'
+ ]
})
export class CopilotMetricsComponent {
metrics?: CopilotMetrics[];
@@ -23,17 +28,20 @@ export class CopilotMetricsComponent {
) { }
dateRangeChange(event: {start: Date, end: Date}) {
- console.log(event)
+ const utcStart = Date.UTC(event.start.getFullYear(), event.start.getMonth(), event.start.getDate());
+ const utcEnd = Date.UTC(event.end.getFullYear(), event.end.getMonth(), event.end.getDate());
+ const startModified = new Date(utcStart - 1);
+ const endModified = new Date(utcEnd + 1);
this.metricsService.getMetrics({
- since: event.start.toISOString(),
- until: event.end.toISOString()
+ since: startModified.toISOString(),
+ until: endModified.toISOString()
}).subscribe((metrics) => {
this.metrics = metrics;
console.log(metrics);
});
this.metricsService.getMetricsTotals({
- since: event.start.toISOString(),
- until: event.end.toISOString()
+ since: startModified.toISOString(),
+ until: endModified.toISOString()
}).subscribe((metricsTotals) => {
this.metricsTotals = metricsTotals;
console.log(metricsTotals);
diff --git a/frontend/src/app/main/copilot/copilot-seats/copilot-seat/copilot-seat.component.ts b/frontend/src/app/main/copilot/copilot-seats/copilot-seat/copilot-seat.component.ts
index 2eb5b96..e9d35ef 100644
--- a/frontend/src/app/main/copilot/copilot-seats/copilot-seat/copilot-seat.component.ts
+++ b/frontend/src/app/main/copilot/copilot-seats/copilot-seat/copilot-seat.component.ts
@@ -1,6 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
-import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
-import 'highcharts/es-modules/masters/modules/gantt.src';
+import * as Highcharts from 'highcharts';
+import HC_gantt from 'highcharts/modules/gantt';
+HC_gantt(Highcharts);
import { HighchartsChartModule } from 'highcharts-angular';
import { Seat, SeatService } from '../../../../services/seat.service';
import { ActivatedRoute } from '@angular/router';
diff --git a/frontend/src/app/main/copilot/copilot-surveys/copilot-surveys.component.ts b/frontend/src/app/main/copilot/copilot-surveys/copilot-surveys.component.ts
index eaa68e0..676365a 100644
--- a/frontend/src/app/main/copilot/copilot-surveys/copilot-surveys.component.ts
+++ b/frontend/src/app/main/copilot/copilot-surveys/copilot-surveys.component.ts
@@ -58,7 +58,7 @@ export class CopilotSurveysComponent implements OnInit {
}
}
- onSurveyClick(survey: any) {
+ onSurveyClick(survey: Survey) {
this.router.navigate(['/copilot/surveys', survey.id]);
}
}
diff --git a/frontend/src/app/main/copilot/copilot-value/adoption-chart/adoption-chart.component.html b/frontend/src/app/main/copilot/copilot-value/adoption-chart/adoption-chart.component.html
index 6f70c35..c9e1823 100644
--- a/frontend/src/app/main/copilot/copilot-value/adoption-chart/adoption-chart.component.html
+++ b/frontend/src/app/main/copilot/copilot-value/adoption-chart/adoption-chart.component.html
@@ -1,2 +1,2 @@
-
+
\ No newline at end of file
diff --git a/frontend/src/app/main/copilot/copilot-value/adoption-chart/adoption-chart.component.ts b/frontend/src/app/main/copilot/copilot-value/adoption-chart/adoption-chart.component.ts
index 1dbb1f4..87d3cc8 100644
--- a/frontend/src/app/main/copilot/copilot-value/adoption-chart/adoption-chart.component.ts
+++ b/frontend/src/app/main/copilot/copilot-value/adoption-chart/adoption-chart.component.ts
@@ -1,5 +1,5 @@
-import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
-import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
+import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
+import * as Highcharts from 'highcharts';
import { HighchartsChartModule } from 'highcharts-angular';
import { ActivityResponse } from '../../../../services/seat.service';
import { HighchartsService } from '../../../../services/highcharts.service';
@@ -17,12 +17,14 @@ import { DatePipe } from '@angular/common';
templateUrl: './adoption-chart.component.html',
styleUrl: './adoption-chart.component.scss'
})
-export class AdoptionChartComponent implements OnChanges {
+export class AdoptionChartComponent implements OnInit, OnChanges {
Highcharts: typeof Highcharts = Highcharts;
updateFlag = false;
totalUsers = 500;
@Input() data?: ActivityResponse;
- chartOptions: Highcharts.Options = {
+ @Input() stripped = false;
+ @Input() chartOptions?: Highcharts.Options;
+ notStrippedChartOptions: Highcharts.Options = {
chart: {
zooming: {
type: 'x'
@@ -71,84 +73,60 @@ export class AdoptionChartComponent implements OnChanges {
}
}
}]
- },
- tooltip: {
- // '{point.x:%b %d, %Y}
',
- headerFormat: '',
- pointFormatter: function () {
- const parts = [
- `${new DatePipe('en-US').transform(this.x)}
`,
- `${this.series.name}: `,
- `${(this as any).raw}`,
- `(${this.y?.toFixed(1)}%)`
- ]
- return parts.join('');
- },
- style: {
- fontSize: '14px'
+ }
+ }
+ _chartOptions: Highcharts.Options = {
+ yAxis: {
+ min: 0,
+ max: 100,
+ labels: {
+ enabled: false
}
},
series: [{
name: 'Users',
type: 'spline',
- data: [],
- lineWidth: 2,
- marker: {
- enabled: true,
- radius: 4,
- symbol: 'circle'
- },
- states: {
- hover: {
- lineWidth: 3
- }
- }
+ data: []
}],
+ tooltip: {
+ headerFormat: '',
+ },
legend: {
enabled: false
},
- plotOptions: {
- series: {
- animation: {
- duration: 300
- }
- }
- }
};
- _chartOptions?: Highcharts.Options;
+ @Output() chartInstanceChange = new EventEmitter();
+ strippedChartOptions: Highcharts.Options = JSON.parse(JSON.stringify(this._chartOptions));
+ charts: Highcharts.Chart[] = [];
constructor(
private highchartsService: HighchartsService,
- ) {
+ ) { }
+
+ ngOnInit() {
+ this._chartOptions = {
+ ...this._chartOptions,
+ ...this.chartOptions
+ }
+ this._chartOptions = this.stripped ? this.strippedChartOptions : {
+ ...this._chartOptions,
+ ...this.notStrippedChartOptions
+ };
}
ngOnChanges(changes: SimpleChanges) {
if (changes['data'] && this.data) {
- this._chartOptions = this.highchartsService.transformActivityMetricsToLine(this.data);
- this.setTooltipFormatter();
- this.chartOptions = {
- ...this.chartOptions,
- ...this._chartOptions
+ const options = this.highchartsService.transformActivityMetricsToLine(this.data);
+ this._chartOptions = {
+ ...this._chartOptions,
+ ...options,
+ tooltip: {
+ ...options.tooltip,
+ ...this._chartOptions.tooltip
+ }
};
this.updateFlag = true;
}
}
- setTooltipFormatter() {
- if (!this.data) return;
- const dateTimes = Object.keys(this.data);
- const isDaily = Math.abs(new Date(dateTimes[1]).getTime() - new Date(dateTimes[0]).getTime()) > 3600000;
- const dateFormat = isDaily ? undefined : 'short';
- console.log(isDaily, dateFormat);
- this.chartOptions.tooltip!.pointFormatter = function () {
- const parts = [
- `${new DatePipe('en-US').transform(this.x, dateFormat)}
`,
- `${this.series.name}: `,
- `${(this as any).raw}`,
- `(${this.y?.toFixed(1)}%)`
- ]
- return parts.join('');
- };
- }
-
}
diff --git a/frontend/src/app/main/copilot/copilot-value/daily-activity-chart/daily-activity-chart.component.html b/frontend/src/app/main/copilot/copilot-value/daily-activity-chart/daily-activity-chart.component.html
index c3c2617..6682a80 100644
--- a/frontend/src/app/main/copilot/copilot-value/daily-activity-chart/daily-activity-chart.component.html
+++ b/frontend/src/app/main/copilot/copilot-value/daily-activity-chart/daily-activity-chart.component.html
@@ -1,2 +1,2 @@
-
+
diff --git a/frontend/src/app/main/copilot/copilot-value/daily-activity-chart/daily-activity-chart.component.ts b/frontend/src/app/main/copilot/copilot-value/daily-activity-chart/daily-activity-chart.component.ts
index 8dddf9f..7e5c5a8 100644
--- a/frontend/src/app/main/copilot/copilot-value/daily-activity-chart/daily-activity-chart.component.ts
+++ b/frontend/src/app/main/copilot/copilot-value/daily-activity-chart/daily-activity-chart.component.ts
@@ -1,5 +1,5 @@
-import { Component, Input, OnChanges } from '@angular/core';
-import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
+import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
+import * as Highcharts from 'highcharts';
import { HighchartsChartModule } from 'highcharts-angular';
import { ActivityResponse } from '../../../../services/seat.service';
import { CopilotMetrics } from '../../../../services/metrics.service.interfaces';
@@ -14,38 +14,19 @@ import { HighchartsService } from '../../../../services/highcharts.service';
templateUrl: './daily-activity-chart.component.html',
styleUrl: './daily-activity-chart.component.scss'
})
-export class DailyActivityChartComponent implements OnChanges {
+export class DailyActivityChartComponent implements OnInit, OnChanges {
Highcharts: typeof Highcharts = Highcharts;
updateFlag = false;
- totalUsers = 500;
@Input() activity?: ActivityResponse;
@Input() metrics?: CopilotMetrics[];
-
- chartOptions: Highcharts.Options = {
- chart: {
- zooming: {
- type: 'x'
- },
- width: undefined,
- },
- xAxis: {
- type: 'datetime',
- dateTimeLabelFormats: {
- // don't display the year
- month: '%b',
- year: '%b'
- },
- crosshair: true
- },
+ @Input() chartOptions?: Highcharts.Options;
+ @Output() chartInstanceChange = new EventEmitter();
+ _chartOptions: Highcharts.Options = {
yAxis: {
title: {
text: 'Average Activity Per User'
},
min: 0,
- // max: 100,
- // labels: {
- // format: '{value}%'
- // },
plotBands: [{
from: 500,
to: 750,
@@ -97,30 +78,27 @@ export class DailyActivityChartComponent implements OnChanges {
name: '.COM Pull Requests',
type: 'spline',
}],
- // legend: {
- // enabled: false
- // },
- plotOptions: {
- series: {
- animation: {
- duration: 300
- }
- }
+ legend: {
+ enabled: true
}
};
- _chartOptions?: Highcharts.Options;
constructor(
private highchartsService: HighchartsService
- ) {
- }
+ ) { }
+ ngOnInit() {
+ this._chartOptions = {
+ ...this._chartOptions,
+ ...this.chartOptions
+ }
+ }
+
ngOnChanges() {
if (this.activity && this.metrics) {
- this._chartOptions = this.highchartsService.transformMetricsToDailyActivityLine(this.activity, this.metrics);
- this.chartOptions = {
- ...this.chartOptions,
- ...this._chartOptions
+ this._chartOptions = {
+ ...this._chartOptions,
+ ...this.highchartsService.transformMetricsToDailyActivityLine(this.activity, this.metrics)
};
this.updateFlag = true;
}
diff --git a/frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.html b/frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.html
index c3c2617..6682a80 100644
--- a/frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.html
+++ b/frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.html
@@ -1,2 +1,2 @@
-
+
diff --git a/frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.ts b/frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.ts
index 705bd0b..dc94518 100644
--- a/frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.ts
+++ b/frontend/src/app/main/copilot/copilot-value/time-saved-chart/time-saved-chart.component.ts
@@ -1,5 +1,5 @@
-import { Component, Input } from '@angular/core';
-import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
+import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
+import * as Highcharts from 'highcharts';
import { HighchartsChartModule } from 'highcharts-angular';
import { Survey } from '../../../../services/copilot-survey.service';
import { HighchartsService } from '../../../../services/highcharts.service';
@@ -13,99 +13,19 @@ import { HighchartsService } from '../../../../services/highcharts.service';
templateUrl: './time-saved-chart.component.html',
styleUrl: './time-saved-chart.component.scss'
})
-export class TimeSavedChartComponent {
+export class TimeSavedChartComponent implements OnInit, OnChanges {
@Input() surveys?: Survey[];
+ @Input() chartOptions?: Highcharts.Options;
+ @Output() chartInstanceChange = new EventEmitter();
Highcharts: typeof Highcharts = Highcharts;
updateFlag = false;
- data = [
- // April 2023
- [1680307200000, 6.2], // Week 1
- [1680912000000, 7.1], // Week 2
- [1681516800000, 6.8], // Week 3
- [1682121600000, 19.2], // Week 4 - 🚀 Anomaly spike
- // May 2023
- [1682726400000, 7.8], // Week 5
- [1683331200000, 8.2], // Week 6
- [1683936000000, 5.1], // Week 7 - 📉 Anomaly drop
- [1684540800000, 8.9], // Week 8
- // June 2023
- [1685145600000, 9.3], // Week 9
- [1685750400000, 8.7], // Week 10
- [1686355200000, 9.8], // Week 11
- [1686960000000, 9.2], // Week 12
- // July 2023
- [1687564800000, 18.8], // Week 13 - 🚀 Anomaly spike
- [1688169600000, 10.1], // Week 14
- [1688774400000, 10.8], // Week 15
- [1689379200000, 10.2], // Week 16
- // August 2023
- [1689984000000, 11.4], // Week 17
- [1690588800000, 10.9], // Week 18
- [1691193600000, 5.3], // Week 19 - 📉 Anomaly drop
- [1691798400000, 11.8], // Week 20
- // September 2023
- [1692403200000, 11.2], // Week 21
- [1693008000000, 12.5], // Week 22
- [1693612800000, 12.0], // Week 23
- [1694217600000, 19.5], // Week 24 - 🚀 Anomaly spike
- // October 2023
- [1694822400000, 12.8], // Week 25
- [1695427200000, 12.3], // Week 26
- [1696032000000, 13.4], // Week 27
- [1696636800000, 12.9], // Week 28
- // November 2023
- [1697241600000, 13.8], // Week 29
- [1697846400000, 13.2], // Week 30
- [1698451200000, 14.3], // Week 31
- [1699056000000, 13.9], // Week 32
- // December 2023
- [1699660800000, 19.8], // Week 33 - 🚀 Anomaly spike
- [1700265600000, 14.5], // Week 34
- [1700870400000, 15.1], // Week 35
- [1701475200000, 14.8], // Week 36
- // January 2024
- [1702080000000, 15.6], // Week 37
- [1702684800000, 15.2], // Week 38
- [1703289600000, 5.5], // Week 39 - 📉 Anomaly drop
- [1703894400000, 15.9], // Week 40
- // February 2024
- [1704499200000, 16.4], // Week 41
- [1705104000000, 15.9], // Week 42
- [1705708800000, 16.8], // Week 43
- [1706313600000, 16.3], // Week 44
- // March 2024
- [1706918400000, 19.9], // Week 45 - 🚀 Final spike
- [1707523200000, 17.2], // Week 46
- [1708128000000, 17.8], // Week 47
- [1708732800000, 17.4], // Week 48
- [1709337600000, 18.2], // Week 49
- [1709942400000, 17.9], // Week 50
- [1710547200000, 18.5], // Week 51
- [1711152000000, 18.8] // Week 52 - Final
- ];
-
- chartOptions: Highcharts.Options = {
- chart: {
- zooming: {
- type: 'x'
- },
- width: undefined,
- },
- xAxis: {
- type: 'datetime',
- dateTimeLabelFormats: {
- // don't display the year
- month: '%b',
- year: '%b'
- },
- crosshair: true
- },
+ _chartOptions: Highcharts.Options = {
yAxis: {
title: {
text: 'Time Saved (%)'
},
min: 0,
- // max: 100,
+ max: 100,
labels: {
format: '{value}%'
},
@@ -148,54 +68,33 @@ export class TimeSavedChartComponent {
series: [{
name: 'Time Saved',
type: 'spline',
- data: this.data.map(point => ({
- x: point[0],
- y: point[1],
- })),
- lineWidth: 2,
- marker: {
- enabled: true,
- radius: 4,
- symbol: 'circle'
- },
- states: {
- hover: {
- lineWidth: 3
- }
- }
+ data: []
}, {
type: 'scatter',
name: 'Observations',
- data: [],
- marker: {
- radius: 4
- }
+ data: []
}],
legend: {
enabled: false
- },
- plotOptions: {
- series: {
- animation: {
- duration: 300
- }
- }
}
};
- _chartOptions?: Highcharts.Options;
constructor(
private highchartsService: HighchartsService
- ) {
+ ) { }
+
+ ngOnInit() {
+ this._chartOptions = {
+ ...this._chartOptions,
+ ...this.chartOptions
+ }
}
ngOnChanges() {
if (this.surveys) {
- console.log('surveys', this.surveys);
- this._chartOptions = this.highchartsService.transformSurveysToScatter(this.surveys);
- this.chartOptions = {
- ...this.chartOptions,
- ...this._chartOptions
+ this._chartOptions = {
+ ...this._chartOptions,
+ ...this.highchartsService.transformSurveysToScatter(this.surveys)
};
this.updateFlag = true;
}
diff --git a/frontend/src/app/main/copilot/copilot-value/value.component.html b/frontend/src/app/main/copilot/copilot-value/value.component.html
index bdefac2..ba2ede4 100644
--- a/frontend/src/app/main/copilot/copilot-value/value.component.html
+++ b/frontend/src/app/main/copilot/copilot-value/value.component.html
@@ -30,19 +30,19 @@ Value
Adoption
-
+
Daily Activity (Usage)
-
+
Time Saved
-
+
\ No newline at end of file
diff --git a/frontend/src/app/main/copilot/copilot-value/value.component.ts b/frontend/src/app/main/copilot/copilot-value/value.component.ts
index 3eb9953..65e26a2 100644
--- a/frontend/src/app/main/copilot/copilot-value/value.component.ts
+++ b/frontend/src/app/main/copilot/copilot-value/value.component.ts
@@ -9,6 +9,7 @@ import { MetricsService } from '../../../services/metrics.service';
import { FormControl } from '@angular/forms';
import { combineLatest, startWith } from 'rxjs';
import { CopilotSurveyService, Survey } from '../../../services/copilot-survey.service';
+import * as Highcharts from 'highcharts';
@Component({
selector: 'app-value',
@@ -20,7 +21,10 @@ import { CopilotSurveyService, Survey } from '../../../services/copilot-survey.s
TimeSavedChartComponent
],
templateUrl: './value.component.html',
- styleUrl: './value.component.scss'
+ styleUrls: [
+ './value.component.scss',
+ // '../copilot-dashboard/dashboard.component.scss'
+ ]
})
export class CopilotValueComponent implements OnInit {
activityData?: ActivityResponse;
@@ -28,6 +32,32 @@ export class CopilotValueComponent implements OnInit {
surveysData?: Survey[];
daysInactive = new FormControl(30);
adoptionFidelity = new FormControl<'day' | 'hour'>('day');
+ Highcharts: typeof Highcharts = Highcharts;
+ charts = [] as Highcharts.Chart[];
+ chartOptions: Highcharts.Options = {
+ chart: {
+ zooming: {
+ type: 'x'
+ },
+ width: undefined,
+ },
+ xAxis: {
+ type: 'datetime',
+ dateTimeLabelFormats: {
+ // don't display the year
+ month: '%b',
+ year: '%b'
+ },
+ crosshair: true
+ },
+ plotOptions: {
+ series: {
+ animation: {
+ duration: 300
+ }
+ }
+ },
+ };
constructor(
private seatService: SeatService,
@@ -51,4 +81,22 @@ export class CopilotValueComponent implements OnInit {
this.surveysData = data;
});
}
+
+ chartChanged(chart: Highcharts.Chart) {
+ this.charts.push(chart);
+ const charts = this.charts;
+ for (chart of charts) {
+ chart.xAxis[0].update({
+ events: {
+ afterSetExtremes: function (event) {
+ charts.forEach(otherChart => {
+ if (otherChart.xAxis[0].min != event.min || otherChart.xAxis[0].max != event.max) {
+ otherChart.xAxis[0].setExtremes(event.min, event.max)
+ }
+ })
+ }
+ }
+ })
+ }
+ }
}
diff --git a/frontend/src/app/services/highcharts.service.ts b/frontend/src/app/services/highcharts.service.ts
index 738aa52..0a6689f 100644
--- a/frontend/src/app/services/highcharts.service.ts
+++ b/frontend/src/app/services/highcharts.service.ts
@@ -1,28 +1,26 @@
import { Injectable } from '@angular/core';
-import { CopilotMetrics } from './metrics.service.interfaces';
+import { ChatModel, CodeModel, CopilotMetrics, DotComChatShared, DotComPullRequestsShared, IdeChatShared, IdeCodeCompletionsShared } from './metrics.service.interfaces';
import { DashboardCardBarsInput } from '../main/copilot/copilot-dashboard/dashboard-card/dashboard-card-bars/dashboard-card-bars.component';
import { ActivityResponse, Seat } from './seat.service';
-import Highcharts from 'highcharts/es-modules/masters/highcharts.src';
+import * as Highcharts from 'highcharts';
import { Survey } from './copilot-survey.service';
-import { DecimalPipe } from '@angular/common';
+import { DatePipe, DecimalPipe } from '@angular/common';
-interface CustomHighchartsPoint extends Highcharts.PointOptionsObject {
+interface CustomHighchartsPoint extends Highcharts.Point {
date?: Date;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
raw?: any;
+ customTooltipInfo?: () => string;
}
interface CustomHighchartsGanttPoint extends Highcharts.GanttPointOptionsObject {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
raw?: any;
}
@Injectable({
providedIn: 'root'
})
export class HighchartsService {
-
- constructor(
- ) { }
-
transformCopilotMetricsToBarChartDrilldown(data: CopilotMetrics[]): Highcharts.Options {
const engagedUsersSeries: Highcharts.SeriesOptionsType = {
name: 'Users',
@@ -43,6 +41,15 @@ export class HighchartsService {
data.forEach(dateData => {
const dateSeriesId = `date_${dateData.date}`;
+ const customTooltipInfo = {
+ 'copilot_ide_code_completions': (data: IdeCodeCompletionsShared) => `Suggestions: ${data.total_code_suggestions}
+
Accepted: ${data.total_code_acceptances} (${((data.total_code_acceptances / data.total_code_suggestions) * 100).toFixed(2)}%)
+
LoC suggested: ${data.total_code_lines_suggested}
+
LoC accepted: ${data.total_code_lines_accepted}`,
+ 'copilot_ide_chat': (data: IdeChatShared) => `Chats: ${data.total_chats}`,
+ 'copilot_dotcom_chat': (data: DotComChatShared) => `Chats: ${data.total_chats}`,
+ 'copilot_dotcom_pull_requests': (data: DotComPullRequestsShared) => `Summaries: ${data.total_pr_summaries_created}`
+ }
drilldownSeries.push({
type: 'column',
name: new Date(dateData.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', weekday: 'long' }),
@@ -51,22 +58,26 @@ export class HighchartsService {
{
name: 'IDE Completions',
y: dateData.copilot_ide_code_completions?.total_engaged_users || 0,
- drilldown: `ide_${dateData.date}`
+ drilldown: `ide_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_ide_code_completions(dateData.copilot_ide_code_completions!)
},
{
name: 'IDE Chat',
y: dateData.copilot_ide_chat?.total_engaged_users || 0,
- drilldown: `chat_${dateData.date}`
+ drilldown: `chat_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_ide_chat(dateData.copilot_ide_chat!)
},
{
name: 'GitHub.com Chat',
y: dateData.copilot_dotcom_chat?.total_engaged_users || 0,
- drilldown: `dotcom_chat_${dateData.date}`
+ drilldown: `dotcom_chat_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_dotcom_chat(dateData.copilot_dotcom_chat!)
},
{
name: 'Pull Requests',
y: dateData.copilot_dotcom_pull_requests?.total_engaged_users || 0,
- drilldown: `pr_${dateData.date}`
+ drilldown: `pr_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_dotcom_pull_requests(dateData.copilot_dotcom_pull_requests!)
}
].sort((a, b) => b.y - a.y)
});
@@ -79,7 +90,9 @@ export class HighchartsService {
data: dateData.copilot_ide_code_completions?.editors.map((editor) => ({
name: editor.name,
y: editor.total_engaged_users,
- drilldown: `ide_${editor.name}_${dateData.date}`
+ drilldown: `ide_${editor.name}_${dateData.date}`,
+ color: this.getEditorColor(editor.name.split('/')[0]),
+ customTooltipInfo: () => customTooltipInfo.copilot_ide_code_completions(editor)
})).sort((a, b) => b.y - a.y)
});
@@ -92,23 +105,61 @@ export class HighchartsService {
data: editor.models.map((model) => ({
name: model.name,
y: model.total_engaged_users,
- drilldown: `ide_${editor.name}_${model.name}_${dateData.date}`
+ drilldown: `ide_${editor.name}_${model.name}_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_ide_code_completions(model)
})).sort((a, b) => b.y - a.y)
});
// Model languages drilldown
editor.models.forEach((model) => {
- if ('languages' in model) {
+ drilldownSeries.push({
+ type: 'column',
+ name: `${model.name} Languages`,
+ id: `ide_${editor.name}_${model.name}_${dateData.date}`,
+ data: (model as CodeModel).languages.map((language) => ({
+ name: language.name,
+ y: language.total_engaged_users,
+ color: this.getLanguageColor(language.name),
+ drilldown: `lang_${editor.name}_${model.name}_${language.name}_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_ide_code_completions(language)
+ })).sort((a, b) => b.y - a.y)
+ });
+ // Add acceptance stats drilldown for each language
+ (model as CodeModel).languages.forEach((language) => {
drilldownSeries.push({
type: 'column',
- name: `${model.name} Languages`,
- id: `ide_${editor.name}_${model.name}_${dateData.date}`,
- data: model.languages.map((lang): [string, number] => [
- lang.name,
- lang.total_engaged_users
- ]).sort((a, b) => b[1] - a[1])
+ name: `${language.name} Suggestions`,
+ id: `lang_${editor.name}_${model.name}_${language.name}_${dateData.date}`,
+ data: [
+ {
+ name: 'Suggestions',
+ y: (language.total_code_lines_suggested || 0),
+ color: '#007ACC'
+ },
+ {
+ name: 'Accepted',
+ y: language.total_code_acceptances || 0,
+ color: '#4CAF50'
+ },
+ {
+ name: 'LoC Suggested',
+ y: language.total_code_lines_suggested,
+ color: '#FFC107'
+ },
+ {
+ name: 'LoC Accepted',
+ y: language.total_code_lines_accepted,
+ color: '#FF5722'
+ }
+ ],
+ tooltip: {
+ headerFormat: '',
+ pointFormatter: function () {
+ return `${this.name}: ${this.y}`;
+ }
+ }
});
- }
+ });
});
});
@@ -120,7 +171,9 @@ export class HighchartsService {
data: dateData.copilot_ide_chat?.editors?.map((editor) => ({
name: editor.name,
y: editor.total_engaged_users,
- drilldown: `chat_${editor.name}_${dateData.date}`
+ drilldown: `chat_${editor.name}_${dateData.date}`,
+ color: this.getEditorColor(editor.name.split('/')[0]),
+ customTooltipInfo: () => customTooltipInfo.copilot_ide_chat(editor)
})).sort((a, b) => b.y - a.y)
});
@@ -133,23 +186,76 @@ export class HighchartsService {
data: editor.models.map((model) => ({
name: model.name,
y: model.total_engaged_users,
+ drilldown: `chat_${editor.name}_${model.name}_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_ide_chat(model)
})).sort((a, b) => b.y - a.y)
});
+ (editor.models as ChatModel[]).forEach((model) => {
+ drilldownSeries.push({
+ type: 'column',
+ name: `${model.name} Suggestions`,
+ id: `chat_${editor.name}_${model.name}_${dateData.date}`,
+ data: [
+ {
+ name: 'Chats',
+ y: model.total_chats,
+ color: '#007ACC'
+ },
+ {
+ name: 'Copys',
+ y: (model.total_chat_copy_events || 0),
+ color: '#4CAF50'
+ },
+ {
+ name: 'Inertions',
+ y: model.total_chat_insertion_events || 0,
+ color: '#FFC107'
+ }
+ ],
+ tooltip: {
+ headerFormat: '',
+ pointFormatter: function () {
+ return `${this.name}: ${this.y}`;
+ }
+ }
+ });
+ });
});
// GitHub.com Chat drilldown (Model level)
- drilldownSeries.push({
- type: 'column',
- name: 'GitHub.com Chat',
- id: `dotcom_chat_${dateData.date}`,
- data: dateData.copilot_dotcom_chat?.models?.map((model) => ({
- name: model.name,
- y: model.total_engaged_users,
- custom: {
- totalChats: model.total_chats
- }
- })).sort((a, b) => b.y - a.y) || []
- });
+ if (dateData.copilot_dotcom_chat) {
+ drilldownSeries.push({
+ type: 'column',
+ name: 'GitHub.com Chat',
+ id: `dotcom_chat_${dateData.date}`,
+ data: dateData.copilot_dotcom_chat.models?.map((model) => ({
+ name: model.name,
+ y: model.total_engaged_users,
+ drilldown: `dotcom_chat_${model.name}_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_dotcom_chat(model)
+ })).sort((a, b) => b.y - a.y) || []
+ });
+ dateData.copilot_dotcom_chat.models?.forEach((model) => {
+ drilldownSeries.push({
+ type: 'column',
+ name: `${model.name} Suggestions`,
+ id: `dotcom_chat_${model.name}_${dateData.date}`,
+ data: [
+ {
+ name: 'Chats',
+ y: model.total_chats,
+ color: '#007ACC'
+ }
+ ],
+ tooltip: {
+ headerFormat: '',
+ pointFormatter: function () {
+ return `${this.name}: ${this.y}`;
+ }
+ }
+ });
+ });
+ }
// PR drilldown (Repo level)
drilldownSeries.push({
@@ -159,7 +265,8 @@ export class HighchartsService {
data: dateData.copilot_dotcom_pull_requests?.repositories.map((repo) => ({
name: repo.name || 'Unknown',
y: repo.total_engaged_users,
- drilldown: `pr_${repo.name}_${dateData.date}`
+ drilldown: `pr_${repo.name}_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_dotcom_pull_requests(repo)
})).sort((a, b) => b.y - a.y)
});
@@ -172,12 +279,39 @@ export class HighchartsService {
data: repo.models.map((model) => ({
name: model.name,
y: model.total_engaged_users,
+ drilldown: `pr_${repo.name}_${model.name}_${dateData.date}`,
+ customTooltipInfo: () => customTooltipInfo.copilot_dotcom_pull_requests(model)
})).sort((a, b) => b.y - a.y)
});
+ (repo.models).forEach((model) => {
+ drilldownSeries.push({
+ type: 'column',
+ name: `${model.name} Suggestions`,
+ id: `pr_${repo.name}_${model.name}_${dateData.date}`,
+ data: [
+ {
+ name: 'Created Summaries',
+ y: model.total_pr_summaries_created,
+ color: '#007ACC'
+ }
+ ],
+ tooltip: {
+ headerFormat: '',
+ pointFormatter: function () {
+ return `${this.name}: ${this.y}`;
+ }
+ }
+ });
+ });
});
});
return {
+ chart: {
+ events: {
+ drilldown: this.drilldownIfSinglePoint()
+ }
+ },
series: [engagedUsersSeries],
drilldown: {
series: drilldownSeries
@@ -186,7 +320,11 @@ export class HighchartsService {
headerFormat: '{series.name}
',
pointFormatter: function (this: CustomHighchartsPoint) {
const formatted = this.date ? this.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', weekday: 'short' }) : this.name;
- return `${formatted}: ${this.y} users
`;
+ const parts = [
+ `${formatted}: ${this.y} users
`
+ ]
+ if (this.customTooltipInfo) parts.push(this.customTooltipInfo());
+ return parts.join('');
}
}
};
@@ -224,9 +362,22 @@ export class HighchartsService {
}
}
};
-
+ const dateTimes = Object.keys(data);
+ const isDaily = Math.abs(new Date(dateTimes[1]).getTime() - new Date(dateTimes[0]).getTime()) > 3600000;
+ const dateFormat = isDaily ? undefined : 'short';
return {
series: [activeUsersSeries],
+ tooltip: {
+ pointFormatter: (function () {
+ const parts = [
+ `${new DatePipe('en-US').transform(this.x, dateFormat)}
`,
+ `${this.series.name}: `,
+ `${this.raw}`,
+ `(${this.y?.toFixed(1)}%)`
+ ]
+ return parts.join('');
+ }) as Highcharts.FormatterCallbackFunction
+ }
};
}
@@ -355,13 +506,13 @@ export class HighchartsService {
pointFormatter: function () {
return [
`User: `,
- '' + (this as any).raw.userId + '',
+ '' + this.raw.userId + '',
`Time saved: `,
'' + Math.round(this.y || 0) + '%',
`PR: `,
- '#' + (this as any).raw.prNumber + '',
+ '#' + this.raw.prNumber + '',
].join('');
- }
+ } as Highcharts.FormatterCallbackFunction
}
};
}
@@ -397,7 +548,7 @@ export class HighchartsService {
data: seatActivity.reduce((acc, seat, index) => {
const lastSeatActivity = seatActivity[index - 1];
- // Skip if same activity timestamp as previous (no new activity) 🕐
+ // Skip if same activity timestamp as previous (no new activity)
if (
lastSeatActivity?.last_activity_at === seat.last_activity_at &&
lastSeatActivity?.last_activity_editor === seat.last_activity_editor
@@ -405,16 +556,13 @@ export class HighchartsService {
return acc;
}
- const activityTime = new Date(Date.parse(seat.last_activity_at || seat.created_at));
+ const activityStartTime = new Date(Date.parse(seat.last_activity_at || seat.created_at));
+ const activityEndTime = new Date(Date.parse(seatActivity[index + 1]?.last_activity_at || seatActivity[index + 1]?.created_at));
- // For first activity or new activity timestamp 📊
acc.push({
name: String(seat.assignee?.login || `Seat ${seat.assignee?.id}`),
- start: activityTime.getTime(),
- // End time is either next activity or current time
- end: index < seatActivity.length - 1
- ? new Date(Date.parse(seatActivity[index + 1].last_activity_at || seatActivity[index + 1].created_at)).getTime()
- : activityTime.getTime() + (60 * 60 * 1000), // Add 1 hour for last activity
+ start: activityStartTime.getTime(),
+ end: index < seatActivity.length - 1 ? activityEndTime.getTime() : activityStartTime.getTime() + (60 * 60 * 1000),
y: getEditorIndex(seat),
color: getEditorColor(seat),
raw: seat,
@@ -446,10 +594,21 @@ export class HighchartsService {
}
transformMetricsToPieDrilldown(metrics: CopilotMetrics): Highcharts.Options {
- // Main pie series showing top level categories 🔝
- console.log(metrics)
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
const sharedSeriesOptions: any = {
colorByPoint: true,
+ innerSize: '40%',
+ };
+ const suggestionsSeries: Highcharts.SeriesOptionsType = {
+ type: 'pie' as const,
+ name: 'Copilot Usage',
+ data: metrics.copilot_ide_code_completions?.editors.map(editor => ({
+ name: editor.name,
+ y: editor.total_code_suggestions,
+ raw: editor,
+ drilldown: `editor_${editor.name}`,
+ color: this.getEditorColor(editor.name.split('/')[0])
+ })),
dataLabels: [{
enabled: true,
distance: 20
@@ -462,18 +621,7 @@ export class HighchartsService {
property: 'percentage',
value: 10
}
- }]
- };
- const suggestionsSeries: Highcharts.SeriesOptionsType = {
- type: 'pie',
- name: 'Copilot Usage',
- data: metrics.copilot_ide_code_completions?.editors.map(editor => ({
- name: editor.name,
- y: editor.total_code_suggestions,
- raw: editor,
- drilldown: `editor_${editor.name}`,
- color: this.getEditorColor(editor.name.split('/')[0])
- })),
+ }],
...sharedSeriesOptions
};
@@ -488,14 +636,13 @@ export class HighchartsService {
id: `editor_${editor.name}`,
data: editor.models.map(model => ({
name: model.name,
- y: 'languages' in model ? model.total_code_suggestions : model.total_chats,
+ y: (model as CodeModel).total_code_suggestions,
raw: model,
- drilldown: 'languages' in model ? `model_${editor.name}_${model.name}` : undefined
+ drilldown: `model_${editor.name}_${model.name}`
})),
...sharedSeriesOptions
});
- // Model languages drilldown 📊
editor.models.forEach(model => {
if ('languages' in model) {
drilldownSeries.push({
@@ -512,9 +659,7 @@ export class HighchartsService {
...sharedSeriesOptions
});
- // Language details drilldown 🔍
model.languages.forEach(lang => {
- console.log(lang.name)
drilldownSeries.push({
type: 'pie',
name: `${lang.name} Details`,
@@ -524,11 +669,13 @@ export class HighchartsService {
name: 'Accepted',
y: lang.total_code_acceptances,
raw: lang,
+ color: '#4CAF50'
},
{
name: 'Ignored',
y: (lang.total_code_suggestions || 0) - (lang.total_code_acceptances || 0),
raw: lang,
+ color: '#757575'
}
] as CustomHighchartsPoint[],
...sharedSeriesOptions
@@ -540,24 +687,28 @@ export class HighchartsService {
}
return {
+ chart: {
+ events: {
+ drilldown: this.drilldownIfSinglePoint()
+ }
+ },
series: [suggestionsSeries],
drilldown: {
- series: drilldownSeries
+ series: drilldownSeries,
},
tooltip: {
headerFormat: undefined,
pointFormatter: function () {
- const point: any = this;
const decimalPipe = new DecimalPipe('en-US');
const parts = [
- `${point.name}
`,
- `Suggestions: ${decimalPipe.transform(point.y)}
`,
- `Accepted: ${decimalPipe.transform(point.raw.total_code_acceptances)} (${decimalPipe.transform(point.raw.total_code_acceptances / point.y * 100, '1.2-2')}%)
`,
- `LoC Suggested: ${decimalPipe.transform(point.raw.total_code_lines_suggested)}
`,
- `LoC Accepted: ${decimalPipe.transform(point.raw.total_code_lines_accepted)}
`
+ `${this.name}
`,
+ `Suggestions: ${decimalPipe.transform(this.y)}
`,
+ `Accepted: ${decimalPipe.transform(this.raw.total_code_acceptances)} (${decimalPipe.transform(this.raw.total_code_acceptances / this.raw.total_code_suggestions * 100, '1.2-2')}%)
`,
+ `LoC Suggested: ${decimalPipe.transform(this.raw.total_code_lines_suggested)}
`,
+ `LoC Accepted: ${decimalPipe.transform(this.raw.total_code_lines_accepted)}
`
];
return parts.join('');
- }
+ } as Highcharts.FormatterCallbackFunction
}
};
}
@@ -566,7 +717,7 @@ export class HighchartsService {
return ({
'visualstudio': '#5C2D91',
'vscode': '#007ACC',
- 'jetbrains': '#000000',
+ 'jetbrains': '#FF6B6B',
'xcode': '#157EFB',
'neovim': '#57A143',
'emacs': '#7F5AB6',
@@ -634,4 +785,18 @@ export class HighchartsService {
return colorMap[language.toLowerCase()]
}
+
+ drilldownIfSinglePoint(): Highcharts.DrilldownCallbackFunction {
+ return function (e) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ if ((e.seriesOptions as any)?.data.length === 1) {
+ setTimeout(() => {
+ const point = this.series[0].points[0];
+ if (point && point.doDrilldown) {
+ point.doDrilldown();
+ }
+ }, 0);
+ }
+ }
+ }
}
diff --git a/frontend/src/app/services/metrics.service.interfaces.ts b/frontend/src/app/services/metrics.service.interfaces.ts
index f8980d9..507b676 100644
--- a/frontend/src/app/services/metrics.service.interfaces.ts
+++ b/frontend/src/app/services/metrics.service.interfaces.ts
@@ -1,82 +1,78 @@
-interface LanguageMetrics {
+interface LanguageMetrics extends IdeCodeCompletionsShared {
name: string;
- total_engaged_users: number;
- total_code_suggestions?: number;
- total_code_acceptances?: number;
- total_code_lines_suggested?: number;
- total_code_lines_accepted?: number;
}
interface ModelBase {
name: string;
is_custom_model: boolean;
custom_model_training_date: string | null;
- total_engaged_users: number;
}
-interface CodeModel extends ModelBase {
- total_code_acceptances: number,
- total_code_suggestions: number,
- total_code_lines_accepted: number,
- total_code_lines_suggested: number,
+export interface CodeModel extends ModelBase, IdeCodeCompletionsShared {
languages: LanguageMetrics[];
}
-interface ChatModel extends ModelBase {
- total_chats: number;
- total_chat_insertion_events?: number;
- total_chat_copy_events?: number;
+export interface ChatModel extends ModelBase, IdeChatShared {
}
-interface DotComChatModel extends ModelBase {
- total_chats: number;
+interface DotComChatModel extends ModelBase, DotComChatShared {
}
-interface PullRequestModel extends ModelBase {
- total_pr_summaries_created: number;
+interface PullRequestModel extends ModelBase, DotComPullRequestsShared {
}
-interface Editor {
+interface CodeEditor extends IdeCodeCompletionsShared {
name: string;
- total_engaged_users: number;
- total_code_acceptances: number,
- total_code_suggestions: number,
- total_code_lines_accepted: number,
- total_code_lines_suggested: number,
- models: (CodeModel | ChatModel)[];
+ models: CodeModel[];
}
-interface Repository {
+interface ChatEditor extends IdeChatShared {
+ name: string;
+ models: ChatModel[];
+}
+
+interface Repository extends DotComPullRequestsShared {
name: string;
- total_engaged_users: number;
models: PullRequestModel[];
}
-interface IdeCodeCompletions {
+export interface IdeCodeCompletionsShared {
total_engaged_users: number;
total_code_acceptances: number;
total_code_lines_accepted: number;
total_code_lines_suggested: number;
total_code_suggestions: number;
+}
+export interface IdeCodeCompletions extends IdeCodeCompletionsShared {
languages: LanguageMetrics[];
- editors: Editor[];
+ editors: CodeEditor[];
}
-interface IdeChat {
+export interface IdeChatShared {
+ total_engaged_users: number;
+ total_chats: number;
+ total_chat_insertion_events?: number;
+ total_chat_copy_events?: number;
+}
+export interface IdeChat extends IdeChatShared {
total_chats: number;
total_engaged_users: number;
- editors: Editor[];
+ editors: ChatEditor[];
}
-interface DotComChat {
+export interface DotComChatShared {
total_engaged_users: number;
total_chats: number;
+}
+export interface DotComChat extends DotComChatShared {
models: DotComChatModel[];
}
-interface DotComPullRequests {
+export interface DotComPullRequestsShared {
total_engaged_users: number;
total_pr_summaries_created: number;
+}
+export interface DotComPullRequests extends DotComPullRequestsShared {
repositories: Repository[];
}
diff --git a/frontend/src/app/shared/date-range-select/date-range-select.component.ts b/frontend/src/app/shared/date-range-select/date-range-select.component.ts
index 8abf8e6..925def7 100644
--- a/frontend/src/app/shared/date-range-select/date-range-select.component.ts
+++ b/frontend/src/app/shared/date-range-select/date-range-select.component.ts
@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, Component, EventEmitter, OnDestroy, Output } from '@angular/core';
+import { ChangeDetectionStrategy, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
import { provideNativeDateAdapter } from '@angular/material/core';
import { MatSelectModule } from '@angular/material/select';
import { MatFormFieldModule } from '@angular/material/form-field';
@@ -38,7 +38,7 @@ export enum DateRangeOption {
templateUrl: './date-range-select.component.html',
styleUrl: './date-range-select.component.scss'
})
-export class DateRangeSelectComponent implements OnDestroy {
+export class DateRangeSelectComponent implements OnInit, OnDestroy {
@Output() dateRangeChange = new EventEmitter<{ start: Date, end: Date }>();
private subscriptions: Subscription[] = [];
type = new FormControl();
@@ -64,6 +64,7 @@ export class DateRangeSelectComponent implements OnDestroy {
this.subscriptions.push(
this.range.valueChanges.subscribe(value => {
if (value.start && value.end) {
+ console.log(value);
this.dateRangeChange.emit({
start: value.start,
end: value.end
@@ -73,7 +74,7 @@ export class DateRangeSelectComponent implements OnDestroy {
)
}
- ngOnInit() {
+ ngOnInit() {
this.dateRangeChange.emit({
start: this.range.value.start,
end: this.range.value.end