Skip to content

Commit 0b2b89e

Browse files
committed
Merge branch 'mike-chart-add'
2 parents fbdea23 + 12e5dc8 commit 0b2b89e

7 files changed

Lines changed: 143 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<highcharts-chart
2+
*ngIf="options"
3+
[Highcharts]="Highcharts"
4+
[options]="options"
5+
style="width: 100%; height: 400px; display: block;">
6+
</highcharts-chart>

frontend/src/app/main/copilot/copilot-dashboard/dashboard-card/dashboard-card-line-chart/dashboard-card-line-chart.component.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { DashboardCardLineChartComponent } from './dashboard-card-line-chart.component';
4+
5+
describe('DashboardCardLineChartComponent', () => {
6+
let component: DashboardCardLineChartComponent;
7+
let fixture: ComponentFixture<DashboardCardLineChartComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [DashboardCardLineChartComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(DashboardCardLineChartComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Component, Input } from '@angular/core';
2+
import { CommonModule } from '@angular/common';
3+
import { HighchartsChartModule } from 'highcharts-angular';
4+
import * as Highcharts from 'highcharts';
5+
import { CopilotMetrics } from '../../../../../services/api/metrics.service.interfaces';
6+
import { HighchartsService } from '../../../../../services/highcharts.service';
7+
8+
@Component({
9+
selector: 'app-dashboard-card-line-chart',
10+
standalone: true,
11+
imports: [CommonModule, HighchartsChartModule],
12+
templateUrl: './dashboard-card-line-chart.component.html',
13+
styleUrls: ['./dashboard-card-line-chart.component.scss']
14+
})
15+
export class DashboardCardLineChartComponent {
16+
@Input() data?: CopilotMetrics[];
17+
18+
Highcharts: typeof Highcharts = Highcharts;
19+
options: Highcharts.Options | undefined;
20+
21+
constructor(private highchartsService: HighchartsService) {}
22+
23+
ngOnChanges(): void {
24+
if (this.data && this.data.length) {
25+
this.options = this.highchartsService.getLanguageTrendsChart(this.data);
26+
}
27+
}
28+
}

frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ <h1>Metrics</h1>
4646
</ng-container>
4747
</mat-card-content>
4848
</mat-card>
49+
<mat-card appearance="outlined" id="line-chart" style="grid-column: span 2;">
50+
<mat-card-header>
51+
<mat-card-title>Language Acceptance Trends</mat-card-title>
52+
</mat-card-header>
53+
<mat-card-content>
54+
<ng-container *ngIf="metrics?.length; else loading">
55+
<app-dashboard-card-line-chart [data]="metrics"></app-dashboard-card-line-chart>
56+
</ng-container>
57+
</mat-card-content>
58+
</mat-card>
4959
</div>
5060
</div>
5161
<ng-template #loading>

frontend/src/app/main/copilot/copilot-metrics/copilot-metrics.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { SeatService } from '../../../services/api/seat.service';
1212
import { MembersService } from '../../../services/api/members.service';
1313
import { CommonModule } from '@angular/common';
1414
import { LoadingSpinnerComponent } from '../../../shared/loading-spinner/loading-spinner.component';
15+
import { DashboardCardLineChartComponent } from '../copilot-dashboard/dashboard-card/dashboard-card-line-chart/dashboard-card-line-chart.component';
1516

1617
@Component({
1718
selector: 'app-metrics',
@@ -25,6 +26,7 @@ import { LoadingSpinnerComponent } from '../../../shared/loading-spinner/loading
2526
ActiveUsersChartComponent,
2627
CommonModule,
2728
LoadingSpinnerComponent,
29+
DashboardCardLineChartComponent
2830
],
2931
templateUrl: './copilot-metrics.component.html',
3032
styleUrls: [

frontend/src/app/services/highcharts.service.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,80 @@ export class HighchartsService {
413413
};
414414
}
415415

416+
getLanguageTrendsChart(metrics: CopilotMetrics[]): Highcharts.Options {
417+
const dailyData: Record<string, Record<string, { accepted: number, suggested: number }>> = {};
418+
const languageTotals: Record<string, number> = {};
419+
420+
for (const entry of metrics) {
421+
const dateStr = new Date(entry.date).toISOString().split('T')[0];
422+
423+
if (!entry.copilot_ide_code_completions?.editors) continue;
424+
425+
for (const editor of entry.copilot_ide_code_completions.editors) {
426+
for (const model of editor.models || []) {
427+
for (const lang of model.languages || []) {
428+
if (!dailyData[dateStr]) dailyData[dateStr] = {};
429+
if (!dailyData[dateStr][lang.name]) {
430+
dailyData[dateStr][lang.name] = { accepted: 0, suggested: 0 };
431+
}
432+
433+
dailyData[dateStr][lang.name].accepted += lang.total_code_lines_accepted || 0;
434+
dailyData[dateStr][lang.name].suggested += lang.total_code_lines_suggested || 0;
435+
436+
languageTotals[lang.name] = (languageTotals[lang.name] || 0) + lang.total_code_lines_suggested;
437+
}
438+
}
439+
}
440+
}
441+
442+
const topLanguages = Object.entries(languageTotals)
443+
.filter(([lang]) => lang.toLowerCase() !== 'unknown') // exclude "unknown"
444+
.sort((a, b) => b[1] - a[1])
445+
.slice(0, 5)
446+
.map(([lang]) => lang);
447+
448+
const series: Highcharts.SeriesSplineOptions[] = topLanguages.map((lang: string) => ({
449+
name: lang,
450+
type: 'spline',
451+
color: this.getLanguageColor(lang),
452+
data: Object.entries(dailyData).map(([date, langs]) => {
453+
const accepted = langs[lang]?.accepted || 0;
454+
const suggested = langs[lang]?.suggested || 0;
455+
const percent = suggested > 0 ? (accepted / suggested) * 100 : null;
456+
457+
return {
458+
x: new Date(date).getTime(),
459+
y: percent,
460+
custom: { accepted, suggested }
461+
};
462+
}).filter(p => p.y !== null)
463+
}));
464+
465+
return {
466+
chart: { type: 'spline' },
467+
xAxis: { type: 'datetime' },
468+
yAxis: {
469+
min: 0,
470+
max: 100,
471+
title: { text: 'Acceptance Rate (%)' }
472+
},
473+
tooltip: {
474+
formatter: function (this: Highcharts.TooltipFormatterContextObject & {
475+
point: Highcharts.Point & { custom?: { accepted: number, suggested: number } }
476+
}) {
477+
return `
478+
<b>${this.series.name}</b><br/>
479+
${Highcharts.dateFormat('%b %e', this.x as number)}<br/>
480+
Accepted: ${this.point.custom?.accepted}<br/>
481+
Suggested: ${this.point.custom?.suggested}<br/>
482+
Acceptance: ${(this.y ?? 0).toFixed(1)}%
483+
`;
484+
}
485+
},
486+
series
487+
};
488+
}
489+
416490
transformCopilotMetricsToBars(data: CopilotMetrics, totalSeats: number): DashboardCardBarsInput[] {
417491
return [
418492
{ name: 'IDE Code Completion', icon: 'code', value: data.copilot_ide_code_completions?.total_engaged_users || 0, maxValue: totalSeats },

0 commit comments

Comments
 (0)