Skip to content

Commit

Permalink
Record and compute sub-requirement progress in a fine-grained way
Browse files Browse the repository at this point in the history
  • Loading branch information
SamChou19815 committed Mar 6, 2021
1 parent 0429190 commit 1896f55
Show file tree
Hide file tree
Showing 43 changed files with 1,450 additions and 1,215 deletions.
33 changes: 11 additions & 22 deletions src/requirement-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,21 @@ type RequirementFulfillmentInformation<T = Record<string, unknown>> =
readonly minCount?: number;
}
| ({
/** Defines how courses in a sub-requirement can be all counted towards a stat. */
readonly subRequirementProgress: 'every-course-needed' | 'any-can-count';
readonly fulfilledBy: 'courses';
/** The minimum number of courses required to fulfill this requirement. */
readonly minCount: number;
} & T)
| ({
readonly fulfilledBy: 'credits';
/** The minimum number of credits required to fulfill this requirement. */
readonly minCount: number;
readonly fulfilledBy: 'courses' | 'credits';
/** The minimum number of courses/credits required to fulfill each sub-requirement. */
readonly perSlotMinCount: readonly number[];
/** When we care more about how many slots are filled with some courses */
readonly minNumberOfSlots?: number;
} & T)
| {
readonly fulfilledBy: 'toggleable';
readonly fulfillmentOptions: {
readonly [optionName: string]:
| ({
readonly minCount: number;
readonly counting: 'courses';
readonly subRequirementProgress: 'every-course-needed' | 'any-can-count';
readonly description: string;
} & T)
| ({
readonly minCount: number;
readonly counting: 'credits';
readonly description: string;
} & T);
readonly [optionName: string]: {
readonly counting: 'courses' | 'credits';
readonly perSlotMinCount: readonly number[];
readonly minNumberOfSlots?: number;
readonly description: string;
} & T;
};
};

Expand Down
22 changes: 5 additions & 17 deletions src/requirements/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,24 +98,12 @@ To reiterate, this phase computes a list of satisfying courses for each requirem
4. Now we created [`decorated-requirements.json`](./decorated-requirements.json).
Pre-computation is done.

Each checker is either:
Each checker is a list of functions `(course: Course) => boolean`, where each function checks
whether the course satisfies one of the sub-requirements that this checker correspond to.

- a TypeScript function `(course: Course) => boolean` that directly tells whether the course
satisfies the requirement that this checker correspond to.
- or a list of such functions, where each function checks whether the course satisfies one of the
sub-requirements that this checker correspond to.

This is how you should use your checkers:

- Case 1: the requirement contains no sub-requirement. Use option 1. Example: CS major practicum.
- Case 2: the requirement contains sub-requirements. Use option 2. Example: CS Core requirement.

Also consider the cases of computing the progress with `subRequirementProgress`:

- Operator type 'every-course-needed' indicates that all instances of a sub-requirement is necessary.
Example: CS Introductory Programming
- Operator type 'any-can-count' indicates that only one instance of a sub-requirement can be used.
Example: ENGL Four 4000 Levels
For each checker, there is an associated minimal number of course/credit required for that
sub-requirement stored in `perSlotMinCount`. There can also be a `minNumberOfSlots` field, which
specifies that we don't need to fulfill every slot, but only `minNumberOfSlots` of sub-requirements.

### Frontend Computation Phase

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ Array [
"College-AS1-Foreign Language",
"College-AS1-Geographic Breadth Requirement (GB)",
"College-AS1-Historic Breadth Requirement (HB)",
"College-AS1-Mathematics & Quantitative Reasoning (MQR-AS)",
"College-AS1-PBS-AS or MQR-AS",
"College-AS1-Physical & Biological Sceiences (PBS-AS/PBSS-AS)",
"College-AS1-PBS and MQR courses",
"College-EN-Advisor-Approved Electives",
"College-EN-Chemistry",
"College-EN-Computing",
Expand Down Expand Up @@ -94,11 +92,9 @@ Array [
"Major-COMM-Upper-Level COMM",
"Major-CRP-Core Classes",
"Major-CRP-Distribution Requirements: 5 Courses",
"Major-CRP-Distribution Requirements: MQR",
"Major-CRP-Distribution Requirements: MQR OR PBS",
"Major-CRP-Distribution Requirements: PBS",
"Major-CRP-Five CRP Classes",
"Major-CRP-Microeconomics",
"Major-CRP-PBS and MQR courses",
"Major-CRP-Statistics",
"Major-CS-Computer Science Core",
"Major-CS-CS Electives",
Expand Down
13 changes: 8 additions & 5 deletions src/requirements/data/checkers-common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Course } from '../types';
import { Course, RequirementChecker } from '../types';
import { FWS_COURSE_ID } from './constants';

/**
Expand Down Expand Up @@ -66,11 +66,14 @@ export const courseIsFWS = (course: Course): boolean =>
* ```
*
* @param includes a list of course code pattern to check against.
* @returns a checker that can be directly assigned to requirement object.
* @returns a list with a single checker that checks whether this single sub-requirement can be
* fulfilled by courses.
*/
export const includesWithSingleRequirement = (...includes: readonly string[]) => (
course: Course
): boolean => courseMatchesCodeOptions(course, includes);
export const includesWithSingleRequirement = (
...includes: readonly string[]
): readonly RequirementChecker[] => [
(course: Course): boolean => courseMatchesCodeOptions(course, includes),
];

/**
* This function returns an array of checkers.
Expand Down
59 changes: 32 additions & 27 deletions src/requirements/data/colleges/ag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ const calsCreditsRequirement: CollegeOrMajorRequirement = {
'and The Department of Statistics and Data Science.',
source:
'https://cals.cornell.edu/undergraduate-students/student-services/degree-requirements/graduation-requirements',
checker: (course: Course): boolean =>
['AG'].includes(course.acadGroup) ||
['AEM', 'BIOEE', 'BIOMG', 'BIOMI', 'BIONB', 'BSOC', 'EAS', 'INFO', 'NS', 'STSCI'].includes(
course.subject
),
checker: [
(course: Course): boolean =>
['AG'].includes(course.acadGroup) ||
['AEM', 'BIOEE', 'BIOMG', 'BIOMI', 'BIONB', 'BSOC', 'EAS', 'INFO', 'NS', 'STSCI'].includes(
course.subject
),
],
fulfilledBy: 'credits',
minCount: 55,
perSlotMinCount: [55],
};

const calsIntroductoryLifeSciencesOrBiologyRequirement: CollegeOrMajorRequirement = {
Expand Down Expand Up @@ -99,7 +101,7 @@ const calsIntroductoryLifeSciencesOrBiologyRequirement: CollegeOrMajorRequiremen
'VIEN 2204'
),
fulfilledBy: 'credits',
minCount: 6,
perSlotMinCount: [6],
};

const calsPhysicalAndLifeSciencesRequirement: CollegeOrMajorRequirement = {
Expand All @@ -118,9 +120,9 @@ const calsChemistryOrPhysicsRequiement: CollegeOrMajorRequirement = {
'Includes all Cornell courses with the CHEM or PHYS prefix at Cornell (excluding courses that are supplemental, independent study, research, TA, internship, and First-Year Writing Seminar).',
source:
'https://cals.cornell.edu/undergraduate-students/student-services/degree-requirements/graduation-requirements/distribution-requirements',
checker: (course: Course): boolean => ['CHEM', 'CHEME', 'PHYS'].includes(course.subject),
checker: [(course: Course): boolean => ['CHEM', 'CHEME', 'PHYS'].includes(course.subject)],
fulfilledBy: 'credits',
minCount: 3,
perSlotMinCount: [3],
};

const calsQuantitativeLiteracyRequirement: CollegeOrMajorRequirement = {
Expand All @@ -131,10 +133,9 @@ const calsQuantitativeLiteracyRequirement: CollegeOrMajorRequirement = {
'or transfer an approved calculus or statistics course with a minimum letter grade of “C” or better; or take an approved calculus or statistics course at Cornell.',
source:
'https://cals.cornell.edu/undergraduate-students/student-services/degree-requirements/graduation-requirements/distribution-requirements',
checker: (course: Course): boolean => ['MATH', 'STSCI'].includes(course.subject),
subRequirementProgress: 'any-can-count',
checker: [(course: Course): boolean => ['MATH', 'STSCI'].includes(course.subject)],
fulfilledBy: 'courses',
minCount: 1,
perSlotMinCount: [1],
};

const calsSocialSciencesAndHumanitiesRequiement: CollegeOrMajorRequirement = {
Expand All @@ -155,9 +156,9 @@ const calsSocialSciencesAndHumanitiesRequiement: CollegeOrMajorRequirement = {
(course: Course): boolean => course.catalogDistr?.includes('LA-') ?? false,
(course: Course): boolean => course.catalogDistr?.includes('SBA-') ?? false,
],
subRequirementProgress: 'every-course-needed',
fulfilledBy: 'courses',
minCount: 4,
perSlotMinCount: [1, 1, 1, 1, 1, 1, 1],
minNumberOfSlots: 4,
};

const calsHumanDiversityRequirement: CollegeOrMajorRequirement = {
Expand All @@ -166,10 +167,9 @@ const calsHumanDiversityRequirement: CollegeOrMajorRequirement = {
'At least one course category MUST be completed in three different categories. Human Diversity (D) is a required category and MUST be completed.',
source:
'https://cals.cornell.edu/undergraduate-students/student-services/degree-requirements/graduation-requirements/distribution-requirements',
checker: (course: Course): boolean => course.catalogDistr?.includes('(D-') ?? false,
subRequirementProgress: 'any-can-count',
checker: [(course: Course): boolean => course.catalogDistr?.includes('(D-') ?? false],
fulfilledBy: 'courses',
minCount: 1,
perSlotMinCount: [1],
};

const calsWrittenAndOralExpressionRequirement: CollegeOrMajorRequirement = {
Expand All @@ -180,25 +180,30 @@ const calsWrittenAndOralExpressionRequirement: CollegeOrMajorRequirement = {
'If not required, all nine credits may be in written expression. Writing in the Majors courses do not count towards the writing requirement.',
source:
'https://cals.cornell.edu/undergraduate-students/student-services/degree-requirements/graduation-requirements/distribution-requirements',
checker: (course: Course): boolean =>
['written expression', 'oral expression', 'First-Year Writing Seminar'].some(
keyword => course.catalogSatisfiesReq?.includes(keyword) ?? false
),
checker: [
(course: Course): boolean =>
['written expression', 'oral expression', 'First-Year Writing Seminar'].some(
keyword => course.catalogSatisfiesReq?.includes(keyword) ?? false
),
],
fulfilledBy: 'credits',
minCount: 9,
perSlotMinCount: [9],
};

const calsWrittenExpressionRequirement: CollegeOrMajorRequirement = {
name: 'Written Expression',
description: 'At least six credits must be in written expression.',
source:
'https://cals.cornell.edu/undergraduate-students/student-services/degree-requirements/graduation-requirements/distribution-requirements',
checker: (course: Course): boolean =>
['written expression', 'First-Year Writing Seminar'].some(
keyword => course.catalogSatisfiesReq?.includes(keyword) ?? false
),
checker: [
(course: Course): boolean =>
['written expression', 'First-Year Writing Seminar'].some(
keyword => course.catalogSatisfiesReq?.includes(keyword) ?? false
),
],
fulfilledBy: 'credits',
minCount: 6,
perSlotMinCount: [6],
allowCourseDoubleCounting: true,
};

const calsRequirements: readonly CollegeOrMajorRequirement[] = [
Expand Down
Loading

0 comments on commit 1896f55

Please sign in to comment.