1
1
import { groupBy } from 'lodash' ;
2
2
3
+ import { Instructor } from '../lib/types' ;
3
4
import { Course } from '../model/Course' ;
4
- import { Instructor } from '../model/Instructor' ;
5
5
import type { Schedule } from '../model/Schedule' ;
6
6
7
7
const TERM_ORDER = [ 'Winter' , 'Summer' , 'Fall' ] ;
8
8
9
9
/**
10
10
* Groups course instructors by their terms, but only for current terms.
11
+ *
11
12
* Creates empty arrays for current terms that have no instructors.
13
+ *
12
14
* @param {Course } course - The course object containing instructors and terms
13
15
* @returns {Record<string, Instructor[]> } Object mapping terms to arrays of instructors
14
16
*/
@@ -33,9 +35,11 @@ export const groupCurrentCourseTermInstructors = (
33
35
34
36
/**
35
37
* Determines the current and next two academic terms based on the current date.
38
+ *
36
39
* - May-July: Returns [Summer current, Fall current, Winter next]
37
40
* - August-December: Returns [Fall current, Winter next, Summer next]
38
41
* - January-April: Returns [Fall previous, Winter current, Summer current]
42
+ *
39
43
* @returns {[string, string, string] } Array of three consecutive terms
40
44
*/
41
45
export const getCurrentTerms = ( ) : [ string , string , string ] => {
@@ -57,9 +61,11 @@ export const getCurrentTerms = (): [string, string, string] => {
57
61
58
62
/**
59
63
* Determines the current academic term based on the current date.
64
+ *
60
65
* - May-July: Summer <year>
61
66
* - August-December: Fall <year>
62
67
* - January-April: Winter <year>
68
+ *
63
69
* @returns string The current term
64
70
*/
65
71
export const getCurrentTerm = ( ) : string => {
@@ -80,7 +86,9 @@ export const getCurrentTerm = (): string => {
80
86
81
87
/**
82
88
* Compares two academic terms for sorting.
89
+ *
83
90
* Terms are compared first by year, then by season according to TERM_ORDER.
91
+ *
84
92
* @param {string } a - First term string (e.g., "Fall 2023")
85
93
* @param {string } b - Second term string (e.g., "Winter 2024")
86
94
* @returns {number } Negative if a comes before b, positive if b comes before a, 0 if equal
@@ -93,7 +101,9 @@ export const compareTerms = (a: string, b: string): number => {
93
101
94
102
/**
95
103
* Sorts an array of academic terms chronologically.
104
+ *
96
105
* Uses compareTerms to determine order.
106
+ *
97
107
* @param {string[] } terms - Array of term strings to sort
98
108
* @returns {string[] } Sorted array of terms
99
109
*/
@@ -103,8 +113,11 @@ export const sortTerms = (terms: string[]): string[] => {
103
113
104
114
/**
105
115
* Sorts course schedules by block type and number.
116
+ *
106
117
* Order priority: Lec > Lab > Seminar > Tut > Conf
118
+ *
107
119
* Within each type, sorts numerically by block number.
120
+ *
108
121
* @param {Schedule[] } schedules - Array of course schedules to sort
109
122
* @returns {Schedule[] } Sorted array of schedules
110
123
*/
@@ -125,7 +138,9 @@ export const sortSchedulesByBlocks = (schedules: Schedule[]): Schedule[] => {
125
138
126
139
/**
127
140
* Converts a course ID to a URL-friendly parameter format.
141
+ *
128
142
* Example: "COMP202" -> "comp-202"
143
+ *
129
144
* @param {string } courseId - The course ID to convert
130
145
* @returns {string } URL-friendly course ID
131
146
*/
@@ -134,6 +149,7 @@ export const courseIdToUrlParam = (courseId: string): string =>
134
149
135
150
/**
136
151
* Capitalizes the first character of a string.
152
+ *
137
153
* @param {string } s - The string to capitalize
138
154
* @returns {string } Capitalized string
139
155
*/
@@ -142,7 +158,9 @@ export const capitalize = (s: string): string =>
142
158
143
159
/**
144
160
* Ensures a string ends with a period.
161
+ *
145
162
* Adds a period if one is not already present.
163
+ *
146
164
* @param {string } s - The string to punctuate
147
165
* @returns {string } String ending with a period
148
166
*/
@@ -153,8 +171,11 @@ const COURSE_CODE_REGEX = /^(([A-Z0-9]){4} [0-9]{3}(D1|D2|N1|N2|J1|J2|J3)?)$/;
153
171
154
172
/**
155
173
* Validates if a string matches the course code format.
174
+ *
156
175
* Valid format: 4 alphanumeric chars + space + 3 digits + optional suffix
176
+ *
157
177
* Suffixes: D1, D2, N1, N2, J1, J2, J3
178
+ *
158
179
* @param {string } s - The string to validate
159
180
* @returns {boolean } True if string is a valid course code
160
181
*/
@@ -163,7 +184,9 @@ export const isValidCourseCode = (s: string): boolean =>
163
184
164
185
/**
165
186
* Inserts a delimiter between the subject and number portions of a course code.
187
+ *
166
188
* Example: spliceCourseCode("COMP202", "-") -> "COMP-202"
189
+ *
167
190
* @param {string } courseCode - The course code to splice
168
191
* @param {string } delimiter - The delimiter to insert
169
192
* @returns {string } Course code with delimiter inserted
@@ -175,14 +198,17 @@ export const spliceCourseCode = (
175
198
176
199
/**
177
200
* Rounds a number to 2 decimal places.
201
+ *
178
202
* @param {number } n - Number to round
179
203
* @returns {number } Rounded number
180
204
*/
181
205
export const round2Decimals = ( n : number ) : number => Math . round ( n * 100 ) / 100 ;
182
206
183
207
/**
184
208
* Performs a true modulo operation (different from JavaScript's % operator).
209
+ *
185
210
* Always returns a positive number, even when inputs are negative.
211
+ *
186
212
* @param {number } n - Dividend
187
213
* @param {number } m - Divisor
188
214
* @returns {number } Positive modulo result
@@ -191,6 +217,7 @@ export const mod = (n: number, m: number): number => ((n % m) + m) % m;
191
217
192
218
/**
193
219
* Custom error class for date-related errors.
220
+ *
194
221
* @extends Error
195
222
*/
196
223
export class InvalidDateError extends Error {
@@ -202,8 +229,11 @@ export class InvalidDateError extends Error {
202
229
203
230
/**
204
231
* Converts a date to a human-readable "time ago" string.
232
+ *
205
233
* Handles various time units from seconds to years.
234
+ *
206
235
* Throws InvalidDateError for null, undefined, invalid formats, or future dates.
236
+ *
207
237
* @param {Date | string | number | null | undefined } date - Date to convert
208
238
* @returns {string } Human-readable time difference (e.g., "2 hours ago")
209
239
* @throws {InvalidDateError } If date is invalid, missing, or in the future
0 commit comments