Skip to content

Commit cc82120

Browse files
leslieyip02zwliew
andauthored
feat(website): Add TA mode (#3875)
Co-authored-by: Zhao Wei Liew <[email protected]>
1 parent fec9bfa commit cc82120

Some content is hidden

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

46 files changed

+1020
-265
lines changed

docker-compose.prod.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ services:
4141
- NODE_ENV=production
4242
- PORT=8082
4343
- HOST=0.0.0.0
44-
- PAGE=http://website:8081
44+
- PAGE=http://website:8081/timetable-only/
4545
- CHROME_EXECUTABLE=/usr/bin/chromium-browser
4646
restart: on-failure
4747
labels:

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ services:
4040
- NODE_ENV=development
4141
- PORT=8082
4242
- HOST=0.0.0.0
43-
- PAGE=http://website:8081
43+
- PAGE=http://website:8081/timetable-only/
4444
- CHROME_EXECUTABLE=/usr/bin/chromium-browser
4545
user: ${CURRENT_UID:-1000:1000}
4646
restart: on-failure

export/Dockerfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:12-alpine
1+
FROM node:18-alpine
22

33
# Installs Chromium package.
44
RUN apk update && apk upgrade && \

export/Dockerfile.prod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:12-alpine
1+
FROM node:18-alpine
22

33
# Installs Chromium package.
44
RUN apk update && apk upgrade && \

export/src/data.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ export function validateExportData(data: ExportData) {
6565
Joi.string(),
6666
Joi.object().pattern(Joi.string(), Joi.string()),
6767
);
68+
const taModulesConfigSchema = Joi.object().pattern(
69+
Joi.string(),
70+
Joi.array().length(2).ordered(Joi.string(), Joi.string()),
71+
);
6872
const themeSchema = Joi.object({
6973
id: Joi.string(),
7074
timetableOrientation: Joi.string().valid('HORIZONTAL', 'VERTICAL'),
@@ -75,6 +79,7 @@ export function validateExportData(data: ExportData) {
7579
timetable: timetableSchema,
7680
colors: Joi.object().pattern(Joi.string(), Joi.number().integer().min(0)),
7781
hidden: Joi.array().items(Joi.string()),
82+
ta: taModulesConfigSchema,
7883
settings: Joi.object({
7984
colorScheme: Joi.string().valid('LIGHT_COLOR_SCHEME', 'DARK_COLOR_SCHEME'),
8085
}),

export/src/render.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export async function launch() {
2828
headless: true,
2929
executablePath: config.chromeExecutable,
3030
devtools: !!process.env.DEVTOOLS,
31+
args: ['--disable-gpu'],
3132
});
3233

3334
const page = await browser.newPage();

export/src/types.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,27 @@ import type { Page } from 'puppeteer-core';
33
// These types are duplicated from `website/`.
44
// TODO: Move these types to a shared package.
55
export type TimetableOrientation = 'HORIZONTAL' | 'VERTICAL';
6-
export type Semester = number;
7-
export type SemTimetableConfig = {
8-
[moduleCode: string]: ModuleLessonConfig;
9-
};
106
export type ColorIndex = number;
117
export type ColorMapping = { [moduleCode: string]: ColorIndex };
12-
export type ModuleCode = string;
138
export type ThemeState = Readonly<{
149
id: string;
1510
timetableOrientation: TimetableOrientation;
1611
showTitle: boolean;
1712
}>;
1813
export type ColorScheme = 'LIGHT_COLOR_SCHEME' | 'DARK_COLOR_SCHEME';
14+
export type Semester = number;
15+
export type ClassNo = string; // E.g. "1", "A"
16+
export type LessonType = string; // E.g. "Lecture", "Tutorial"
17+
export type ModuleCode = string; // E.g. "CS3216"
18+
export type SemTimetableConfig = {
19+
[moduleCode: ModuleCode]: ModuleLessonConfig;
20+
};
21+
export interface ModuleLessonConfig {
22+
[lessonType: LessonType]: ClassNo;
23+
}
24+
export type TaModulesConfig = {
25+
[moduleCode: ModuleCode]: [lessonType: LessonType, classNo: ClassNo][];
26+
};
1927

2028
// `ExportData` is duplicated from `website/src/types/export.ts`.
2129
export interface ExportData {
@@ -29,10 +37,6 @@ export interface ExportData {
2937
};
3038
}
3139

32-
export interface ModuleLessonConfig {
33-
[lessonType: string]: string;
34-
}
35-
3640
export interface State {
3741
data: ExportData;
3842
page: Page;

website/src/actions/export.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import type { Module, ModuleCode, Semester } from 'types/modules';
22
import type { ExportData } from 'types/export';
33
import type { Dispatch, GetState } from 'types/redux';
4-
import { hydrateSemTimetableWithLessons } from 'utils/timetables';
4+
import {
5+
hydrateSemTimetableWithLessons,
6+
hydrateTaModulesConfigWithLessons,
7+
} from 'utils/timetables';
58
import { captureException } from 'utils/error';
69
import retryImport from 'utils/retryImport';
710
import { getSemesterTimetableLessons } from 'selectors/timetables';
11+
import { TaModulesConfig } from 'types/timetables';
812
import { SET_EXPORTED_DATA } from './constants';
913

1014
function downloadUrl(blob: Blob, filename: string) {
@@ -31,12 +35,28 @@ export function downloadAsIcal(semester: Semester) {
3135
.then(([ical, icalUtils]) => {
3236
const state = getState();
3337
const { modules } = state.moduleBank;
34-
const hiddenModules: ModuleCode[] = state.timetables.hidden[semester] || [];
38+
const hiddenModules: ModuleCode[] = state.timetables.hidden[semester] ?? [];
39+
const taModules: TaModulesConfig = state.timetables.ta[semester] ?? {};
3540

3641
const timetable = getSemesterTimetableLessons(state)(semester);
3742
const timetableWithLessons = hydrateSemTimetableWithLessons(timetable, modules, semester);
43+
const timetableWithTaLessons = hydrateTaModulesConfigWithLessons(
44+
taModules,
45+
modules,
46+
semester,
47+
);
48+
const filteredTimetableWithLessons = {
49+
...timetableWithLessons,
50+
...timetableWithTaLessons,
51+
};
3852

39-
const events = icalUtils.default(semester, timetableWithLessons, modules, hiddenModules);
53+
const events = icalUtils.default(
54+
semester,
55+
filteredTimetableWithLessons,
56+
modules,
57+
hiddenModules,
58+
taModules,
59+
);
4060
const cal = ical.default({
4161
domain: 'nusmods.com',
4262
prodId: '//NUSMods//NUSMods//EN',

website/src/actions/timetables.ts

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { each, flatMap } from 'lodash';
22

3-
import type { ColorIndex, Lesson, ModuleLessonConfig, SemTimetableConfig } from 'types/timetables';
3+
import type {
4+
ColorIndex,
5+
Lesson,
6+
ModuleLessonConfig,
7+
SemTimetableConfig,
8+
TaModulesConfig,
9+
} from 'types/timetables';
410
import type { Dispatch, GetState } from 'types/redux';
511
import type { ColorMapping } from 'types/reducers';
612
import type { ClassNo, LessonType, Module, ModuleCode, Semester } from 'types/modules';
@@ -19,17 +25,18 @@ import { getModuleTimetable } from 'utils/modules';
1925
export const SET_TIMETABLE = 'SET_TIMETABLE' as const;
2026
export const ADD_MODULE = 'ADD_MODULE' as const;
2127
export const SET_HIDDEN_IMPORTED = 'SET_HIDDEN_IMPORTED' as const;
22-
export const HIDDEN_IMPORTED_SEM = 'HIDDEN_IMPORTED_SEM' as const;
28+
export const SET_TA_IMPORTED = 'SET_TA_IMPORTED' as const;
2329
export const Internal = {
2430
setTimetable(
2531
semester: Semester,
2632
timetable: SemTimetableConfig | undefined,
2733
colors?: ColorMapping,
2834
hiddenModules?: ModuleCode[],
35+
taModules?: TaModulesConfig,
2936
) {
3037
return {
3138
type: SET_TIMETABLE,
32-
payload: { semester, timetable, colors, hiddenModules },
39+
payload: { semester, timetable, colors, hiddenModules, taModules },
3340
};
3441
},
3542

@@ -165,7 +172,8 @@ export function setTimetable(
165172
semester,
166173
validatedTimetable,
167174
colors,
168-
getState().timetables.hidden[HIDDEN_IMPORTED_SEM] || [],
175+
getState().timetables.hidden[semester] ?? [],
176+
getState().timetables.ta[semester] ?? {},
169177
),
170178
);
171179
};
@@ -211,14 +219,25 @@ export function fetchTimetableModules(timetables: SemTimetableConfig[]) {
211219
};
212220
}
213221

214-
export function setHiddenModulesFromImport(hiddenModules: ModuleCode[]) {
215-
return (dispatch: Dispatch) => dispatch(setHiddenImported(hiddenModules));
222+
export function setHiddenModulesFromImport(semester: Semester, hiddenModules: ModuleCode[]) {
223+
return (dispatch: Dispatch) => dispatch(setHiddenImported(semester, hiddenModules));
216224
}
217225

218-
export function setHiddenImported(hiddenModules: ModuleCode[]) {
226+
export function setHiddenImported(semester: Semester, hiddenModules: ModuleCode[]) {
219227
return {
220228
type: SET_HIDDEN_IMPORTED,
221-
payload: { semester: HIDDEN_IMPORTED_SEM, hiddenModules },
229+
payload: { semester, hiddenModules },
230+
};
231+
}
232+
233+
export function setTaModulesFromImport(semester: Semester, taModules: TaModulesConfig) {
234+
return (dispatch: Dispatch) => dispatch(setTaImported(semester, taModules));
235+
}
236+
237+
export function setTaImported(semester: Semester, taModules: TaModulesConfig) {
238+
return {
239+
type: SET_TA_IMPORTED,
240+
payload: { semester, taModules },
222241
};
223242
}
224243

@@ -253,3 +272,37 @@ export function showLessonInTimetable(semester: Semester, moduleCode: ModuleCode
253272
payload: { moduleCode, semester },
254273
};
255274
}
275+
276+
export const ADD_TA_LESSON_IN_TIMETABLE = 'ADD_TA_LESSON_IN_TIMETABLE' as const;
277+
export function addTaLessonInTimetable(
278+
semester: Semester,
279+
moduleCode: ModuleCode,
280+
lessonType: LessonType,
281+
classNo: ClassNo,
282+
) {
283+
return {
284+
type: ADD_TA_LESSON_IN_TIMETABLE,
285+
payload: { semester, moduleCode, lessonType, classNo },
286+
};
287+
}
288+
289+
export const REMOVE_TA_LESSON_IN_TIMETABLE = 'REMOVE_TA_LESSON_IN_TIMETABLE' as const;
290+
export function removeTaLessonInTimetable(
291+
semester: Semester,
292+
moduleCode: ModuleCode,
293+
lessonType: LessonType,
294+
classNo: ClassNo,
295+
) {
296+
return {
297+
type: REMOVE_TA_LESSON_IN_TIMETABLE,
298+
payload: { semester, moduleCode, lessonType, classNo },
299+
};
300+
}
301+
302+
export const DISABLE_TA_MODE_IN_TIMETABLE = 'DISABLE_TA_MODE_IN_TIMETABLE' as const;
303+
export function disableTaModeInTimetable(semester: Semester, moduleCode: ModuleCode) {
304+
return {
305+
type: DISABLE_TA_MODE_IN_TIMETABLE,
306+
payload: { semester, moduleCode },
307+
};
308+
}

website/src/apis/export.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type ExportOptions = {
1010
pixelRatio?: number;
1111
};
1212

13+
// Replace with http://localhost:8080/export when testing export
1314
const baseUrl = 'https://export.nusmods.com/api/export';
1415

1516
function serializeState(

0 commit comments

Comments
 (0)