line height text, refactored course schedule, added string representation functions to course meeting

This commit is contained in:
Sriram Hariharan
2023-03-06 16:51:46 -06:00
parent 007ade81a0
commit 9b76f8afa0
9 changed files with 182 additions and 56 deletions

View File

@@ -72,7 +72,7 @@ export class Course {
/** Which semester is the course from */
semester: Semester;
constructor(course: Course | Serialized<Course>) {
constructor(course: Serialized<Course>) {
Object.assign(this, course);
this.schedule = new CourseSchedule(course.schedule);
}

View 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';
};

View File

@@ -1,61 +1,27 @@
import { Serialized } from 'chrome-extension-toolkit';
/**
* 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;
};
import { CourseMeeting, Day, DAY_MAP } from './CourseMeeting';
/**
* 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 {
meetings: CourseMeeting[];
meetings: CourseMeeting[] = [];
constructor(courseSchedule: CourseSchedule | Serialized<CourseSchedule>) {
constructor(courseSchedule?: Serialized<CourseSchedule>) {
if (!courseSchedule) {
return;
}
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 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.
* @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 {
let days: Day[] = dayLine
.split('')
@@ -87,15 +53,15 @@ export class CourseSchedule {
const [building, number] = roomLine.split(' ');
return days.map(day => ({
day,
return new CourseMeeting({
days,
startTime,
endTime,
room: {
building,
number,
},
}));
});
} catch (e) {
throw new Error(`Failed to parse schedule: ${dayLine} ${timeLine} ${roomLine}`);
}

View File

@@ -25,6 +25,7 @@ export default function Text(props: PropsWithChildren<TextProps>) {
style.color ??= colors?.[props.color ?? 'charcoal'];
style.fontSize ??= fonts?.[`${props.size ?? 'medium'}_size`];
style.fontWeight ??= fonts?.[`${props.weight ?? 'regular'}_weight`];
style.lineHeight ??= fonts?.[`${props.size ?? 'medium'}_line_height`];
if (props.span) {
return (

View File

@@ -16,6 +16,7 @@ interface Props {
* The popup that appears when the user clicks on a course for more details.
*/
export default function CoursePopup({ course, onClose }: Props) {
console.log(course);
return (
<Popup className={styles.popup} overlay>
<Icon className={styles.close} size='large' name='close' onClick={onClose} />

View File

@@ -1,5 +1,5 @@
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';
/**
@@ -289,17 +289,18 @@ export class CourseCatalogScraper {
throw new Error('Schedule data is malformed');
}
const meetings: CourseMeeting[] = [];
const schedule = new CourseSchedule();
for (let i = 0; i < dayLines.length; i += 1) {
const lineMeetings = CourseSchedule.parse(
schedule.meetings.push(
CourseSchedule.parse(
dayLines[i].textContent || '',
hourLines[i].textContent || '',
roomLines[i].textContent || ''
)
);
meetings.push(...lineMeetings);
}
return new CourseSchedule({ meetings });
return schedule;
}
}

View File

@@ -30,6 +30,13 @@ $large_size: 24px;
$x_large_size: 32px;
$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 {
light_weight: $light_weight;
regular_weight: $regular_weight;
@@ -44,4 +51,12 @@ $xx_large_size: 48px;
large_size: $large_size;
x_large_size: $x_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;
}

View File

@@ -22,6 +22,18 @@ export interface ISizes {
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 */
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
* when we import them into ts/tsx files
*/
export type IFonts = IWeights & ISizes;
export type IFonts = IWeights & ISizes & LineHeight;
declare const fonts: IFonts;
export default fonts;

View File

@@ -42,7 +42,9 @@ Last Updated: 03/4/2023
- [ ] see who else is looking at certain classes (waitlist, or has it in their schedule)
- [ ] github contributors displayed somewhere
- [ ] 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!
- [ ] 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
## LEGACY FROM UTRP-V1