Skip to content

Commit

Permalink
Enhance application configuration and metrics handling
Browse files Browse the repository at this point in the history
- Update timezone settings in Docker Compose and Sequelize configuration for consistent UTC usage.
- Add precision parameter to getActivity method in SeatService for improved activity data granularity.
- Modify metrics insertion logic to correctly handle date parsing.
- Remove unnecessary console logs from various components for cleaner code.
- Introduce new form fields in the value component for selecting adoption fidelity and inactivity threshold.
  • Loading branch information
austenstone committed Nov 17, 2024
1 parent 8d69f65 commit 7a8b31f
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 37 deletions.
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"start": "node --enable-source-maps dist/app.js",
"test": "jest",
"build": "tsc",
"dev": "tsx watch src/app.ts | bunyan -o short",
"dev": "tsx watch src/app.ts | bunyan -o short -l debug",
"lint": "eslint src/**/*.ts",
"db:start": "docker-compose -f ../compose.yml up -d db",
"dotenv": "cp -n .env.example .env || true"
Expand Down
4 changes: 2 additions & 2 deletions backend/src/controllers/seats.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ class SeatsController {
}

async getActivity(req: Request, res: Response): Promise<void> {
const { daysInactive } = req.query;
const { daysInactive, precision } = req.query;
const _daysInactive = Number(daysInactive);
if (!daysInactive || isNaN(_daysInactive)) {
res.status(400).json({ error: 'daysInactive query parameter is required' });
return;
}
try {
const activityDays = await SeatsService.getAssigneesActivity(_daysInactive);
const activityDays = await SeatsService.getAssigneesActivity(_daysInactive, precision as 'hour' | 'day');
res.status(200).json(activityDays);
} catch (error) {
res.status(500).json(error);
Expand Down
12 changes: 10 additions & 2 deletions backend/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ const sequelize = process.env.JAWSDB_URL ?
acquire: 30000,
idle: 10000
},
logging: (sql: string) => logger.debug(sql)
logging: (sql) => logger.debug(sql),
timezone: '+00:00', // Force UTC timezone
dialectOptions: {
timezone: '+00:00' // Force UTC for MySQL connection
},
}) :
new Sequelize({
dialect: 'mysql',
Expand All @@ -19,7 +23,11 @@ const sequelize = process.env.JAWSDB_URL ?
username: process.env.MYSQL_USER || 'root',
password: process.env.MYSQL_PASSWORD || 'octocat',
database: process.env.MYSQL_DATABASE || 'value',
logging: (sql: string) => logger.debug(sql)
logging: (sql) => logger.debug(sql),
timezone: '+00:00', // Force UTC timezone
dialectOptions: {
timezone: '+00:00' // Force UTC for MySQL connection
},
});

const dbConnect = async () => {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/models/metrics.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ MetricDotcomChatModelStats.belongsTo(MetricDotcomChatMetrics, {
export async function insertMetrics(data: CopilotMetrics[]) {
for (const day of data) {
const parts = day.date.split('-').map(Number);
const date = new Date(Date.UTC(parts[0], parts[1] - 1, parts[2], 12));
const date = new Date(Date.UTC(parts[0], parts[1] - 1, parts[2] + 1));
let metric: MetricDaily;
try {
metric = await MetricDaily.create({
Expand Down
40 changes: 26 additions & 14 deletions backend/src/services/copilot.seats.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,20 @@ class SeatsService {
}
}

async getAssigneesActivity(daysInactive: number): Promise<AssigneeDailyActivity> {
async getAssigneesActivity(daysInactive: number, precision: 'hour' | 'day' = 'day'): Promise<AssigneeDailyActivity> {
const assignees = await Assignee.findAll({
attributes: ['login', 'id'],
order: [
['login', 'ASC'],
[{ model: Seat, as: 'activity' }, 'createdAt', 'DESC']
],
include: [
{
model: Seat,
as: 'activity',
required: false,
attributes: ['createdAt', 'last_activity_at']
attributes: ['createdAt', 'last_activity_at'],
order: [['last_activity_at', 'ASC']],
}
],
order: [
[{ model: Seat, as: 'activity' }, 'last_activity_at', 'ASC']
]
});
const activityDays: AssigneeDailyActivity = {};
Expand All @@ -123,32 +123,44 @@ class SeatsService {
const fromTime = activity.last_activity_at?.getTime() || 0;
const toTime = activity.createdAt.getTime();
const diff = Math.floor((toTime - fromTime) / 86400000);
const dateIndex = activity.createdAt.toISOString().slice(0, 10);
if (!activityDays[dateIndex]) {
activityDays[dateIndex] = {
const dateIndex = new Date(activity.createdAt);
if (precision === 'day') {
dateIndex.setUTCHours(0, 0, 0, 0);
} else if (precision === 'hour') {
dateIndex.setUTCMinutes(0, 0, 0);
}
const dateIndexStr = new Date(dateIndex).toISOString();
if (!activityDays[dateIndexStr]) {
activityDays[dateIndexStr] = {
totalSeats: 0,
totalActive: 0,
totalInactive: 0,
active: {},
inactive: {}
}
}
if (activityDays[dateIndex].active[assignee.login] || activityDays[dateIndex].inactive[assignee.login]) {
if (activityDays[dateIndexStr].active[assignee.login] || activityDays[dateIndexStr].inactive[assignee.login]) {
return; // already processed for this day
}
if (diff > daysInactive) {
activityDays[dateIndex].inactive[assignee.login] = assignee.activity[0].last_activity_at;
activityDays[dateIndexStr].inactive[assignee.login] = assignee.activity[0].last_activity_at;
} else {
activityDays[dateIndex].active[assignee.login] = assignee.activity[0].last_activity_at;
activityDays[dateIndexStr].active[assignee.login] = assignee.activity[0].last_activity_at;
}
});
});
Object.entries(activityDays).forEach(([date, activity]) => {
activityDays[date].totalSeats = Object.values(activity.active).length + Object.values(activity.inactive).length
activityDays[date].totalActive = Object.values(activity.active).length
activityDays[date].totalInactive = Object.values(activity.inactive).length
});
return activityDays;
});

const sortedActivityDays = Object.fromEntries(
Object.entries(activityDays)
.sort(([dateA], [dateB]) => new Date(dateA).getTime() - new Date(dateB).getTime())
);

return sortedActivityDays;
}
}

Expand Down
4 changes: 2 additions & 2 deletions backend/src/services/query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { insertMetrics } from '../models/metrics.model.js';
import { CopilotMetrics } from '../models/metrics.model.interfaces.js';
import { getLastUpdatedAt, Member, Team, TeamMemberAssociation } from '../models/teams.model.js';

const DEFAULT_CRON_EXPRESSION = '0 0 * * *';
const DEFAULT_CRON_EXPRESSION = '0 * * * *';
class QueryService {
private static instance: QueryService;
private cronJob: CronJob;
Expand All @@ -32,7 +32,7 @@ class QueryService {
setup.setSetupStatusDbInitialized({ copilotSeats: true })),
]
// Query teams and members if it has been more than 24 hours since the last update
if ((await getLastUpdatedAt() || new Date(0)).getTime() < new Date().getTime() - 1000 * 60 * 60 * 24) {
if ((await getLastUpdatedAt()).getTime() < new Date().getTime() - 1000 * 60 * 60 * 24) {
queries.push(
this.queryTeamsAndMembers().then(() =>
setup.setSetupStatusDbInitialized({ teamsAndMembers: true }))
Expand Down
3 changes: 0 additions & 3 deletions backend/src/services/settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ class SettingsService {
await SmeeService.createSmeeWebhookProxy();
}
if (name === 'webhookSecret') {
console.log('setting webhook secret', value)
setup.addToEnv({ GITHUB_WEBHOOK_SECRET: value });
console.log(name, value)
try {
await setup.createAppFromEnv();
} catch {
Expand All @@ -96,7 +94,6 @@ class SettingsService {

async updateSettings(obj: { [key: string]: string }) {
Object.entries(obj).forEach(([name, value]) => {
console.log(name, value)
this.updateSetting(name, value);
});
}
Expand Down
2 changes: 1 addition & 1 deletion compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ services:
environment:
MYSQL_ROOT_PASSWORD: octocat
MYSQL_DATABASE: value
# TZ: UTC
TZ: UTC
ports:
- '3306:3306'
volumes:
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/app/highcharts.theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ Highcharts.theme = {
},
xAxis: xAxisConfig,
yAxis: yAxisConfig,
time: {
useUTC: false
},
legend: {
align: 'left',
verticalAlign: 'top',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@ import { DateRangeSelectComponent } from "../../../shared/date-range-select/date
})
export class CopilotMetricsComponent {
dateRangeChange(event: {start: Date, end: Date}) {
console.log(event);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class AdoptionChartComponent implements OnChanges {
},
yAxis: {
title: {
text: 'Adoption (%)'
text: 'Percent Active'
},
min: 0,
max: 100,
Expand Down Expand Up @@ -114,14 +114,12 @@ export class AdoptionChartComponent implements OnChanges {
}

ngOnChanges(changes: SimpleChanges) {
console.log('old', this.chartOptions);
if (changes['data'] && this.data) {
this._chartOptions = this.highchartsService.transformActivityMetricsToLine(this.data);
this.chartOptions = {
...this.chartOptions,
...this._chartOptions
};
console.log('new', this.chartOptions);
this.updateFlag = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,12 @@ export class DailyActivityChartComponent implements OnChanges {
}

ngOnChanges() {
console.log('old', this.chartOptions);
if (this.activity && this.metrics) {
this._chartOptions = this.highchartsService.transformMetricsToDailyActivityLine(this.activity, this.metrics);
this.chartOptions = {
...this.chartOptions,
...this._chartOptions
};
console.log('new', this.chartOptions);
this.updateFlag = true;
}
}
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/app/main/copilot/copilot-value/value.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@
<div class="page-header">
<h1>Value</h1>
<span class="spacer"></span>
<div>
<mat-form-field>
<mat-label>Adoption Fidelity</mat-label>
<mat-select [formControl]="adoptionFidelity">
<mat-option value="day">Day</mat-option>
<mat-option value="hour">Hour</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>Inactivity Threshold</mat-label>
<mat-select [formControl]="daysInactive">
<mat-option [value]="1">1 Day</mat-option>
<mat-option [value]="7">7 Days</mat-option>
<mat-option [value]="30">30 Days</mat-option>
<mat-option [value]="60">60 Days</mat-option>
<mat-option [value]="90">90 Days</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="cards-grid">
<mat-card class="chart-card" id="adoption" appearance="outlined">
Expand Down
14 changes: 12 additions & 2 deletions frontend/src/app/main/copilot/copilot-value/value.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { DailyActivityChartComponent } from './daily-activity-chart/daily-activi
import { TimeSavedChartComponent } from './time-saved-chart/time-saved-chart.component';
import { CopilotMetrics } from '../../../services/metrics.service.interfaces';
import { MetricsService } from '../../../services/metrics.service';
import { FormControl } from '@angular/forms';
import { combineLatest, startWith } from 'rxjs';

@Component({
selector: 'app-value',
Expand All @@ -22,14 +24,22 @@ import { MetricsService } from '../../../services/metrics.service';
export class CopilotValueComponent implements OnInit {
activityData?: ActivityResponse;
metricsData?: CopilotMetrics[];
daysInactive = new FormControl(30);
adoptionFidelity = new FormControl<'day' | 'hour'>('hour');

constructor(
private seatService: SeatService,
private metricsService: MetricsService
) { }

ngOnInit() {
this.seatService.getActivity().subscribe(data => {
this.activityData = data;
combineLatest([
this.daysInactive.valueChanges.pipe(startWith(this.daysInactive.value || 30)),
this.adoptionFidelity.valueChanges.pipe(startWith(this.adoptionFidelity.value || 'day'))
]).subscribe(([days, fidelity]) => {
this.seatService.getActivity(days || 30, fidelity || 'day').subscribe(data => {
this.activityData = data;
});
});
this.metricsService.getMetrics().subscribe(data => {
this.metricsData = data;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/services/highcharts.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,9 @@ export class HighchartsService {
};

Object.entries(activity).forEach(([date, dateData]) => {
const currentMetrics = metrics.find(m => m.date.startsWith(date));
console.log(date, date.slice(0, 10), metrics)
const currentMetrics = metrics.find(m => m.date.startsWith(date.slice(0, 10)));
if (currentMetrics?.copilot_ide_code_completions) {
console.log(`date: ${date} total_code_suggestions: ${currentMetrics.copilot_ide_code_completions.total_code_suggestions} totalActive: ${dateData.totalActive} percentage: ${(currentMetrics.copilot_ide_code_completions.total_code_suggestions / dateData.totalActive)}`);
(dailyActiveIdeCompletionsSeries.data).push({
x: new Date(date).getTime(),
y: (currentMetrics.copilot_ide_code_completions.total_code_suggestions / dateData.totalActive),
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/app/services/seat.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ export class SeatService {
return this.http.get<Seat[]>(`${this.apiUrl}`);
}

getActivity(daysInactive = 30) {
getActivity(daysInactive = 30, precision: 'hour' | 'day' = 'day') {
return this.http.get<ActivityResponse>(`${this.apiUrl}/activity`,
{
params: {
precision,
daysInactive: daysInactive.toString()
}
}
Expand Down

0 comments on commit 7a8b31f

Please sign in to comment.