Skip to content

Commit

Permalink
add point history overview
Browse files Browse the repository at this point in the history
will need to make it not load all history at some point but make it paginated
  • Loading branch information
FreekBes committed Sep 25, 2024
1 parent 59b0cf2 commit 3fe2ae4
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/dev/create_quiz_questions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const createQuizQuestionAnswerSet = async function(question: string, answers: {
question: question
},
});
const coalitionIds = await getCoalitionIds();
const coalitionIds = await getCoalitionIds(prisma);
// Randomize the order of the answers to make it less obvious to the client
// which answer belongs to which coalition (based on lower answer id in the DB = earlier created = vela, pyxis, cetus etc.)
answers = answers.sort(() => Math.random() - 0.5);
Expand Down
11 changes: 11 additions & 0 deletions src/handlers/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,15 @@ export const setupNunjucksFilters = function(app: Express): void {
}
return date.getTime();
});

// Add filter to replace hex color with RGBA color
nunjucksEnv.addFilter('rgba', (hex: string, alpha: number) => {
if (!hex || !hex.startsWith('#')) {
return `rgba(0, 0, 0, ${alpha})`;
}
const r = parseInt(hex.substring(1, 3), 16);
const g = parseInt(hex.substring(3, 5), 16);
const b = parseInt(hex.substring(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
});
};
20 changes: 10 additions & 10 deletions src/routes/admin/apisearcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'page[size]': '100',
'sort': '-updated_at'
});
const modifiedTeams = await parseTeamInAPISearcher(teams);
const modifiedTeams = await parseTeamInAPISearcher(prisma, teams);
return res.json(modifiedTeams);
}
catch (err) {
Expand Down Expand Up @@ -111,7 +111,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'page[size]': '100',
'sort': '-updated_at'
});
const modifiedTeams = await parseTeamInAPISearcher(teams);
const modifiedTeams = await parseTeamInAPISearcher(prisma, teams);
return res.json(modifiedTeams);
}
catch (err) {
Expand All @@ -128,7 +128,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
const teams = await fetchSingleApiPage(api, '/teams/', {
'filter[id]': teamId,
});
const modifiedTeams = await parseTeamInAPISearcher(teams);
const modifiedTeams = await parseTeamInAPISearcher(prisma, teams);
return res.json(modifiedTeams);
}
catch (err) {
Expand All @@ -150,7 +150,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'page[size]': '100',
'sort': '-updated_at'
});
const modifiedTeams = await parseTeamInAPISearcher(teams);
const modifiedTeams = await parseTeamInAPISearcher(prisma, teams);
return res.json(modifiedTeams);
}
catch (err) {
Expand Down Expand Up @@ -181,7 +181,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'page[size]': '100',
'sort': '-updated_at'
});
const modifiedTeams = await parseTeamInAPISearcher(teams);
const modifiedTeams = await parseTeamInAPISearcher(prisma, teams);
return res.json(modifiedTeams);
}
catch (err) {
Expand All @@ -199,7 +199,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'filter[id]': teamId,
'filter[project_id]': EXAM_PROJECT_IDS.join(','),
});
const modifiedTeams = await parseTeamInAPISearcher(teams);
const modifiedTeams = await parseTeamInAPISearcher(prisma, teams);
return res.json(modifiedTeams);
}
catch (err) {
Expand All @@ -221,7 +221,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'page[size]': '25',
'sort': '-filled_at'
});
const modifiedScaleTeams = await parseScaleTeamInAPISearcher(evaluations);
const modifiedScaleTeams = await parseScaleTeamInAPISearcher(prisma, evaluations);
return res.json(modifiedScaleTeams);
}
catch (err) {
Expand Down Expand Up @@ -255,7 +255,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'page[size]': '25',
'sort': '-filled_at'
});
const modifiedScaleTeams = await parseScaleTeamInAPISearcher(evaluations);
const modifiedScaleTeams = await parseScaleTeamInAPISearcher(prisma, evaluations);
return res.json(modifiedScaleTeams);
}
catch (err) {
Expand All @@ -275,7 +275,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'page[size]': '25',
'sort': '-filled_at'
});
const modifiedScaleTeams = await parseScaleTeamInAPISearcher(evaluations);
const modifiedScaleTeams = await parseScaleTeamInAPISearcher(prisma, evaluations);
return res.json(modifiedScaleTeams);
}
catch (err) {
Expand All @@ -295,7 +295,7 @@ export const setupAPISearchRoutes = function(app: Express, prisma: PrismaClient)
'page[size]': '25',
'sort': '-filled_at'
});
const modifiedScaleTeams = await parseScaleTeamInAPISearcher(evaluations);
const modifiedScaleTeams = await parseScaleTeamInAPISearcher(prisma, evaluations);
return res.json(modifiedScaleTeams);
}
catch (err) {
Expand Down
50 changes: 49 additions & 1 deletion src/routes/admin/points.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,56 @@
import { Express } from 'express';
import { PrismaClient } from '@prisma/client';
import { CodamCoalition, PrismaClient } from '@prisma/client';
import fs from 'fs';

export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClient): void {
app.get('/admin/points/history', async (req, res) => {
// Retrieve all scores
const scores = await prisma.codamCoalitionScore.findMany({
select: {
id: true,
intra_score_id: true,
amount: true,
reason: true,
created_at: true,
fixed_type_id: true,
type_intra_id: true,
coalition_id: true,
coalition: false,
fixed_type: false,
user: {
select: {
intra_user: {
select: {
login: true,
}
}
},
},
},
orderBy: {
created_at: 'desc',
},
});

// Retrieve all coalitions
const coalitionRows = await prisma.codamCoalition.findMany({
select: {
intra_coalition: true,
},
});

// Create a map of coalition id to coalition object
const coalitions: { [key: number]: any } = {};
for (const row of coalitionRows) {
coalitions[row.intra_coalition.id] = row;
}

return res.render('admin/points/history.njk', {
scores,
coalitions,
});
});

app.get('/admin/points/manual', async (req, res) => {
// Retrieve all fixed point types
const fixedPointTypes = await prisma.codamCoalitionFixedType.findMany({
Expand Down
4 changes: 2 additions & 2 deletions src/sync/coalitions_users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const syncCoalitionUser = async function(coalitionUser: any): Promise<voi
}

// Check if coalitionUser is of any of the coalitions we care about (are in our database)
const coalitionIds = await getCoalitionIds();
const coalitionIds = await getCoalitionIds(prisma);
const coalitionIdsArray = Object.values(coalitionIds);
if (!coalitionIdsArray.includes(coalitionUser.coalition_id)) {
console.warn(`Coalition ${coalitionUser.coalition_id} is not in our database, skipping local CoalitionUser creation...`);
Expand Down Expand Up @@ -73,7 +73,7 @@ export const syncCoalitionUsers = async function(api: Fast42, syncDate: Date): P
const syncSince = new Date(LAST_SHUTDOWN_TIMESTAMP);

// Fetch all of our coalition ids
const coalitionIds = await getCoalitionIds();
const coalitionIds = await getCoalitionIds(prisma);

// Fetch all users from the API updated since the last shutdown
console.log('Coalition IDs used by the coalitions_users synchronization: ', coalitionIds);
Expand Down
9 changes: 5 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { PrismaClient, IntraUser } from "@prisma/client";
import { ExpressIntraUser } from "./sync/oauth";
import Fast42 from "@codam/fast42";
import { INTRA_API_UID, INTRA_API_SECRET } from "./env";
const prisma = new PrismaClient();

export const getAPIClient = async function(): Promise<Fast42> {
return new Fast42([{
Expand Down Expand Up @@ -44,7 +43,7 @@ export const isStaff = async function(intraUser: ExpressIntraUser | IntraUser):
return intraUser.kind === 'admin';
};

export const getCoalitionIds = async function(): Promise<any> {
export const getCoalitionIds = async function(prisma: PrismaClient): Promise<any> {
const coalitionIds = await prisma.intraCoalition.findMany({
select: {
id: true,
Expand All @@ -59,7 +58,7 @@ export const getCoalitionIds = async function(): Promise<any> {
return returnable;
};

export const parseTeamInAPISearcher = async function(teams: any): Promise<any> {
export const parseTeamInAPISearcher = async function(prisma: PrismaClient, teams: any): Promise<any> {
const projects = await prisma.intraProject.findMany({
select: {
id: true,
Expand All @@ -75,6 +74,7 @@ export const parseTeamInAPISearcher = async function(teams: any): Promise<any> {

for (const team of teams) {
// Add the project to the team
// @ts-ignore
const project = projects.find(p => p.id === team.project_id);
team.project = project;

Expand All @@ -90,7 +90,7 @@ export const parseTeamInAPISearcher = async function(teams: any): Promise<any> {
return teams;
};

export const parseScaleTeamInAPISearcher = async function(scaleTeams: any): Promise<any> {
export const parseScaleTeamInAPISearcher = async function(prisma: PrismaClient, scaleTeams: any): Promise<any> {
const projects = await prisma.intraProject.findMany({
select: {
id: true,
Expand All @@ -106,6 +106,7 @@ export const parseScaleTeamInAPISearcher = async function(scaleTeams: any): Prom

for (const scaleTeam of scaleTeams) {
// Add the project to the team
// @ts-ignore
const project = projects.find(p => p.id === scaleTeam.team.project_id);
scaleTeam.team.project = project;

Expand Down
3 changes: 3 additions & 0 deletions static/css/bootstrap-overruler.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.table.coalition-colored > :not(caption) > * > * {
background-color: transparent !important;
}
44 changes: 44 additions & 0 deletions templates/admin/points/history.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{% extends "base.njk" %}
{% set title = "Point History" %}

{% block content %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/admin">Admin</a></li>
<li class="breadcrumb-item active" aria-current="page">Point History</li>
</ol>
</nav>

<table class="table coalition-colored">
<thead>
<th scope="col">Date</th>
<th scope="col">Coalition</th>
<th scope="col">Points</th>
<th scope="col">User</th>
<th scope="col">Reason</th>
<th scope="col">Type</th>
<th scope="col">Intra Type ID</th>
<th scope="col">Intra Score ID</th>
<th scope="col text-end" style="text-align: end;">Actions</th> <!-- for some reason the text-end class doesn't work here -->
</thead>
<tbody>
{% for score in scores %}
<tr style="background: {{ coalitions[score.coalition_id].intra_coalition.color | rgba(0.25) }}">
<td title="{{ score.created_at }}">{{ score.created_at | timeAgo }}</td>
<td>{{ coalitions[score.coalition_id].intra_coalition.name }}</td>
<td>{{ score.amount }}</td>
<td>{{ score.user.intra_user.login }}</td>
<td style="white-space: normal; word-wrap: break-word;">{{ score.reason }}</td>
<td>{{ score.fixed_type_id if score.fixed_type_id else "" }}</td>
<td>{{ score.type_intra_id if score.type_intra_id else "" }}</td>
<td>{{ score.intra_score_id if score.intra_score_id else "not yet synced" }}</td>
<td class="text-end" style="white-space: nowrap;">
<a href="/admin/points/sync/{{ score.id }}" class="btn btn-sm btn-outline-secondary" title="Force synchronize with Intra">Sync</a>
<a href="/admin/points/recalculate/{{ score.id }}" class="btn btn-sm btn-outline-warning" title="Force recalculate this score with the current point system settings">Recalculate</a>
<a href="/admin/points/delete/{{ score.id }}" class="btn btn-sm btn-outline-danger" title="Remove this score and act like it never happened">Remove</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
1 change: 1 addition & 0 deletions templates/base.njk
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<title>{{ title | e if title }}{{ " | " if title }}Codam Coalition System</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
{# <link rel="stylesheet" href="/css/base.css" /> #}
<link rel="stylesheet" href="/css/bootstrap-overruler.css" />
<!-- theme color -->
<meta name="theme-color" content="#6610f2" />
</head>
Expand Down

0 comments on commit 3fe2ae4

Please sign in to comment.