line height text, refactored course schedule, added string representation functions to course meeting
This commit is contained in:
@@ -72,7 +72,7 @@ export class Course {
|
|||||||
/** Which semester is the course from */
|
/** Which semester is the course from */
|
||||||
semester: Semester;
|
semester: Semester;
|
||||||
|
|
||||||
constructor(course: Course | Serialized<Course>) {
|
constructor(course: Serialized<Course>) {
|
||||||
Object.assign(this, course);
|
Object.assign(this, course);
|
||||||
this.schedule = new CourseSchedule(course.schedule);
|
this.schedule = new CourseSchedule(course.schedule);
|
||||||
}
|
}
|
||||||
|
|||||||
128
src/shared/types/CourseMeeting.ts
Normal file
128
src/shared/types/CourseMeeting.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { Serialized } from 'chrome-extension-toolkit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a map of the days of the week that a class is taught, and the corresponding abbreviation
|
||||||
|
*/
|
||||||
|
export const DAY_MAP = {
|
||||||
|
M: 'Monday',
|
||||||
|
T: 'Tuesday',
|
||||||
|
W: 'Wednesday',
|
||||||
|
TH: 'Thursday',
|
||||||
|
F: 'Friday',
|
||||||
|
S: 'Saturday',
|
||||||
|
SU: 'Sunday',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/** A day of the week that a class is taught */
|
||||||
|
export type Day = typeof DAY_MAP[keyof typeof DAY_MAP];
|
||||||
|
|
||||||
|
/** A physical room that a class is taught in */
|
||||||
|
export type Room = {
|
||||||
|
/** The UT building code for where the class is taught */
|
||||||
|
building: string;
|
||||||
|
/** The room number for where the class is taught */
|
||||||
|
number: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This represents one "Meeting Time" for a course, which includes the day of the week that the course is taught, the time that the course is taught, and the location that the course is taught
|
||||||
|
*/
|
||||||
|
export class CourseMeeting {
|
||||||
|
/** The day of the week that the course is taught */
|
||||||
|
days: Day[];
|
||||||
|
/** The start time of the course, in minutes since midnight */
|
||||||
|
startTime: number;
|
||||||
|
/** The end time of the course, in minutes since midnight */
|
||||||
|
endTime: number;
|
||||||
|
/** The location that the course is taught */
|
||||||
|
room?: Room;
|
||||||
|
|
||||||
|
constructor(meeting: Serialized<CourseMeeting>) {
|
||||||
|
Object.assign(this, meeting);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the string representation of the days of the week that this meeting is taught
|
||||||
|
* @param options options for the string representation
|
||||||
|
* @returns string representation of the days of the week that this meeting is taught
|
||||||
|
*/
|
||||||
|
getDaysString(options: DaysStringOptions): string {
|
||||||
|
let { format, separator } = options;
|
||||||
|
let { days } = this;
|
||||||
|
|
||||||
|
if (format === 'short') {
|
||||||
|
days = Object.keys(DAY_MAP).filter(day => days.includes(DAY_MAP[day as keyof typeof DAY_MAP])) as Day[];
|
||||||
|
}
|
||||||
|
if (separator === 'none') {
|
||||||
|
return days.join('');
|
||||||
|
}
|
||||||
|
const listFormat = new Intl.ListFormat('en-US', {
|
||||||
|
style: separator,
|
||||||
|
type: 'conjunction',
|
||||||
|
});
|
||||||
|
return listFormat.format(days);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the string representation of the time range for the course
|
||||||
|
* @param options options for the string representation
|
||||||
|
* @returns string representation of the time range for the course
|
||||||
|
*/
|
||||||
|
getTimeString(options: TimeStringOptions): string {
|
||||||
|
const { startTime, endTime } = this;
|
||||||
|
const startHour = Math.floor(startTime / 60);
|
||||||
|
const startMinute = startTime % 60;
|
||||||
|
const endHour = Math.floor(endTime / 60);
|
||||||
|
const endMinute = endTime % 60;
|
||||||
|
|
||||||
|
let startTimeString = '';
|
||||||
|
let endTimeString = '';
|
||||||
|
|
||||||
|
if (startHour === 0) {
|
||||||
|
startTimeString = '12';
|
||||||
|
} else if (startHour > 12) {
|
||||||
|
startTimeString = `${startHour - 12}`;
|
||||||
|
} else {
|
||||||
|
startTimeString = `${startHour}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTimeString += startMinute === 0 ? ':00' : `:${startMinute}`;
|
||||||
|
startTimeString += startHour >= 12 ? 'pm' : 'am';
|
||||||
|
|
||||||
|
if (endHour === 0) {
|
||||||
|
endTimeString = '12';
|
||||||
|
} else if (endHour > 12) {
|
||||||
|
endTimeString = `${endHour - 12}`;
|
||||||
|
} else {
|
||||||
|
endTimeString = `${endHour}`;
|
||||||
|
}
|
||||||
|
endTimeString += endMinute === 0 ? ':00' : `:${endMinute}`;
|
||||||
|
endTimeString += endHour >= 12 ? 'pm' : 'am';
|
||||||
|
|
||||||
|
if (options.capitalize) {
|
||||||
|
startTimeString = startTimeString.toUpperCase();
|
||||||
|
endTimeString = endTimeString.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${startTimeString} ${options.separator} ${endTimeString}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options to control the format of the time string
|
||||||
|
*/
|
||||||
|
type TimeStringOptions = {
|
||||||
|
/** the separator between the start and end times */
|
||||||
|
separator: string;
|
||||||
|
/** capitalizes the AM/PM */
|
||||||
|
capitalize?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options to control the format of the days string
|
||||||
|
*/
|
||||||
|
type DaysStringOptions = {
|
||||||
|
/** The format of the days string, short = MWF, long = Monday, Wednesday, Friday */
|
||||||
|
format: 'short' | 'long';
|
||||||
|
separator: Intl.ListFormatStyle | 'none';
|
||||||
|
};
|
||||||
@@ -1,61 +1,27 @@
|
|||||||
import { Serialized } from 'chrome-extension-toolkit';
|
import { Serialized } from 'chrome-extension-toolkit';
|
||||||
|
import { CourseMeeting, Day, DAY_MAP } from './CourseMeeting';
|
||||||
/**
|
|
||||||
* a map of the days of the week that a class is taught, and the corresponding abbreviation
|
|
||||||
*/
|
|
||||||
const DAY_MAP = {
|
|
||||||
M: 'Monday',
|
|
||||||
T: 'Tuesday',
|
|
||||||
W: 'Wednesday',
|
|
||||||
TH: 'Thursday',
|
|
||||||
F: 'Friday',
|
|
||||||
S: 'Saturday',
|
|
||||||
SU: 'Sunday',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
/** A day of the week that a class is taught */
|
|
||||||
export type Day = typeof DAY_MAP[keyof typeof DAY_MAP];
|
|
||||||
|
|
||||||
/** A physical room that a class is taught in */
|
|
||||||
export type Room = {
|
|
||||||
/** The UT building code for where the class is taught */
|
|
||||||
building: string;
|
|
||||||
/** The room number for where the class is taught */
|
|
||||||
number: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This represents one "Meeting Time" for a course, which includes the day of the week that the course is taught, the time that the course is taught, and the location that the course is taught
|
|
||||||
*/
|
|
||||||
export type CourseMeeting = {
|
|
||||||
/** The day of the week that the course is taught */
|
|
||||||
day: Day;
|
|
||||||
/** The start time of the course, in minutes since midnight */
|
|
||||||
startTime: number;
|
|
||||||
/** The end time of the course, in minutes since midnight */
|
|
||||||
endTime: number;
|
|
||||||
/** The location that the course is taught */
|
|
||||||
room?: Room;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This represents the schedule for a course, which includes all the meeting times for the course, as well as helper functions for parsing, serializing, and deserializing the schedule
|
* This represents the schedule for a course, which includes all the meeting times for the course, as well as helper functions for parsing, serializing, and deserializing the schedule
|
||||||
*/
|
*/
|
||||||
export class CourseSchedule {
|
export class CourseSchedule {
|
||||||
meetings: CourseMeeting[];
|
meetings: CourseMeeting[] = [];
|
||||||
|
|
||||||
constructor(courseSchedule: CourseSchedule | Serialized<CourseSchedule>) {
|
constructor(courseSchedule?: Serialized<CourseSchedule>) {
|
||||||
|
if (!courseSchedule) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Object.assign(this, courseSchedule);
|
Object.assign(this, courseSchedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a string representation of a schedule, parse it into a CourseSchedule object
|
* Given a string representation of the meeting information for a class, parse it into a CourseMeeting object
|
||||||
* @param dayLine a string representation of the days of the week that the course is taught: MWF, TR, etc.
|
* @param dayLine a string representation of the days of the week that the course is taught: MWF, TR, etc.
|
||||||
* @param timeLine a string representation of a time-range that the course is taught: 10:00 am - 11:00 am, 1:00 pm - 2:00 pm, etc.
|
* @param timeLine a string representation of a time-range that the course is taught: 10:00 am - 11:00 am, 1:00 pm - 2:00 pm, etc.
|
||||||
* @param roomLine a string representation of the room that the course is taught in: JGB 2.302, etc.
|
* @param roomLine a string representation of the room that the course is taught in: JGB 2.302, etc.
|
||||||
* @returns an array of CourseMeeting objects, which represent the schedule for the course
|
* @returns CourseMeeting object representing the meeting information
|
||||||
*/
|
*/
|
||||||
static parse(dayLine: string, timeLine: string, roomLine: string): CourseMeeting[] {
|
static parse(dayLine: string, timeLine: string, roomLine: string): CourseMeeting {
|
||||||
try {
|
try {
|
||||||
let days: Day[] = dayLine
|
let days: Day[] = dayLine
|
||||||
.split('')
|
.split('')
|
||||||
@@ -87,15 +53,15 @@ export class CourseSchedule {
|
|||||||
|
|
||||||
const [building, number] = roomLine.split(' ');
|
const [building, number] = roomLine.split(' ');
|
||||||
|
|
||||||
return days.map(day => ({
|
return new CourseMeeting({
|
||||||
day,
|
days,
|
||||||
startTime,
|
startTime,
|
||||||
endTime,
|
endTime,
|
||||||
room: {
|
room: {
|
||||||
building,
|
building,
|
||||||
number,
|
number,
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Failed to parse schedule: ${dayLine} ${timeLine} ${roomLine}`);
|
throw new Error(`Failed to parse schedule: ${dayLine} ${timeLine} ${roomLine}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export default function Text(props: PropsWithChildren<TextProps>) {
|
|||||||
style.color ??= colors?.[props.color ?? 'charcoal'];
|
style.color ??= colors?.[props.color ?? 'charcoal'];
|
||||||
style.fontSize ??= fonts?.[`${props.size ?? 'medium'}_size`];
|
style.fontSize ??= fonts?.[`${props.size ?? 'medium'}_size`];
|
||||||
style.fontWeight ??= fonts?.[`${props.weight ?? 'regular'}_weight`];
|
style.fontWeight ??= fonts?.[`${props.weight ?? 'regular'}_weight`];
|
||||||
|
style.lineHeight ??= fonts?.[`${props.size ?? 'medium'}_line_height`];
|
||||||
|
|
||||||
if (props.span) {
|
if (props.span) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ interface Props {
|
|||||||
* The popup that appears when the user clicks on a course for more details.
|
* The popup that appears when the user clicks on a course for more details.
|
||||||
*/
|
*/
|
||||||
export default function CoursePopup({ course, onClose }: Props) {
|
export default function CoursePopup({ course, onClose }: Props) {
|
||||||
|
console.log(course);
|
||||||
return (
|
return (
|
||||||
<Popup className={styles.popup} overlay>
|
<Popup className={styles.popup} overlay>
|
||||||
<Icon className={styles.close} size='large' name='close' onClick={onClose} />
|
<Icon className={styles.close} size='large' name='close' onClick={onClose} />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Course, Instructor, Status, InstructionMode, ScrapedRow } from 'src/shared/types/Course';
|
import { Course, Instructor, Status, InstructionMode, ScrapedRow } from 'src/shared/types/Course';
|
||||||
import { CourseSchedule, CourseMeeting } from 'src/shared/types/CourseSchedule';
|
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
|
||||||
import { SiteSupport } from 'src/views/lib/getSiteSupport';
|
import { SiteSupport } from 'src/views/lib/getSiteSupport';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -289,17 +289,18 @@ export class CourseCatalogScraper {
|
|||||||
throw new Error('Schedule data is malformed');
|
throw new Error('Schedule data is malformed');
|
||||||
}
|
}
|
||||||
|
|
||||||
const meetings: CourseMeeting[] = [];
|
const schedule = new CourseSchedule();
|
||||||
|
|
||||||
for (let i = 0; i < dayLines.length; i += 1) {
|
for (let i = 0; i < dayLines.length; i += 1) {
|
||||||
const lineMeetings = CourseSchedule.parse(
|
schedule.meetings.push(
|
||||||
|
CourseSchedule.parse(
|
||||||
dayLines[i].textContent || '',
|
dayLines[i].textContent || '',
|
||||||
hourLines[i].textContent || '',
|
hourLines[i].textContent || '',
|
||||||
roomLines[i].textContent || ''
|
roomLines[i].textContent || ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
meetings.push(...lineMeetings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CourseSchedule({ meetings });
|
return schedule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ $large_size: 24px;
|
|||||||
$x_large_size: 32px;
|
$x_large_size: 32px;
|
||||||
$xx_large_size: 48px;
|
$xx_large_size: 48px;
|
||||||
|
|
||||||
|
$x_small_line_height: 12px;
|
||||||
|
$small_line_height: 16px;
|
||||||
|
$medium_line_height: 20px;
|
||||||
|
$large_line_height: 28px;
|
||||||
|
$x_large_line_height: 36px;
|
||||||
|
$xx_large_line_height: 52px;
|
||||||
|
|
||||||
:export {
|
:export {
|
||||||
light_weight: $light_weight;
|
light_weight: $light_weight;
|
||||||
regular_weight: $regular_weight;
|
regular_weight: $regular_weight;
|
||||||
@@ -44,4 +51,12 @@ $xx_large_size: 48px;
|
|||||||
large_size: $large_size;
|
large_size: $large_size;
|
||||||
x_large_size: $x_large_size;
|
x_large_size: $x_large_size;
|
||||||
xx_large_size: $xx_large_size;
|
xx_large_size: $xx_large_size;
|
||||||
|
|
||||||
|
x_small_line_height: $x_small_line_height;
|
||||||
|
small_line_height: $small_line_height;
|
||||||
|
medium_line_height: $medium_line_height;
|
||||||
|
large_line_height: $large_line_height;
|
||||||
|
x_large_line_height: $x_large_line_height;
|
||||||
|
xx_large_line_height: $xx_large_line_height;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/views/styles/fonts.module.scss.d.ts
vendored
14
src/views/styles/fonts.module.scss.d.ts
vendored
@@ -22,6 +22,18 @@ export interface ISizes {
|
|||||||
xx_large_size: number;
|
xx_large_size: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the type for all the line height scss variables exported from fonts.module.scss
|
||||||
|
*/
|
||||||
|
export interface LineHeight {
|
||||||
|
x_small_line_height: number;
|
||||||
|
small_line_height: number;
|
||||||
|
medium_line_height: number;
|
||||||
|
large_line_height: number;
|
||||||
|
x_large_line_height: number;
|
||||||
|
xx_large_line_height: number;
|
||||||
|
}
|
||||||
|
|
||||||
/** A utility type that removes the _weight postfix from the variable names for weights */
|
/** A utility type that removes the _weight postfix from the variable names for weights */
|
||||||
export type Weight = keyof IWeights extends `${infer U}_weight` ? U : never;
|
export type Weight = keyof IWeights extends `${infer U}_weight` ? U : never;
|
||||||
|
|
||||||
@@ -32,7 +44,7 @@ export type Size = keyof ISizes extends `${infer U}_size` ? U : never;
|
|||||||
* This is a file that we need to create to tell typescript what the shape of the css modules is
|
* This is a file that we need to create to tell typescript what the shape of the css modules is
|
||||||
* when we import them into ts/tsx files
|
* when we import them into ts/tsx files
|
||||||
*/
|
*/
|
||||||
export type IFonts = IWeights & ISizes;
|
export type IFonts = IWeights & ISizes & LineHeight;
|
||||||
|
|
||||||
declare const fonts: IFonts;
|
declare const fonts: IFonts;
|
||||||
export default fonts;
|
export default fonts;
|
||||||
|
|||||||
2
todo.md
2
todo.md
@@ -42,7 +42,9 @@ Last Updated: 03/4/2023
|
|||||||
- [ ] see who else is looking at certain classes (waitlist, or has it in their schedule)
|
- [ ] see who else is looking at certain classes (waitlist, or has it in their schedule)
|
||||||
- [ ] github contributors displayed somewhere
|
- [ ] github contributors displayed somewhere
|
||||||
- [ ] Links to discord/github
|
- [ ] Links to discord/github
|
||||||
|
- [ ] my twitter handle for support
|
||||||
- [ ] on CS/ECE/MIS pages, show some banner somewhere for students to join the dev team!
|
- [ ] on CS/ECE/MIS pages, show some banner somewhere for students to join the dev team!
|
||||||
|
- [ ] suggest fun classes to take? or classes that are easy A's? or classes that have not a lot of people / lot of people in them?
|
||||||
- [ ] CHECK ALL THE TODOs in CODE BEFORE LAUNCHING
|
- [ ] CHECK ALL THE TODOs in CODE BEFORE LAUNCHING
|
||||||
|
|
||||||
## LEGACY FROM UTRP-V1
|
## LEGACY FROM UTRP-V1
|
||||||
|
|||||||
Reference in New Issue
Block a user