Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Status #84

Merged
merged 6 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/index.js | bunyan -o short -l info",
"test": "jest",
"build": "tsc",
"dev": "tsx watch src/index.ts | bunyan -o short -l debug",
"dev": "tsx watch src/index.ts | bunyan -o short -l info",
"lint": "eslint src/**/*.ts",
"db:start": "docker-compose -f ../compose.yml up -d db",
"dotenv": "cp -n .env.example .env || true"
Expand Down
9 changes: 6 additions & 3 deletions backend/src/controllers/seats.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,20 @@ class SeatsController {
return;
}
try {
const activityDays = await SeatsService.getMembersActivity(org, _daysInactive, precision as 'hour' | 'day' | 'minute');
const activityDays = await SeatsService.getMembersActivity({
org,
daysInactive: _daysInactive,
precision: precision as 'hour' | 'day'
});
res.status(200).json(activityDays);
} catch (error) {
res.status(500).json(error);
}
}

async getActivityTotals(req: Request, res: Response): Promise<void> {
const org = req.query.org?.toString()
try {
const totals = await SeatsService.getMembersActivityTotals(org);
const totals = await SeatsService.getMembersActivityTotals(req.query);
res.status(200).json(totals);
} catch (error) {
res.status(500).json(error);
Expand Down
2 changes: 2 additions & 0 deletions backend/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ router.post('/setup/existing-app', setupController.addExistingApp);
router.post('/setup/db', setupController.setupDB);
router.get('/setup/status', setupController.setupStatus);

router.get('/status', setupController.getStatus);

router.get('/predictive-modeling/targets', targetValuesController.getTargetValues);
router.post('/predictive-modeling/targets', targetValuesController.updateTargetValues);

Expand Down
48 changes: 35 additions & 13 deletions backend/src/services/copilot.seats.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Endpoints } from '@octokit/types';
import { Seat } from "../models/copilot.seats.model.js";
import { QueryTypes, Sequelize } from 'sequelize';
import { Op, QueryTypes, Sequelize } from 'sequelize';

Check failure

Code scanning / ESLint

Disallow unused variables Error

'QueryTypes' is defined but never used.

Copilot Autofix AI about 2 months ago

To fix the problem, we need to remove the unused QueryTypes import from the file. This will resolve the ESLint error and clean up the code. The change should be made in the backend/src/services/copilot.seats.service.ts file, specifically on line 3.

Suggested changeset 1
backend/src/services/copilot.seats.service.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/backend/src/services/copilot.seats.service.ts b/backend/src/services/copilot.seats.service.ts
--- a/backend/src/services/copilot.seats.service.ts
+++ b/backend/src/services/copilot.seats.service.ts
@@ -2,3 +2,3 @@
 import { Seat } from "../models/copilot.seats.model.js";
-import { Op, QueryTypes, Sequelize } from 'sequelize';
+import { Op, Sequelize } from 'sequelize';
 import { components } from "@octokit/openapi-types";
EOF
@@ -2,3 +2,3 @@
import { Seat } from "../models/copilot.seats.model.js";
import { Op, QueryTypes, Sequelize } from 'sequelize';
import { Op, Sequelize } from 'sequelize';
import { components } from "@octokit/openapi-types";
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
import { components } from "@octokit/openapi-types";
import { Member, Team } from '../models/teams.model.js';
import app from '../index.js';
Expand Down Expand Up @@ -142,7 +142,14 @@
}
}

async getMembersActivity(org?: string, daysInactive = 30, precision = 'day' as 'hour' | 'day' | 'minute'): Promise<MemberDailyActivity> {
async getMembersActivity(params: {
org?: string;
daysInactive?: number;
precision?: 'hour' | 'day' | 'minute';
since?: string;
until?: string;
} = {}): Promise<MemberDailyActivity> {
const { org, daysInactive = 30, precision = 'day', since, until } = params;
if (!app.database.sequelize) throw new Error('No database connection available');
// const assignees = await app.database.sequelize.query<Member>(
// `SELECT
Expand All @@ -165,6 +172,11 @@
// mapToModel: true // 🎯 Maps results to the Model
// }
// );

const dateFilter = {
...(since && { [Op.gte]: new Date(since as string) }),
...(until && { [Op.lte]: new Date(until as string) })
};
const assignees = await Member.findAll({
attributes: ['login', 'id'],
include: [
Expand All @@ -175,6 +187,7 @@
order: [['last_activity_at', 'ASC']],
where: {
...(org ? { org } : {}),
...Object.getOwnPropertySymbols(dateFilter).length ? { createdAt: dateFilter } : {}
}
}
],
Expand Down Expand Up @@ -230,17 +243,25 @@
return sortedActivityDays;
}

async getMembersActivityTotals(org?: string) {
const assignees2 = await app.database.sequelize?.query(`
SELECT \`Member\`.\`login\`, \`Member\`.\`id\`, \`activity\`.\`id\` AS \`activity.id\`, \`activity\`.\`last_activity_at\` AS \`activity.last_activity_at\`
FROM \`Members\` AS \`Member\`
INNER JOIN \`Seats\` AS \`activity\` ON \`Member\`.\`id\` = \`activity\`.\`assignee_id\`
`, {
replacements: { org },
type: QueryTypes.SELECT
});
console.log(assignees2);

async getMembersActivityTotals(params: {
org?: string;
since?: string;
until?: string;
}) {
// const assignees2 = await app.database.sequelize?.query(`
// SELECT \`Member\`.\`login\`, \`Member\`.\`id\`, \`activity\`.\`id\` AS \`activity.id\`, \`activity\`.\`last_activity_at\` AS \`activity.last_activity_at\`
// FROM \`Members\` AS \`Member\`
// INNER JOIN \`Seats\` AS \`activity\` ON \`Member\`.\`id\` = \`activity\`.\`assignee_id\`
// `, {
// replacements: { org },
// type: QueryTypes.SELECT
// });
// console.log(assignees2);
const { org, since, until } = params;
const dateFilter = {
...(since && { [Op.gte]: new Date(since as string) }),
...(until && { [Op.lte]: new Date(until as string) })
};
const assignees = await Member.findAll({
attributes: ['login', 'id'],
include: [{
Expand All @@ -250,6 +271,7 @@
order: [['last_activity_at', 'ASC']],
where: {
...(org ? { org } : {}),
...Object.getOwnPropertySymbols(dateFilter).length ? { createdAt: dateFilter } : {}
}
}]
});
Expand Down
55 changes: 34 additions & 21 deletions backend/src/services/status.service.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import app from "index.js";
import { Seat } from "../models/copilot.seats.model.js";
import { Survey } from "../models/survey.model.js";
import { Member } from "../models/teams.model.js";
import { Endpoints } from "@octokit/types";
import copilotSeatsService from "./copilot.seats.service.js";
Fixed Show fixed Hide fixed

export interface StatusType {
github?: {
isGood: boolean
github?: boolean;
seatsHistory?: {
oldestCreatedAt: string;
daysSinceOldestCreatedAt?: number;
};
pollingHistory?: {
isGood: boolean;
message: string;
value?: any;
progress?: string;
};
repos?: {
value: number;
};
surveys?: StatusType;
installations: {
installation: Endpoints["GET /app/installations"]["response"]["data"][0]
repos: Endpoints["GET /app/installations"]["response"]["data"];
}[];
surveyCount: number;
}

class StatusService {
Expand All @@ -24,6 +24,7 @@ class StatusService {
}

async getStatus(): Promise<StatusType> {
console.log('Getting status');
const status = {} as StatusType;

const assignee = await Member.findOne();
Expand All @@ -36,28 +37,40 @@ class StatusService {
where: {
assignee_id: assignee.id
},
order: [['createdAt', 'DESC']],
order: [['createdAt', 'ASC']],
});
const oldestSeat = seats.find(seat => seat.createdAt);
const daysSince = oldestSeat ? Math.floor((new Date().getTime() - oldestSeat.createdAt.getTime()) / (1000 * 3600 * 24)) : undefined;
status.pollingHistory = {
isGood: true,
message: `${oldestSeat?.createdAt}`,
value: daysSince
status.seatsHistory = {
oldestCreatedAt: oldestSeat?.createdAt.toISOString() || 'No data',
daysSinceOldestCreatedAt: daysSince
}
}


status.installations = [];
for (const installation of app.github.installations) {
const repos = await installation.octokit.request(installation.installation.repositories_url);
status.installations.push({
installation: installation.installation,
repos: repos.data.repositories
});
}

const surveys = await Survey.findAll({
order: [['updatedAt', 'DESC']]
});

if (surveys) {
// status.surveys = {
// message: `${surveys.length} surveys created`,
// value: surveys.length
// }
status.surveyCount = surveys.length;
}

const yesterday = new Date(new Date().setDate(new Date().getDate() - 1));
const activity = await copilotSeatsService.getMembersActivity({
since: yesterday.toISOString(),
});
console.log(Object.keys(activity));

return status;
}
}
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/app/guards/setup.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@ import { Injectable, isDevMode } from '@angular/core';
import { CanActivate, GuardResult, MaybeAsync, Router } from '@angular/router';
import { of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { InstallationsService } from '../services/api/installations.service';
import { InstallationsService, statusResponse } from '../services/api/installations.service';

@Injectable({
providedIn: 'root'
})
export class SetupStatusGuard implements CanActivate {
responseCache?: statusResponse;

constructor(
private installationsService: InstallationsService,
private router: Router
) {}

canActivate(): MaybeAsync<GuardResult> {
if (this.responseCache?.isSetup === true) return of(true);
return this.installationsService.refreshStatus().pipe(
map((response) => {
this.responseCache = response;
if (!response.dbConnected) {
this.router.navigate(['/setup/db']);
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ <h1>Dashboard</h1>
</div> -->
<div class="cards-grid">

<app-dashboard-card-value routerLink="/copilot/seats" title="Seats" [value]="totalSeats" [change]="seatPercentage"
<!-- <app-dashboard-card-value routerLink="/copilot/seats" title="Seats" [value]="totalSeats" [change]="seatPercentage"
changeSuffix="" icon="" changeDescription="% have Copilot"
subtitle="Total Copilot Seats"></app-dashboard-card-value>
<app-dashboard-card-value title="Active Users" [value]="activeCurrentWeekAverage"
[change]="activeWeeklyChangePercent" changeSuffix="" changeDescription="% since last"
subtitle="Average activity for last 7 days"></app-dashboard-card-value>
<app-dashboard-card-value routerLink="/copilot/surveys" title="Surveys Complete" icon="" [value]="totalSurveys"
[change]="totalSurveysThisWeek" changeSuffix="" changeDescription=" this week"></app-dashboard-card-value>
[change]="totalSurveysThisWeek" changeSuffix="" changeDescription=" this week"></app-dashboard-card-value> -->

<mat-card id="adoption" appearance="outlined" routerLink="/copilot/value" fragment="adoption">
<mat-card-header>
Expand All @@ -39,37 +39,12 @@ <h1>Dashboard</h1>
</ng-container>
</mat-card>

<!-- <mat-card id="status" appearance="outlined">
<app-status [status]="statusChecks"></app-status>
</mat-card> -->
@for (status of statuses; track $index) {
<mat-card id="status" appearance="outlined">
<app-status [title]="status?.title" [message]="status?.message" [status]="status?.status"></app-status>
</mat-card>
}

<mat-card appearance="outlined" id="card-bars">
<mat-card-header>
<mat-card-title>Engagement</mat-card-title>
</mat-card-header>
<mat-card-content *ngIf="metricsData; else loading">
<app-dashboard-card-bars title="Engagement" [data]="metricsData ? metricsData[metricsData.length - 1] : undefined"
[totalSeats]="totalSeats"></app-dashboard-card-bars>
</mat-card-content>
</mat-card>
<mat-card appearance="outlined" id="drilldown-bar-chart">
<mat-card-header>
<mat-card-title>Engagement Breakdown</mat-card-title>
</mat-card-header>
<mat-card-content>
<ng-container *ngIf="metricsData; else loading">
<app-dashboard-card-drilldown-bar-chart [data]="metricsData"></app-dashboard-card-drilldown-bar-chart>
</ng-container>
</mat-card-content>
</mat-card>
<mat-card id="active-users" appearance="outlined">
<mat-card-header>
<mat-card-title>Most Active Users</mat-card-title>
</mat-card-header>
<ng-container *ngIf="activityTotals; else loading">
<app-active-users-chart [data]="activityTotals" [chartOptions]="chartOptions"></app-active-users-chart>
</ng-container>
</mat-card>
</div>
</div>
<ng-template #loading>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@use '@angular/material' as mat;

.cards-grid {
margin-top:24px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
Expand Down Expand Up @@ -28,9 +29,9 @@
overflow: hidden;
}

#status {
grid-column: span 3;
}
// #status {
// grid-column: span 3;
// }
}

/* Add media query for smaller screens */
Expand Down
Loading
Loading