Skip to content

Commit e5f1c52

Browse files
committed
Refactor activity totals handling and improve logging; update database schema for assignee tracking
1 parent 31804e2 commit e5f1c52

File tree

7 files changed

+94
-56
lines changed

7 files changed

+94
-56
lines changed

backend/src/controllers/adoption.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class AdoptionController {
3333

3434
async getAdoptionTotals(req: Request, res: Response): Promise<void> {
3535
try {
36-
const totals = await SeatsService.getMembersActivityTotals(req.query);
36+
const totals = await SeatsService.getMembersActivityTotals2(req.query);
3737
res.status(200).json(totals);
3838
} catch (error) {
3939
res.status(500).json(error);

backend/src/controllers/seats.controller.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,6 @@ class SeatsController {
4242
res.status(500).json(error);
4343
}
4444
}
45-
46-
async getActivityTotals(req: Request, res: Response): Promise<void> {
47-
try {
48-
const totals = await SeatsService.getMembersActivityTotals(req.query);
49-
res.status(200).json(totals);
50-
} catch (error) {
51-
res.status(500).json(error);
52-
}
53-
}
5445
}
5546

5647
export default new SeatsController();

backend/src/database.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import updateDotenv from 'update-dotenv';
22
import logger from './services/logger.js';
3-
import mongoose, { Schema } from 'mongoose';
3+
import mongoose, { mongo, Schema } from 'mongoose';
44
import util from 'util';
55

66
class Database {
@@ -34,10 +34,11 @@ class Database {
3434
});
3535
mongoose.set('debug', (collectionName: string, methodName: string, ...methodArgs: unknown[]) => {
3636
const msgMapper = (m: unknown) => {
37-
return util.inspect(m, false, 10, true)
37+
return util.inspect(m, false, 10, true)
3838
.replace(/\n/g, '').replace(/\s{2,}/g, ' ');
3939
};
40-
logger.debug(`\x1B[0;36mMongoose:\x1B[0m: ${collectionName}.${methodName}` + `(${methodArgs.map(msgMapper).join(', ')})`);
40+
// logger.debug(`\x1B[0;36mMongoose:\x1B[0m: ${collectionName}.${methodName}` + `(${methodArgs.map(msgMapper).join(', ')})`);
41+
logger.debug(`[Mongoose] ${collectionName}.${methodName}(${methodArgs.map(msgMapper).join(', ')})`);
4142
});
4243

4344
} catch (error) {
@@ -241,11 +242,7 @@ class Database {
241242
});
242243

243244
seatsSchema.index({ org: 1, queryAt: 1, last_activity_at: -1 });
244-
seatsSchema.index({ team: 1, member: 1 }, { unique: true });
245-
seatsSchema.index({ createdAt: 1 });
246-
seatsSchema.index({ queryAt: 1 });
247-
seatsSchema.index({ assignee: 1 });
248-
seatsSchema.index({ assignee_id: 1 });
245+
seatsSchema.index({ org: 1, team: 1, queryAt: 1, assignee_id: 1 }, { unique: true });
249246

250247
mongoose.model('Seats', seatsSchema);
251248

@@ -286,10 +283,12 @@ class Database {
286283

287284
const activityTotalsSchema = new mongoose.Schema({
288285
org: String,
289-
member_id: {
286+
assignee: {
290287
type: Schema.Types.ObjectId,
291288
ref: 'Member'
292289
},
290+
assignee_id: Number,
291+
assignee_login: String,
293292
date: Date,
294293
total_active_time_ms: Number,
295294
last_activity_at: Date,
@@ -298,7 +297,7 @@ class Database {
298297
timestamps: true
299298
});
300299

301-
activityTotalsSchema.index({ org: 1, date: 1, member_id: 1 }, { unique: true });
300+
activityTotalsSchema.index({ org: 1, date: 1, assignee: 1 }, { unique: true });
302301
activityTotalsSchema.index({ date: 1 }); // For date range queries
303302

304303
mongoose.model('ActivityTotals', activityTotalsSchema);

backend/src/services/copilot.seats.service.ts

Lines changed: 72 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,13 @@ class SeatsService {
3535
.sort({ queryAt: -1 }) // -1 for descending order
3636
.select('queryAt');
3737

38-
//console.log(`Latest query at: ${latestQuery?.queryAt}`);
39-
4038
const seats = await Seats.find({
4139
...(org ? { org } : {}),
4240
queryAt: latestQuery?.queryAt
4341
})
4442
.populate('assignee')
4543
.sort({ last_activity_at: -1 }); // DESC ordering ⬇️
4644

47-
console.log(`Found ${seats.length} seats for ${org || 'all orgs'} at ${latestQuery?.queryAt}`);
4845
return seats;
4946
}
5047

@@ -124,7 +121,7 @@ class SeatsService {
124121
}
125122
}));
126123
await Members.bulkWrite(memberUpdates);
127-
124+
128125
const updatedMembers = await Members.find({
129126
org,
130127
id: { $in: data.map(seat => seat.assignee.id) }
@@ -140,7 +137,6 @@ class SeatsService {
140137
assignee: updatedMembers.find(m => m.id === seat.assignee.id)?._id
141138
}));
142139
const seatResults = await Seats.insertMany(seatsData);
143-
console.log(`Inserted ${seatResults.length} seats for ${org} at ${queryAt}`);
144140

145141
const adoptionData = {
146142
enterprise: null,
@@ -160,33 +156,65 @@ class SeatsService {
160156
await adoptionService.createAdoption(adoptionData);
161157

162158
const today = new Date(queryAt);
163-
today.setUTCHours(0,0,0,0);
159+
// add 1 to day
160+
// today.setDate(today.getDate() + 1);
161+
today.setUTCHours(0, 0, 0, 0);
164162
const activityUpdates = seatResults.map(seat => {
165-
if (!seat.last_activity_at) return null;
166-
163+
if (!seat.assignee_login) {
164+
console.warn('Missing assignee_login for seat:', seat);
165+
return null;
166+
}
167167
return {
168168
updateOne: {
169169
filter: {
170170
org,
171-
member_id: seat.assignee,
172-
date: today,
173-
$or: [
174-
{ last_activity_at: { $lt: seat.last_activity_at } },
175-
{ last_activity_at: { $exists: false } }
176-
]
171+
assignee: seat.assignee,
172+
assignee_id: seat.assignee_id,
173+
assignee_login: seat.assignee_login,
174+
date: today
177175
},
178-
update: {
179-
$inc: { total_active_time_ms: 30 * 60 * 1000 }, // 30 min per activity
176+
update: [{
180177
$set: {
181-
last_activity_at: seat.last_activity_at,
182-
last_activity_editor: seat.last_activity_editor
178+
total_active_time_ms: {
179+
$cond: {
180+
if: { $eq: [seat.last_activity_at, null] },
181+
then: { $ifNull: ["$total_active_time_ms", 0] },
182+
else: {
183+
$add: [
184+
{ $ifNull: ["$total_active_time_ms", 0] },
185+
{
186+
$cond: {
187+
if: {
188+
$and: [
189+
{
190+
$or: [
191+
{ $eq: ["$last_activity_at", null] },
192+
{ $lt: ["$last_activity_at", seat.last_activity_at] }
193+
]
194+
},
195+
{ $gt: [seat.last_activity_at, today] }
196+
]
197+
},
198+
then: 1,
199+
else: 0
200+
}
201+
}
202+
]
203+
}
204+
}
205+
}
183206
}
184-
},
207+
}, {
208+
$set: {
209+
last_activity_editor: seat.last_activity_editor,
210+
last_activity_at: seat.last_activity_at
211+
}
212+
}],
185213
upsert: true
186214
}
187215
};
188216
}).filter(update => update !== null);
189-
217+
190218
if (activityUpdates.length > 0) {
191219
await ActivityTotals.bulkWrite(activityUpdates);
192220
}
@@ -351,39 +379,49 @@ class SeatsService {
351379
}) {
352380
const ActivityTotals = mongoose.model('ActivityTotals');
353381
const { org, since, until } = params;
354-
382+
355383
const match: any = {};
356384
if (org) match.org = org;
357-
if (since) match.date = { $gte: new Date(since) };
358-
if (until) match.date = { ...match.date, $lte: new Date(until) };
359-
385+
if (since || until) {
386+
match.date = {
387+
...(since && { $gte: new Date(since) }),
388+
...(until && { $lte: new Date(until) })
389+
};
390+
}
391+
392+
console.log('getting totals', match);
360393
const totals = await ActivityTotals.aggregate([
394+
// Match documents within date range and org
361395
{ $match: match },
396+
// First group by date AND assignee_login
362397
{
363398
$group: {
364-
_id: "$member_id",
365-
total_time: { $sum: "$total_active_time_ms" }
399+
_id: {
400+
date: "$date",
401+
login: "$assignee_login"
402+
},
403+
daily_time: { $sum: "$total_active_time_ms" }
366404
}
367405
},
406+
// Then group by just assignee_login to sum across days
368407
{
369-
$lookup: {
370-
from: 'members',
371-
localField: '_id',
372-
foreignField: '_id',
373-
as: 'member'
408+
$group: {
409+
_id: "$_id.login",
410+
total_time: { $sum: "$daily_time" }
374411
}
375412
},
376-
{ $unwind: '$member' },
413+
// Sort by total time descending
377414
{ $sort: { total_time: -1 } },
415+
// Project final fields
378416
{
379417
$project: {
380418
_id: 0,
381-
login: '$member.login',
419+
login: '$_id',
382420
total_time: 1
383421
}
384422
}
385423
]);
386-
424+
387425
return totals.map(t => [t.login, t.total_time]);
388426
}
389427
}

backend/src/services/logger.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ const logger = bunyan.createLogger({
4040
level: 'error',
4141
stream: process.stderr
4242
},
43+
{
44+
path: `${logsDir}/error.json`,
45+
period: '1d',
46+
count: 14,
47+
level: 'error'
48+
},
4349
{
4450
path: `${logsDir}/debug.json`,
4551
period: '1d',

backend/src/services/query.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { insertUsage } from '../models/usage.model.js';
44
import SeatService, { SeatEntry } from '../services/copilot.seats.service.js';
55
import { App, Octokit } from 'octokit';
66
import { MetricDailyResponseType } from '../models/metrics.model.js';
7-
import mongoose from 'mongoose';
7+
import mongoose, { MongooseError } from 'mongoose';
88
import metricsService from './metrics.service.js';
99
import teamsService from './teams.service.js';
1010
import adoptionService from './adoption.service.js';
@@ -175,6 +175,10 @@ class QueryService {
175175
return result;
176176
} catch (error) {
177177
logger.debug(error)
178+
if (error instanceof Error) {
179+
logger.error('Error updating seat assignments', error.message);
180+
return;
181+
}
178182
logger.error('Error querying copilot seat assignments');
179183
}
180184
}

frontend/src/app/main/copilot/copilot-dashboard/dashboard-card/active-users-chart/active-users-chart.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class ActiveUsersChartComponent implements OnChanges {
4040
pointFormat: '<span style="padding:0">{point.y: .1f} hours</span>',
4141
headerFormat: '',
4242
formatter: function () {
43-
const hours = (this.y || 0) / (1000 * 60 * 60); // Convert ms to hours
43+
const hours = (this.y || 0); // Convert ms to hours
4444
return `<span style="padding:0">@${this.key}</span><br>
4545
<span style="padding:0">${hours.toFixed(1)} hours</span>`;
4646
},

0 commit comments

Comments
 (0)