diff --git a/src/SelectCourses.tsx b/src/SelectCourses.tsx
new file mode 100644
index 0000000..b81ba38
--- /dev/null
+++ b/src/SelectCourses.tsx
@@ -0,0 +1,173 @@
+import Course from "./Course";
+import CourseGroup from "./CourseGroup";
+import SelectSubjectRequirement from "./SelectSubjectRequirement";
+import { checkSelect, countUnitFromCode } from "./checkSelect";
+import { GradRequirement } from "./data/gradRequirement";
+
+const Select = ({
+ courseList,
+ includeCourseYear,
+ gradRequirement,
+}: {
+ courseList: Course[];
+ includeCourseYear: boolean;
+ gradRequirement: GradRequirement;
+}) => {
+ const { selectList, sumUnit, courseGroupList, courseIDList, groupUnitList } =
+ checkSelect(courseList, gradRequirement);
+
+ return (
+
+
選択科目の条件一覧
+
+
+
+
+ );
+};
+
+const SelectGroups = ({
+ selectList,
+ courseIDList,
+ courseList,
+ includeCourseYear,
+}: {
+ selectList: SelectSubjectRequirement[];
+ courseIDList: string[];
+ courseList: Course[];
+ includeCourseYear: boolean;
+}) => (
+ <>
+ {selectList.map((selectSubject) => (
+
+ ))}
+ >
+);
+
+const SelectGroup = ({
+ selectSubject,
+ courseIDList,
+ courseList,
+ includeCourseYear,
+}: {
+ selectSubject: SelectSubjectRequirement;
+ courseIDList: string[];
+ courseList: Course[];
+ includeCourseYear: boolean;
+}) => {
+ const { excludeCourseList, unitCount } = countUnitFromCode(
+ selectSubject,
+ courseIDList,
+ courseList
+ );
+
+ return (
+
+
+ {selectSubject.message}{" "}
+ {unitCount >= selectSubject.minimum ? 〇 : ✖}{" "}
+ {unitCount}({selectSubject.minimum}-{selectSubject.maximum}){" "}
+ {unitCount > selectSubject.maximum && (上限を超えています)}
+
+
+
+ );
+};
+
+const DetailContent = ({
+ courses,
+ includeCourseYear,
+}: {
+ courses: Course[];
+ includeCourseYear: boolean;
+}) => (
+ <>
+ {courses.map((course) => {
+ const year = includeCourseYear ? `(${course.year}年度)` : "";
+ return (
+
+ {course.id} {course.name} {year}: {course.grade}
+
+ );
+ })}
+ >
+);
+
+const CourseGroups = ({
+ courseGroupList,
+ groupUnitList,
+}: {
+ courseGroupList: CourseGroup[];
+ groupUnitList: { [key: number]: number };
+}) => (
+ <>
+ {courseGroupList.map((courseGroup, index) => (
+
+ ))}
+ >
+);
+
+const CourseGroupFC = ({
+ courseGroup,
+ groupUnitList,
+ index,
+}: {
+ courseGroup: CourseGroup;
+ groupUnitList: { [key: number]: number };
+ index: number;
+}) => {
+ const courseGroupUnit = groupUnitList[courseGroup.id];
+ const marubatsu =
+ courseGroupUnit >= courseGroup.minUnit ? 〇 : ✖;
+ const exceedMessage =
+ courseGroupUnit > courseGroup.maxUnit ? (
+ (単位上限を超えています)
+ ) : (
+ ""
+ );
+ return (
+ <>
+ {courseGroup.name}
+ {groupUnitList[index]}/({courseGroup.minUnit}~{courseGroup.maxUnit})
+ {marubatsu}
+ {exceedMessage}
+ >
+ );
+};
+
+const SelectCourseTotal = ({
+ sumUnit,
+ selectMinimumUnit,
+}: {
+ sumUnit: number;
+ selectMinimumUnit: number;
+}) => (
+
+ 合計{sumUnit}/{selectMinimumUnit}単位
+
+);
+
+export { Select };
diff --git a/src/SelectSubjectRequirement.ts b/src/SelectSubjectRequirement.ts
index f8bafbc..a39890d 100644
--- a/src/SelectSubjectRequirement.ts
+++ b/src/SelectSubjectRequirement.ts
@@ -5,7 +5,7 @@ class SelectSubjectRequirement {
public maximum: number,
public isExcludeRequirement: boolean,
public message: string,
- public group: number
+ public group: 0 | 1 | 2 | 3
) {}
}
diff --git a/src/checkCompulsory.ts b/src/checkCompulsory.ts
index 2f5fe31..7b8b925 100644
--- a/src/checkCompulsory.ts
+++ b/src/checkCompulsory.ts
@@ -93,8 +93,8 @@ const checkCourseCertificate = (courseList: Course[]): Course[] => {
"English Presentation Skills II": "31L",
};
- courseList.map((c) => {
- if (c.grade == "認" && compulsoryEnglishDict[c.name] != undefined) {
+ courseList.forEach((c) => {
+ if (c.grade === "認" && compulsoryEnglishDict[c.name] !== undefined) {
c.id = compulsoryEnglishDict[c.name];
}
});
@@ -133,7 +133,7 @@ const checkCompulsory = (
let courseExists: boolean;
let alternativeExists: boolean;
- compulsoryList.map((compulsory) => {
+ compulsoryList.forEach((compulsory) => {
//初期化
detectedCourses = [];
courseName = compulsory;
@@ -160,7 +160,7 @@ const checkCompulsory = (
(courseID) =>
beginWithMatch(courseID, codes) && !beginWithMatch(courseID, except)
)
- .map((courseID) => {
+ .forEach((courseID) => {
const unit = getCourseUnitFromID(courseID, courseList);
detectedCourses.push(searchCourseFromID(courseID, courseList));
excludeCourseList.push(searchCourseFromID(courseID, courseList));
diff --git a/src/checkSelect.ts b/src/checkSelect.ts
index 9402f78..6fd96b5 100644
--- a/src/checkSelect.ts
+++ b/src/checkSelect.ts
@@ -1,6 +1,7 @@
import Course from "./Course";
import CourseGroup from "./CourseGroup";
import codeType from "./data/courseCodeTypes";
+import { GradRequirement } from "./data/gradRequirement";
class SelectSubjectRequirement {
constructor(
@@ -9,7 +10,7 @@ class SelectSubjectRequirement {
public maximum: number,
public isExcludeRequirement: boolean,
public message: string,
- public group: number
+ public group: 0 | 1 | 2 | 3
) {}
}
@@ -46,7 +47,7 @@ const countUnitFromCode = (
selectSubject: SelectSubjectRequirement,
courseIDList: string[],
courseList: Course[]
-): [Course[], number] => {
+): { excludeCourseList: Course[]; unitCount: number } => {
const isExclusive = selectSubject.isExcludeRequirement;
const codes = selectSubject.codes;
let unitCount = 0;
@@ -57,7 +58,7 @@ const countUnitFromCode = (
// 通常の条件のカウントの場合
if (!isExclusive) {
- codes.map((code) => {
+ codes.forEach((code) => {
// 科目のタグ表記ではない場合
if (!code.startsWith("*")) {
includedIDList = courseIDList.filter((id) => id.startsWith(code));
@@ -71,7 +72,7 @@ const countUnitFromCode = (
let tag = code.replace("*", "");
tagCodes = codeType[tag as keyof typeof codeType].codes;
tagExcept = codeType[tag as keyof typeof codeType].except;
- tagCodes.map((tagCode) => {
+ tagCodes.forEach((tagCode) => {
includedIDList = courseIDList.filter(
(id) => id.startsWith(tagCode) && !beginWithMatch(id, tagExcept)
);
@@ -86,12 +87,12 @@ const countUnitFromCode = (
//除外するカウントの場合
let expandedExcludeList: string[] = [];
let expandedExceptList: string[] = [];
- codes.map((code) => {
+ codes.forEach((code) => {
if (!code.startsWith("*")) {
expandedExcludeList.push(code);
} else {
//タグを展開してリストに追加
- let tag = code.replace("*", "");
+ const tag = code.replace("*", "");
tagCodes = codeType[tag as keyof typeof codeType].codes;
tagExcept = codeType[tag as keyof typeof codeType].except;
expandedExcludeList = expandedExcludeList.concat(tagCodes);
@@ -108,126 +109,71 @@ const countUnitFromCode = (
);
unitCount += getUnitFromIDList(includedIDList, courseList);
}
- return [excludeCourseList, unitCount];
+ return { excludeCourseList, unitCount };
};
-const createDetail = (
- detectedCourses: Course[],
- includeCourseYear: boolean
-): string[] =>
- detectedCourses.map((course) => {
- const year = includeCourseYear ? `(${course.year}年度)` : "";
- console.log(`${course.id} ${course.name} ${year}: ${course.grade}
`);
- return `${course.id} ${course.name} ${year}: ${course.grade}
`;
- });
-
const checkSelect = (
courseList: Course[],
- includeCourseYear: boolean,
- requirementObject: any
-): { newCourseList: Course[]; sumUnit: number } => {
- const selectList: SelectSubjectRequirement[] =
- requirementObject.courses.select.map(
- (x: any) =>
- new SelectSubjectRequirement(x[0], x[1], x[2], x[3], x[4], x[5])
- );
- const courseIDList: string[] = createElementList("id", courseList);
- const courseGroupList: CourseGroup[] = requirementObject.courses.groups.map(
- (x: any) => new CourseGroup(x[0], x[1], x[2], x[3])
+ requirementObject: GradRequirement
+) => {
+ const selectList = requirementObject.courses.select.map(
+ (x) => new SelectSubjectRequirement(x[0], x[1], x[2], x[3], x[4], x[5])
+ );
+ const courseIDList = createElementList("id", courseList);
+ const courseGroupList = requirementObject.courses.groups.map(
+ (x) => new CourseGroup(x[0], x[1], x[2], x[3])
);
- let groupUnitList: { [key: number]: number } = { 0: 0, 1: 0, 2: 0, 3: 0 };
- let groupid: number;
- let excludeCourseList: Course[] = [];
- let detectedCourses: Course[];
- let tmp: [Course[], number];
- let resultArray: string[] = [];
- let unitCount;
- let sumUnit = 0;
- let isCompleted = true;
-
- selectList.map((selectSubject) => {
- groupid = selectSubject.group;
- tmp = countUnitFromCode(selectSubject, courseIDList, courseList);
- unitCount = tmp[1];
- groupUnitList[groupid] += unitCount;
- detectedCourses = tmp[0];
- excludeCourseList = excludeCourseList.concat(detectedCourses);
- if (unitCount >= selectSubject.maximum) {
- resultArray.push(`
-
- ${selectSubject.message} 〇 ${unitCount}(${
- selectSubject.minimum
- }~${selectSubject.maximum}) (上限を超えています)
-
- ${createDetail(detectedCourses, includeCourseYear).map(
- (course) => `${course}`
- )}
- `);
- } else if (unitCount >= selectSubject.minimum) {
- resultArray.push(
- `
-
- ${selectSubject.message} 〇 ${unitCount}(${
- selectSubject.minimum
- }~${selectSubject.maximum})
-
- ${createDetail(detectedCourses, includeCourseYear).join("")}
- `
- );
- } else {
- // 条件を満たしていない場合
- resultArray.push(
- "" +
- selectSubject.message +
- " " +
- "✖" +
- " " +
- unitCount +
- "(" +
- String(selectSubject.minimum) +
- "~" +
- String(selectSubject.maximum) +
- ")" +
- "
" +
- createDetail(detectedCourses, includeCourseYear).join("") +
- " "
- );
- }
- });
- console.log(resultArray);
- resultArray.unshift("選択科目の条件一覧
");
- let courseGroupUnit: number;
- let marubatsu: string;
- let exceedMessage: string;
- courseGroupList.map((courseGroup, index) => {
- exceedMessage = "";
- courseGroupUnit = groupUnitList[courseGroup.id];
- if (courseGroupUnit >= courseGroup.minUnit) {
- marubatsu = "〇";
- if (courseGroupUnit >= courseGroup.maxUnit) {
- exceedMessage = " (単位上限を超えています)";
- }
- } else {
- marubatsu = "✖";
- isCompleted = false;
- }
- resultArray.push(`${courseGroup.name}
- ${groupUnitList[index]}/(${courseGroup.minUnit}~${courseGroup.maxUnit})${marubatsu}${exceedMessage}`);
- sumUnit += Math.min(courseGroupUnit, courseGroup.maxUnit);
- });
- sumUnit = Math.min(sumUnit, requirementObject.courses.selectMinimumUnit);
+ const { groupUnitList, excludeCourseList } = selectList.reduce<{
+ groupUnitList: { [key: number]: number };
+ excludeCourseList: Course[];
+ }>(
+ (acc, selectSubject) => {
+ const { excludeCourseList: _excludeCourseList, unitCount: _unitCount } =
+ countUnitFromCode(selectSubject, courseIDList, courseList);
+ const groupId = selectSubject.group;
+ acc.groupUnitList[groupId] += _unitCount;
+ acc.excludeCourseList = acc.excludeCourseList.concat(_excludeCourseList);
+ return acc;
+ },
+ { groupUnitList: { 0: 0, 1: 0, 2: 0, 3: 0 }, excludeCourseList: [] }
+ );
- resultArray.push(`
- 合計${sumUnit}/${requirementObject.courses.selectMinimumUnit}単位
-
`);
- document.getElementById("select")!.innerHTML = resultArray.join("
");
+ const sumUnit = calcSumUnit({
+ courseGroupList,
+ groupUnitList,
+ selectMinimumUnit: requirementObject.courses.selectMinimumUnit,
+ });
// 差集合をとる
const newCourseList = courseList.filter(
(val) => !excludeCourseList.includes(val)
);
- return { newCourseList: newCourseList, sumUnit: sumUnit };
+
+ return {
+ newCourseList,
+ sumUnit,
+ selectList,
+ courseGroupList,
+ courseIDList,
+ groupUnitList,
+ };
+};
+
+const calcSumUnit = ({
+ courseGroupList,
+ groupUnitList,
+ selectMinimumUnit,
+}: {
+ courseGroupList: CourseGroup[];
+ groupUnitList: { [key: number]: number };
+ selectMinimumUnit: number;
+}) => {
+ const _sumUnit = courseGroupList.reduce((acc, courseGroup) => {
+ const courseGroupUnit = groupUnitList[courseGroup.id];
+ return acc + Math.min(courseGroupUnit, courseGroup.maxUnit);
+ }, 0);
+ return Math.min(_sumUnit, selectMinimumUnit);
};
-export default checkSelect;
+export { checkSelect, countUnitFromCode };
diff --git a/src/data/gradRequirement.ts b/src/data/gradRequirement.ts
index 140bfdf..74ea6f7 100644
--- a/src/data/gradRequirement.ts
+++ b/src/data/gradRequirement.ts
@@ -16,7 +16,7 @@ const gradRequirement = z.object({
z.number(),
z.boolean(),
z.string(),
- z.number(),
+ z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]),
])
),
selectMinimumUnit: z.number(),
diff --git a/src/graduationChecker.tsx b/src/graduationChecker.tsx
index 4f12926..b8892bd 100644
--- a/src/graduationChecker.tsx
+++ b/src/graduationChecker.tsx
@@ -2,7 +2,7 @@ import React from "react";
import Course from "./Course";
import { gradRequirement, GradRequirement } from "./data/gradRequirement";
import checkCompulsory from "./checkCompulsory";
-import checkSelect from "./checkSelect";
+import { checkSelect } from "./checkSelect";
import showRequirements from "./showRequirements";
import "./graduationChecker.css";
import { GradePieChart } from "./GradePieChart";
@@ -15,6 +15,7 @@ import klis_ksc22 from "./data/klis_ksc22.json";
import klis_kis22 from "./data/klis_kis22.json";
import klis_irm22 from "./data/klis_irm22.json";
import { TotalGPA } from "./totalGPA";
+import { Select } from "./SelectCourses";
const getRequirement = (major: Major): GradRequirement =>
gradRequirement.parse(
@@ -35,16 +36,21 @@ const GraduationChecker: React.FC = () => {
const [exceptCourses, setExceptCourses] = React.useState(
null
);
+ const [includeCourseYear, setIncludeCourseYear] =
+ React.useState(false);
const [usageVisible, setUsageVisible] = React.useState(true);
const [sumUnit, setSumUnit] = React.useState(0);
const [minimumGraduationUnit, setMinimumGraduationUnit] =
React.useState(124);
const [isCompulsoryCompleted, setIsCompulsoryCompleted] =
React.useState(false);
+ const [gradRequirement, setGradRequirement] = React.useState(
+ getRequirement("mast21")
+ );
- const includeCourseYear = React.useRef(null);
const majorSelect = React.useRef(null);
const enrollYear = React.useRef(null);
+
const loadCSV = (csv: string): Course[] => {
document.getElementById("result")!.style.display = "block";
csv = csv.replaceAll('"', "");
@@ -76,16 +82,13 @@ const GraduationChecker: React.FC = () => {
// 使い方の表示を消す
setUsageVisible(false);
//チェックボックスの判定
- const checkBox = includeCourseYear.current;
const tmpMajor = majorSelect.current!.value + enrollYear.current!.value;
const major = (tmpMajor as Major) || "mast21";
- const requirementObject = getRequirement(major);
+ setGradRequirement(getRequirement(major));
- const isChecked = (checkBox && checkBox.checked) || false;
const reader = new FileReader();
const minimumGraduationUnit = 124;
- const compulsoryRequirementUnit =
- requirementObject.courses.compulsorySumUnit;
+ const compulsoryRequirementUnit = gradRequirement.courses.compulsorySumUnit;
let sumUnit = 0;
let isCompulsoryCompleted = false;
reader.readAsText(csv);
@@ -95,12 +98,12 @@ const GraduationChecker: React.FC = () => {
const {
newCourseList: compulsoryCourseList,
sumUnit: compulsorySumUnit,
- } = checkCompulsory(courseList, isChecked, requirementObject);
+ } = checkCompulsory(courseList, includeCourseYear, gradRequirement);
isCompulsoryCompleted = compulsorySumUnit === compulsoryRequirementUnit;
const { newCourseList: selectCourseList, sumUnit: selectSumUnit } =
- checkSelect(compulsoryCourseList, isChecked, requirementObject);
+ checkSelect(compulsoryCourseList, gradRequirement);
sumUnit = selectSumUnit + compulsorySumUnit;
setSumUnit(sumUnit);
setMinimumGraduationUnit(minimumGraduationUnit);
@@ -146,7 +149,7 @@ const GraduationChecker: React.FC = () => {
id="includeCourseYear"
type="checkbox"
name="includeCourseYear"
- ref={includeCourseYear}
+ onChange={(e) => setIncludeCourseYear(e.target.checked)}
/>
@@ -182,7 +185,13 @@ const GraduationChecker: React.FC = () => {
-
+ {courseList && (
+
+ )}
卒業要件外の科目
{exceptCourses ? (
@@ -286,9 +295,9 @@ const Sum = ({
{sumUnit >= minimumGraduationUnit && isCompulsoryCompleted ? (
- ◯
+ ◯
) : (
- ✖
+ ✖
)}
{sumUnit >= minimumGraduationUnit && !isCompulsoryCompleted && (