Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record and compute sub-requirement progress in a fine-grained way #344

Merged
merged 1 commit into from
Mar 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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