Skip to content

Commit

Permalink
Merge pull request #84 from austenstone/status
Browse files Browse the repository at this point in the history
Status
  • Loading branch information
austenstone authored Dec 9, 2024
2 parents 30cb830 + 5d05b58 commit f0e48a4
Show file tree
Hide file tree
Showing 27 changed files with 407 additions and 307 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/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
9 changes: 0 additions & 9 deletions backend/src/controllers/survey.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class SurveyController {
})
res.status(201).json(survey);
} catch (error) {
console.log(error);
res.status(500).json(error);
return;
}
Expand All @@ -83,14 +82,6 @@ class SurveyController {
} : {}
} as WhereOptions
});
console.log('test', JSON.stringify({
reason: {
[Op.and]: [
{ [Op.ne]: '' },
{ [Op.gte]: minReasonLength }
]
}
}));
res.status(200).json(surveys);
} 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
47 changes: 34 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';
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 @@ class SeatsService {
}
}

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 @@ class SeatsService {
// 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 @@ class SeatsService {
order: [['last_activity_at', 'ASC']],
where: {
...(org ? { org } : {}),
...Object.getOwnPropertySymbols(dateFilter).length ? { createdAt: dateFilter } : {}
}
}
],
Expand Down Expand Up @@ -230,17 +243,24 @@ class SeatsService {
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
// });
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 +270,7 @@ class SeatsService {
order: [['last_activity_at', 'ASC']],
where: {
...(org ? { org } : {}),
...Object.getOwnPropertySymbols(dateFilter).length ? { createdAt: dateFilter } : {}
}
}]
});
Expand Down
47 changes: 26 additions & 21 deletions backend/src/services/status.service.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
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";

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 @@ -36,26 +35,32 @@ 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;
}

return status;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/database/database.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class DatabaseComponent implements AfterViewInit {
}),
finalize(async () => {
await this.router.navigate(['/copilot'], {
queryParams: { celebrate: true }
// queryParams: { celebrate: true }
})
})
).subscribe(() => this.checkStatus());
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

0 comments on commit f0e48a4

Please sign in to comment.