Skip to content

Commit

Permalink
feat: add overview of failed score assignments
Browse files Browse the repository at this point in the history
  • Loading branch information
FreekBes committed Dec 23, 2024
1 parent c5fac25 commit 7f52088
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 16 deletions.
54 changes: 43 additions & 11 deletions src/routes/admin/points.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
});

// Custom point types
app.post('/admin/points/manual/event', async (req, res) => {
app.post('/admin/points/manual/event/assign', async (req, res) => {
try {
const eventName = req.body.event_name;
const eventType = req.body.event_type;
Expand All @@ -378,7 +378,7 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
}

const sessionUser = req.user as ExpressIntraUser;
console.log(`User ${sessionUser.login} is assigning points manually for ${eventType} "${eventName}" to the following logins: ${logins.split('\n').join(', ')}`);
console.log(`User ${sessionUser.login} is assigning points manually for ${eventType} "${eventName}" to the following logins: ${logins.replaceAll('\r', '').split('\n').join(', ')}`);

// Get the fixed type ID for the event type
const fixedPointType = await prisma.codamCoalitionFixedType.findFirst({
Expand Down Expand Up @@ -415,8 +415,11 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
return res.status(404).send('Intra event not found');
}

// Verify the logins exist in our database
const loginsArray = logins.split('\n').map((login: string) => login.trim());
// Init an array to store failed score creations to display to the user later
const failedScores: { login: string, amount: number, error: string }[] = [];

// Verify the logins exist in our database, removing duplicate logins using a Set
const loginsArray: string[] = Array.from(new Set(logins.split('\n').map((login: string) => login.trim())));
const users = await prisma.intraUser.findMany({
where: {
login: {
Expand All @@ -431,7 +434,9 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
if (users.length !== loginsArray.length) {
const missingLogins = loginsArray.filter((login: string) => !users.find((user: any) => user.login === login));
console.log(`The following logins have not been found in the coalition system: ${missingLogins.join(', ')}`);
return res.status(400).send(`The following logins have not been found in the coalition system: ${missingLogins.join(', ')}`);
for (const missingLogin of missingLogins) {
failedScores.push({ login: missingLogin, amount: fixedPointType.point_amount, error: 'Login not found in coalition system' });
}
}

// Assign the points
Expand All @@ -443,6 +448,7 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
// Warning: do not try to use eventDate as the score assignation date! If the event was organized in a past season, this past season could be influenced.
if (!score) {
console.warn(`Failed to create score for user ${user.login} for event ${intraEvent.id}`);
failedScores.push({ login: user.login, amount: 0, error: 'Failed to create score' });
continue;
}
scores.push(score);
Expand All @@ -452,6 +458,7 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
return res.render('admin/points/manual/added.njk', {
redirect: `/admin/points/manual/${eventType}`,
scores,
failedScores,
});
}
catch (err) {
Expand All @@ -460,14 +467,15 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
}
});

app.post('/admin/points/manual/custom', async (req, res) => {
app.post('/admin/points/manual/custom/assign', async (req, res) => {
try {
const login = req.body.login;
const pointAmount = parseInt(req.body.point_amount);
const reason = req.body.reason;
if (!login || isNaN(pointAmount) || !reason) {
return res.status(400).send('Invalid input');
}
const redirect = `/admin/points/manual/custom#single-score-form`;

const sessionUser = req.user as ExpressIntraUser;
console.log(`User ${sessionUser.login} is assigning ${pointAmount} custom points manually to ${login} for reason "${reason}"`);
Expand All @@ -484,16 +492,29 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
});
if (!user) {
console.log(`The login ${login} has not been found in the coalition system`);
return res.status(400).send(`The login ${login} has not been found in the coalition system`);
return res.render('admin/points/manual/added.njk', {
redirect,
scores: [],
failedScores: [{ login, amount: pointAmount, error: 'Login not found in coalition system' }],
});
}

// Assign the points
const score = await createScore(prisma, null, null, user.id, pointAmount, reason);
if (!score) {
console.warn(`Failed to create score for user ${user.login}`);
return res.render('admin/points/manual/added.njk', {
redirect,
scores: [],
failedScores: [{ login, amount: pointAmount, error: 'Failed to create score' }],
});
}

// Display the points assigned
return res.render('admin/points/manual/added.njk', {
redirect: `/admin/points/manual/custom#single-score-form`,
redirect,
scores: [score],
failedScores: [],
});
}
catch (err) {
Expand All @@ -502,7 +523,7 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
}
});

app.post('/admin/points/manual/custom-csv', async (req, res) => {
app.post('/admin/points/manual/custom-csv/assign', async (req, res) => {
try {
const csv = req.body.csv;
if (!csv) {
Expand All @@ -514,7 +535,7 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien

// Parse csv
const lines = csv.split('\n');
const scoresToCreate: { login: string, points: number, reason: string }[] = [];
let scoresToCreate: { login: string, points: number, reason: string }[] = [];
let lineNumber = 0;
for (const line of lines) {
lineNumber++;
Expand Down Expand Up @@ -542,6 +563,7 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien

scoresToCreate.push({ login, points, reason });
}
const failedScores: { login: string, amount: number, error: string }[] = [];

// Verify the logins exist in our database
const loginsArray = scoresToCreate.map((score) => score.login);
Expand All @@ -561,7 +583,14 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
if (users.length !== uniqueUsers.length) {
const missingLogins = uniqueUsers.filter((login: string) => !users.find((user: any) => user.login === login));
console.log(`The following logins have not been found in the coalition system: ${missingLogins.join(', ')}`);
return res.status(400).send(`The following logins have not been found in the coalition system: ${missingLogins.join(', ')}`);
for (const missingLogin of missingLogins) {
// Add error message for each missing login
for (const score of scoresToCreate.filter((score) => score.login === missingLogin)) {
failedScores.push({ login: missingLogin, amount: score.points, error: 'Login not found in coalition system' });
}
// Remove each missing login from the scoresToCreate array
scoresToCreate = scoresToCreate.filter((score) => score.login !== missingLogin);
}
}

// Assign the points
Expand All @@ -570,12 +599,14 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
const user = users.find((user: any) => user.login === scoreToCreate.login);
if (!user) {
console.warn(`User not found for login ${scoreToCreate.login}, which is weird, because earlier we did select all users with specified logins...`);
failedScores.push({ login: scoreToCreate.login, amount: scoreToCreate.points, error: 'User not found' });
continue;
}
console.log(`User ${sessionUser.login} is assigning ${scoreToCreate.points} custom points manually to ${user.login} with reason "${scoreToCreate.reason}"`);
const score = await createScore(prisma, null, null, user.id, scoreToCreate.points, scoreToCreate.reason);
if (!score) {
console.warn(`Failed to create score for user ${user.login}`);
failedScores.push({ login: user.login, amount: scoreToCreate.points, error: 'Failed to create score' });
continue;
}
scores.push(score);
Expand All @@ -585,6 +616,7 @@ export const setupAdminPointsRoutes = function(app: Express, prisma: PrismaClien
return res.render('admin/points/manual/added.njk', {
redirect: `/admin/points/manual/custom#many-score-form`,
scores,
failedScores,
});
}
catch (err) {
Expand Down
26 changes: 24 additions & 2 deletions templates/admin/points/manual/added.njk
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,30 @@
{% set title = "Points assigner" %}

{% block content %}
<h2 class="mb-3">Points assigned</h2>
<p>The following points have been assigned successfully:</p>
<h2 class="mb-3">Result</h2>
{% if failedScores|length > 0 %}
<div class="alert alert-danger">The following points could not be assigned. Please check the error messages below.</div>
<table class="table">
<thead>
<tr>
<th>Login</th>
<th>Points not assigned</th>
<th>Error message</th>
</tr>
</thead>
<tbody>
{% for failedScore in failedScores %}
<tr>
<td><a href="/profile/{{ failedScore.login }}">{{ failedScore.login }}</a></td>
<td>{{ failedScore.amount }}</td>
<td>{{ failedScore.error }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr style="margin-top: 80px;">
{% endif %}
<div class="alert alert-success">The following points have been assigned successfully:</div>
<table class="table coalition-colored" id="points-added">
<thead>
<tr>
Expand Down
4 changes: 2 additions & 2 deletions templates/admin/points/manual/custom.njk
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</li>
</ul>

<form class="mb-4" action="/admin/points/manual/custom" method="post" id="single-score-form">
<form class="mb-4" action="/admin/points/manual/custom/assign" method="post" id="single-score-form">
<div class="mb-2">
<label for="login" class="form-label">Login student</label>
<input type="text" id="login" name="login" class="form-control" placeholder="Login" required>
Expand All @@ -44,7 +44,7 @@
<button type="submit" class="btn btn-primary">Assign single score</button>
</form>

<form class="mb-4" action="/admin/points/manual/custom-csv" method="post" id="many-score-form">
<form class="mb-4" action="/admin/points/manual/custom-csv/assign" method="post" id="many-score-form">
<div class="mb-2">
<label for="csv" class="form-label">Paste your CSV file or upload a CSV file:</label>
<input type="file" id="csv-file" name="csv-file" class="form-control form-control-sm" accept=".csv">
Expand Down
2 changes: 1 addition & 1 deletion templates/admin/points/manual/event.njk
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@

<hr>

<form action="/admin/points/manual/event" method="post" class="mb-4">
<form action="/admin/points/manual/event/assign" method="post" class="mb-4">
<div class="mb-2">
<label for="event_name" class="form-label">Intra event</label>
<input id="event_name" name="event_name" class="form-control" placeholder="Intra event" required>
Expand Down

0 comments on commit 7f52088

Please sign in to comment.