Skip to content

Commit 23fb7ae

Browse files
committed
fetch course from backend
1 parent da91ce6 commit 23fb7ae

File tree

18 files changed

+213
-56
lines changed

18 files changed

+213
-56
lines changed

bun.lock

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"dependencies": {
3030
"@elysiajs/cors": "^1.3.3",
3131
"@libsql/client": "^0.15.10",
32-
"@sinclair/typebox": "^0.34.37",
32+
"@packages/class_data": "workspace:*",
33+
"@sinclair/typebox": "^0.34.38",
3334
"better-auth": "^1.3.1",
3435
"drizzle-orm": "^0.44.3",
3536
"elysia": "^1.3.5",

packages/models/atoms.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,32 @@ export const Stream = t.UnionEnum(["s1", "s2", "s3", "l1", "l2", "l3"]);
88
/**
99
* 授業コード
1010
* 例: 30003
11-
* フォーマットが不明なので、 String にしておく。後の調査でフォーマットを探し、 Regex に変更したい。
1211
*/
1312
export type CourseCode = typeof CourseCode.static;
14-
export const CourseCode = t.String();
13+
export const CourseCode = t.RegExp("^\\d{5}$");
1514

1615
/**
1716
* 共通科目コード
1817
* 例:
19-
* - CAS-FC1871L1
2018
* - XAB-CD1001L2
21-
* 参考: https://www.u-tokyo.ac.jp/ja/students/classes/course-numbering.html
19+
* - CAS-FC1871L1
20+
* - CAS-GC1L37S4
21+
* - CASPG1F40L3 // 絶対入力ミスだが、データにあるので対応しなければならない
22+
* 仕様: https://www.u-tokyo.ac.jp/ja/students/classes/course-numbering.html
23+
* 本当は CommonSubjectCode になるはずだが、公式が勝手に CommonCourseCode と読んでいる
2224
*/
23-
export type CommonSubjectCode = typeof CommonSubjectCode.static;
24-
export const CommonSubjectCode = t.RegExp(
25-
"^[A-Z]{3}-[A-Z]{2}[0-9]{3}[A-Z]{1}$",
25+
export type CommonCourseCode = typeof CommonCourseCode.static;
26+
export const CommonCourseCode = t.RegExp(
27+
`
28+
^[CFG] ${/* [1] 課程コード */ ""}
29+
(?:LA|ME|EN|LE|SC|AG|EC|AS|ED|PH|GL) ${/* [2] 開講学部・研究科(教育部)コード */ ""}
30+
-?
31+
[A-Z]{2} ${/* [3] 開講学科・専攻等コード */ ""}
32+
[1-7] ${/* [4] レベルコード */ ""}
33+
[0-9a-zA-Z]{3} ${/* [5] 整理番号 */ ""}
34+
[LSEPTZ] ${/* [6] 授業形態コード */ ""}
35+
[123459] ${/* [7] 使用言語コード */ ""}
36+
$`.replaceAll(/\s/g, ""),
2637
);
2738

2839
/**
@@ -60,17 +71,19 @@ export const Evaluation = t.UnionEnum(["試験", "レポート", "出席", "平
6071
* 単位の種類。
6172
*/
6273
export type ClassType = typeof ClassType.static;
63-
export const ClassType = t.UnionEnum([
74+
export const ClassType = t.UnionEnum(["基礎", "要求", "主題", "総合", "展開"]);
75+
76+
export type ClassSeries = typeof ClassSeries.static;
77+
export const ClassSeries = t.UnionEnum([
6478
"基礎",
65-
"総合",
6679
"要求",
6780
"主題",
6881
"展開",
69-
"L",
70-
"A",
71-
"B",
72-
"C",
73-
"D",
74-
"E",
75-
"F",
82+
"総合L",
83+
"総合A",
84+
"総合B",
85+
"総合C",
86+
"総合D",
87+
"総合E",
88+
"総合F",
7689
]);

packages/models/models.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ export * from "./mappings.ts";
44
import { t } from "elysia";
55

66
import {
7+
ClassSeries,
78
ClassType,
8-
CommonSubjectCode,
9+
CommonCourseCode,
910
CourseCode,
1011
DayPeriod,
1112
Semester,
@@ -28,41 +29,50 @@ export type Course = typeof Course.static;
2829
export const Course = t.Object({
2930
/// 基本情報
3031
code: CourseCode,
31-
ccCode: CommonSubjectCode,
32+
ccCode: CommonCourseCode,
33+
/** 講義名 (例: 大規模計算) */
3234
titleJp: t.String(),
35+
/** 講義名 (例: Large-Scale Computing) */
3336
titleEn: t.String(),
3437
lecturer: t.String(),
3538
lecturerEn: t.String(),
3639

3740
/// 講義の内容に関する情報
3841
/** 授業詳細 */
3942
detail: t.String(),
40-
/** 授業スケジュール */
43+
/** 授業スケジュール (内容的な方) */
4144
schedule: t.String(),
42-
notes: t.String(), // メモ (なんでも良さそう)
45+
notes: t.String(), // 備考
4346

4447
/// 授業の開催に関する情報
4548
semester: Semester,
46-
/** 開催教室 */
49+
/** 開催教室 (例: 駒場5号館 523教室) */
4750
classroom: t.String(),
48-
shortenedClassroom: t.String(), // 短縮表示っぽい
49-
/** 授業の方法? なぜ複数形になった? */
51+
/** 開催教室の短縮表示 (例: 523) */
52+
shortenedClassroom: t.String(),
53+
/**
54+
* 授業の方法
55+
* 例: 講義形式であるが, 担当教員によっては適宜小テストやレポートを課すことがある. 成績評価は主として期末試験によって行なう.
56+
*/
5057
methods: t.String(),
5158
/** 授業の長さ (分) (例: 90) */
5259
time: t.Number(),
60+
dayPeriod: t.Union([t.Array(DayPeriod), t.Literal("集中")]),
5361

5462
/// 講義のカテゴライジングに関する情報
5563
/** 授業の種別 (例: 基礎) */
5664
type: ClassType,
5765
/** 授業のカテゴリー (例: 数理科学) */
5866
category: t.String(),
59-
shortenedCategory: t.String(),
60-
dayPeriod: t.Union([t.Array(DayPeriod), t.Literal("集中")]),
67+
/**
68+
* 科目区分+系列 (例: 基礎、総合A)
69+
* 授業カテゴリの短縮表示**じゃない**!!!!
70+
*/
71+
shortenedCategory: ClassSeries,
6172

6273
/// 単位に関する情報
6374
/** 単位数 (例: 2) */
6475
credits: t.Number(),
65-
/** 評価方法 */
6676
/** 評価方法 (例: 出席状況、提出物などの状況、研究の達成度などをもとに評価します。) */
6777
evaluation: t.String(),
6878
shortenedEvaluation: t.String(),
@@ -79,6 +89,8 @@ export const Course = t.Object({
7989
importance: t.Array(t.Array(t.String())),
8090
});
8191

92+
export type CourseList = typeof CourseList.static;
93+
export const CourseList = t.Array(Course);
8294
/**
8395
* code -> Course
8496
*/

packages/models/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"type": "module",
44
"private": true,
55
"exports": {
6-
".": "./models.ts"
6+
".": "./models.ts",
7+
"./transformer": "./transformer/transformer.ts"
78
},
89
"devDependencies": {
910
"@types/bun": "^1.2.18"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { CourseCollection, CourseList } from "@packages/models";
2+
3+
export function courseListToCourseCollection(courseList: CourseList) {
4+
const collection: CourseCollection = {};
5+
for (const course of courseList) {
6+
collection[course.code] = course;
7+
}
8+
return collection;
9+
}

packages/server/app.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { cors } from "@elysiajs/cors";
22
import { Elysia } from "elysia";
33
import { betterAuth } from "./lib/auth.ts";
4+
import coursesRouter from "./router/courses.ts";
45

56
export const app = new Elysia({
67
prefix: "/api",
78
})
89
.use(betterAuth)
9-
.use(cors());
10+
.use(cors())
11+
.group("/courses", (route) => route.use(coursesRouter));
1012

1113
export type App = typeof app;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Course, type CourseCollection } from "@packages/models";
2+
import { Value } from "@sinclair/typebox/value";
3+
4+
export function transformJSONToCourseCollection(coursesJSON: unknown) {
5+
if (!Array.isArray(coursesJSON)) {
6+
throw new Error("Invalid courses JSON: not array");
7+
}
8+
9+
const collection: CourseCollection = {};
10+
for (const rawCourse of coursesJSON) {
11+
const course = Value.Parse(Course, rawCourse);
12+
collection[course.code] = course;
13+
}
14+
return collection;
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { describe, expect, it } from "bun:test";
2+
import { toHttpDate, toUnixTime } from "./dateconv.ts";
3+
4+
describe("toHttpDate", () => {
5+
it("should format a date as an HTTP date string", () => {
6+
const date = new Date(Date.UTC(2006, 1, 2, 15, 4, 5));
7+
expect(toHttpDate(date)).toBe("Mon, 02 Jan 2006 15:04:05 GMT");
8+
});
9+
});
10+
11+
describe("toUnixTime", () => {
12+
it("should convert a date to a Unix timestamp", () => {
13+
const date = new Date(Date.UTC(1971, 0, 1, 0, 0, 0));
14+
expect(toUnixTime(date)).toBe(31536000);
15+
});
16+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function toUnixTime(date: Date) {
2+
return Math.floor(date.getTime() / 1000);
3+
}
4+
5+
export function toHttpDate(date: Date) {
6+
return date.toUTCString();
7+
}

packages/server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"dependencies": {
2121
"@elysiajs/cors": "^1.3.3",
2222
"@libsql/client": "^0.15.10",
23-
"@sinclair/typebox": "^0.34.37",
23+
"@packages/class_data": "workspace:*",
24+
"@sinclair/typebox": "^0.34.38",
2425
"better-auth": "^1.3.1",
2526
"drizzle-orm": "^0.44.3",
2627
"elysia": "^1.3.5"

0 commit comments

Comments
 (0)