Skip to content

Commit dbdea1a

Browse files
authored
Merge pull request #285 from uwblueprint/formatting-fixes
Formatting fixes are largely done, linter is passing. And app is working as expected after changes.
2 parents 8971509 + 76e5e51 commit dbdea1a

File tree

98 files changed

+4533
-4715
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+4533
-4715
lines changed

.github/workflows/deploy.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ jobs:
3737
run: |
3838
cd backend && yarn install
3939
cd ../frontend && yarn install
40-
40+
41+
- name: Generate Prisma Client
42+
run: |
43+
cd backend
44+
npx prisma generate
45+
4146
- name: Run backend tests
4247
run: |
4348
cd backend

.github/workflows/lint.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: Lint codebase
2+
3+
on:
4+
push:
5+
branches:
6+
- dev
7+
paths:
8+
- "frontend/**"
9+
- "backend/**"
10+
pull_request:
11+
branches:
12+
- dev
13+
paths:
14+
- "frontend/**"
15+
- "backend/**"
16+
17+
jobs:
18+
run-lint:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v2
23+
24+
- name: Filter changed files
25+
uses: dorny/paths-filter@v2
26+
id: changes
27+
with:
28+
filters: |
29+
frontend:
30+
- "frontend/**"
31+
backend:
32+
- "backend/**"
33+
34+
- name: Set up Node.js
35+
if: steps.changes.outputs.frontend == 'true' || steps.changes.outputs.backend == 'true'
36+
uses: actions/setup-node@v4
37+
with:
38+
node-version: "18.18.2"
39+
cache: "yarn"
40+
cache-dependency-path: |
41+
frontend/yarn.lock
42+
backend/yarn.lock
43+
44+
- name: Install Node.js dependencies
45+
if: steps.changes.outputs.frontend == 'true' || steps.changes.outputs.backend == 'true'
46+
run: yarn --cwd ./frontend --prefer-offline && yarn --cwd ./backend --prefer-offline
47+
48+
- name: Generate Prisma Client
49+
if: steps.changes.outputs.backend == 'true'
50+
working-directory: ./backend
51+
run: npx prisma generate
52+
53+
- name: Lint frontend
54+
if: always() &&steps.changes.outputs.frontend == 'true'
55+
working-directory: ./frontend
56+
run: yarn lint
57+
58+
- name: Lint backend
59+
if: always() &&steps.changes.outputs.backend == 'true'
60+
working-directory: ./backend
61+
run: yarn lint

backend/crons/index.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import expireAdminNotes from "./scripts/expireAdminNotes";
33
import expireAdminAnnouncements from "./scripts/expireAdminAnnouncements";
44
import expireLoginStreak from "./scripts/expireLoginStreak";
55
import createAssignedTasks from "./scripts/createAssignedTask";
6-
import { sendWeeklyReports, sendMonthlyReports } from "./scripts/sendReportEmails";
6+
import {
7+
sendWeeklyReports,
8+
sendMonthlyReports,
9+
} from "./scripts/sendReportEmails";
710

811
cron.schedule("0 0 * * * *", async () => {
912
const res = await expireAdminNotes();
@@ -32,7 +35,7 @@ cron.schedule("0 0 0 * * *", async () => {
3235
}
3336
});
3437

35-
cron.schedule("0 0 * * 1", async () => {
38+
cron.schedule("0 0 * * 1", async () => {
3639
const res = await createAssignedTasks();
3740
if (res) {
3841
console.log("Created assigned tasks");
@@ -44,8 +47,10 @@ cron.schedule("0 0 * * 1", async () => {
4447
// Weekly reports - every Monday at 9 AM
4548
cron.schedule("0 9 * * 1", async () => {
4649
const timestamp = new Date().toISOString();
47-
console.log(`[${timestamp}] Starting weekly report generation and emailing...`);
48-
50+
console.log(
51+
`[${timestamp}] Starting weekly report generation and emailing...`
52+
);
53+
4954
const res = await sendWeeklyReports();
5055
if (res) {
5156
console.log(`[${timestamp}] Weekly reports sent successfully`);
@@ -57,8 +62,10 @@ cron.schedule("0 9 * * 1", async () => {
5762
// Monthly reports - first day of month at 9 AM
5863
cron.schedule("0 9 1 * *", async () => {
5964
const timestamp = new Date().toISOString();
60-
console.log(`[${timestamp}] Starting monthly report generation and emailing...`);
61-
65+
console.log(
66+
`[${timestamp}] Starting monthly report generation and emailing...`
67+
);
68+
6269
const res = await sendMonthlyReports();
6370
if (res) {
6471
console.log(`[${timestamp}] Monthly reports sent successfully`);
Lines changed: 96 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1+
import {
2+
TaskType,
3+
RecurrenceFrequency,
4+
TimeOption,
5+
DayOfWeek,
6+
} from "@prisma/client";
17
import prisma from "../../prisma";
2-
import { TaskType, RecurrenceFrequency, TimeOption, DayOfWeek } from "@prisma/client";
38
import { formatDateTime, getToday } from "../../utils/formatDateTime";
49

510
async function createAssignedTasks(): Promise<boolean> {
611
try {
712
const participants = await prisma.participant.findMany({
813
where: {
914
OR: [{ departure_date: null }, { departure_date: { gt: getToday() } }],
10-
}
15+
},
1116
});
1217

1318
const requiredTasks = await prisma.task.findMany({
1419
where: { task_type: TaskType.REQUIRED },
15-
})
20+
});
1621

1722
const weekdayOffsets: { [key in DayOfWeek]: number } = {
1823
MONDAY: 0,
@@ -24,86 +29,102 @@ async function createAssignedTasks(): Promise<boolean> {
2429
SUNDAY: 6,
2530
};
2631

27-
const mondayOfCurrentWeek = new Date();
32+
const mondayOfCurrentWeek = new Date();
33+
34+
await Promise.all(
35+
participants.map(async (participant) => {
36+
return Promise.all(
37+
requiredTasks.map(async (task) => {
38+
if (
39+
task.recurrence_preference ===
40+
RecurrenceFrequency.EVERY_SELECTED_DAYS ||
41+
task.recurrence_preference === RecurrenceFrequency.DAILY
42+
) {
43+
await Promise.all(
44+
task.repeat_days.map(async (repeatDay) => {
45+
const offset = weekdayOffsets[repeatDay];
46+
const taskDate = new Date(mondayOfCurrentWeek);
47+
taskDate.setDate(mondayOfCurrentWeek.getDate() + offset);
48+
let startDate: Date;
49+
let endDate: Date;
50+
if (task.time_preference === TimeOption.SPECIFIC) {
51+
startDate = new Date(taskDate);
52+
endDate = new Date(taskDate);
53+
if (task.start_time) {
54+
const [hours, minutes] = task.start_time
55+
.split(":")
56+
.map(Number);
57+
startDate.setHours(hours, minutes, 0, 0);
58+
}
59+
if (task.end_time) {
60+
const [hours, minutes] = task.end_time
61+
.split(":")
62+
.map(Number);
63+
endDate.setHours(hours, minutes, 0, 0);
64+
}
65+
} else {
66+
startDate = new Date(taskDate);
67+
startDate.setHours(0, 0, 0, 0);
68+
endDate = new Date(taskDate);
69+
endDate.setHours(23, 59, 0, 0);
70+
}
71+
const startDateString = formatDateTime(startDate, true);
72+
const endDateString = formatDateTime(endDate, true);
73+
await prisma.assignedTask.create({
74+
data: {
75+
participant_id: participant.participant_id,
76+
task_name: task.task_name,
77+
task_type: task.task_type,
78+
start_date: startDateString,
79+
end_date: endDateString,
80+
marillac_bucks_addition: task.marillac_bucks_addition,
81+
marillac_bucks_deduction: task.marillac_bucks_deduction,
82+
comment: task.comment,
83+
},
84+
});
85+
})
86+
);
87+
} else if (
88+
task.recurrence_preference ===
89+
RecurrenceFrequency.ANY_SELECTED_DAYS
90+
) {
91+
const firstDayOffset = weekdayOffsets[task.repeat_days[0]];
92+
const lastDayOffset =
93+
weekdayOffsets[task.repeat_days[task.repeat_days.length - 1]];
2894

29-
for (const participant of participants) {
30-
for (const task of requiredTasks) {
31-
if (task.recurrence_preference === RecurrenceFrequency.EVERY_SELECTED_DAYS ||
32-
task.recurrence_preference === RecurrenceFrequency.DAILY) {
33-
for (const repeatDay of task.repeat_days) {
34-
const offset = weekdayOffsets[repeatDay];
35-
const taskDate = new Date(mondayOfCurrentWeek);
36-
taskDate.setDate(mondayOfCurrentWeek.getDate() + offset);
37-
let startDate: Date;
38-
let endDate: Date;
39-
if (task.time_preference === TimeOption.SPECIFIC) {
40-
startDate = new Date(taskDate);
41-
endDate = new Date(taskDate);
42-
if (task.start_time) {
43-
const [hours, minutes] = task.start_time.split(":").map(Number);
44-
startDate.setHours(hours, minutes, 0, 0);
45-
}
46-
if (task.end_time) {
47-
const [hours, minutes] = task.end_time.split(":").map(Number);
48-
endDate.setHours(hours, minutes, 0, 0);
49-
}
50-
} else {
51-
startDate = new Date(taskDate);
95+
const startDate = new Date(mondayOfCurrentWeek);
96+
startDate.setDate(mondayOfCurrentWeek.getDate() + firstDayOffset);
5297
startDate.setHours(0, 0, 0, 0);
53-
endDate = new Date(taskDate);
98+
99+
const endDate = new Date(mondayOfCurrentWeek);
100+
endDate.setDate(mondayOfCurrentWeek.getDate() + lastDayOffset);
54101
endDate.setHours(23, 59, 0, 0);
55-
}
56-
const startDateString = formatDateTime(startDate, true);
57-
const endDateString = formatDateTime(endDate, true);
58-
await prisma.assignedTask.create({
59-
data: {
60-
participant_id: participant.participant_id,
61-
task_name: task.task_name,
62-
task_type: task.task_type,
63-
start_date: startDateString,
64-
end_date: endDateString,
65-
marillac_bucks_addition: task.marillac_bucks_addition,
66-
marillac_bucks_deduction: task.marillac_bucks_deduction,
67-
comment: task.comment,
68-
},
69-
});
70-
}
71-
} else if (task.recurrence_preference === RecurrenceFrequency.ANY_SELECTED_DAYS) {
72102

73-
const firstDayOffset = weekdayOffsets[task.repeat_days[0]];
74-
const lastDayOffset = weekdayOffsets[task.repeat_days[task.repeat_days.length - 1]];
75-
76-
const startDate = new Date(mondayOfCurrentWeek);
77-
startDate.setDate(mondayOfCurrentWeek.getDate() + firstDayOffset);
78-
startDate.setHours(0, 0, 0, 0);
79-
80-
const endDate = new Date(mondayOfCurrentWeek);
81-
endDate.setDate(mondayOfCurrentWeek.getDate() + lastDayOffset);
82-
endDate.setHours(23, 59, 0, 0);
83-
84-
const startDateString = formatDateTime(startDate, true);
85-
const endDateString = formatDateTime(endDate, true);
86-
87-
await prisma.assignedTask.create({
88-
data: {
89-
participant_id: participant.participant_id,
90-
task_name: task.task_name,
91-
task_type: task.task_type,
92-
start_date: startDateString,
93-
end_date: endDateString,
94-
marillac_bucks_addition: task.marillac_bucks_addition,
95-
marillac_bucks_deduction: task.marillac_bucks_deduction,
96-
comment: task.comment,
97-
},
98-
});
99-
}
100-
}
101-
}
103+
const startDateString = formatDateTime(startDate, true);
104+
const endDateString = formatDateTime(endDate, true);
105+
106+
await prisma.assignedTask.create({
107+
data: {
108+
participant_id: participant.participant_id,
109+
task_name: task.task_name,
110+
task_type: task.task_type,
111+
start_date: startDateString,
112+
end_date: endDateString,
113+
marillac_bucks_addition: task.marillac_bucks_addition,
114+
marillac_bucks_deduction: task.marillac_bucks_deduction,
115+
comment: task.comment,
116+
},
117+
});
118+
}
119+
})
120+
);
121+
})
122+
);
102123
return true;
103124
} catch (err) {
104125
console.error(err);
105126
return false;
106127
}
107-
};
128+
}
108129

109130
export default createAssignedTasks;

backend/crons/scripts/generateDataReport.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ interface LoginStats {
4141
login_date: string;
4242
}
4343

44-
interface DataReport {
44+
export interface DataReport {
4545
reportPeriod: string;
4646
startDate: string;
4747
endDate: string;
@@ -54,7 +54,7 @@ interface DataReport {
5454
}
5555

5656
// Main function to generate a json for data report
57-
async function generateDataReport(
57+
export async function generateDataReport(
5858
params: DataReportParams
5959
): Promise<DataReport | null> {
6060
try {
@@ -287,4 +287,3 @@ export async function generateMonthlyReport(): Promise<boolean> {
287287

288288
// Export the main function for generating a data report by default
289289
export default generateDataReport;
290-

backend/crons/scripts/generateDataReportCSV.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import path from "path";
22
import fs from "fs/promises";
3-
import generateDataReport from "./generateDataReport";
3+
import { generateDataReport, DataReport } from "./generateDataReport";
44

55
// Convert JSON data to CSV format
6-
function jsonToCsv(data: any[], headers: string[]): string {
7-
if (data.length === 0) return headers.join(",") + "\n";
6+
function jsonToCsv(data: object[], headers: string[]): string {
7+
if (data.length === 0) return `${headers.join(",")}\n`;
88

99
const csvRows = [headers.join(",")];
1010

11-
for (const row of data) {
11+
const rows = data.map((row) => {
1212
const values = headers.map((header) => {
13-
const value = row[header];
13+
const value = (row as Record<string, unknown>)[header];
1414
// Escape commas and quotes in CSV
1515
if (
1616
typeof value === "string" &&
@@ -20,14 +20,15 @@ function jsonToCsv(data: any[], headers: string[]): string {
2020
}
2121
return value ?? "";
2222
});
23-
csvRows.push(values.join(","));
24-
}
23+
return values.join(",");
24+
});
25+
csvRows.push(...rows);
2526

26-
return csvRows.join("\n") + "\n";
27+
return `${csvRows.join("\n")}\n`;
2728
}
2829

2930
// Generate a single comprehensive CSV with all data types
30-
function generateComprehensiveCSV(report: any): string {
31+
function generateComprehensiveCSV(report: DataReport): string {
3132
const csvSections: string[] = [];
3233

3334
// Report header information
@@ -207,4 +208,3 @@ export async function generateMonthlyReportCSV(): Promise<string | null> {
207208
return null;
208209
}
209210
}
210-

0 commit comments

Comments
 (0)