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

履修情報の記憶と更新、相手の授業も考慮した日程調整 #84

Merged
merged 12 commits into from
Jan 23, 2025
47 changes: 47 additions & 0 deletions prisma/migrations/20250108040947_added_courses_table/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-- CreateTable
CREATE TABLE "Course" (
"code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"credits" INTEGER NOT NULL,
"overview" TEXT NOT NULL,
"remarks" TEXT NOT NULL,
"type" INTEGER NOT NULL,
"recommendedGrade" INTEGER[],

CONSTRAINT "Course_pkey" PRIMARY KEY ("code")
);

-- CreateTable
CREATE TABLE "CourseSchedule" (
"module" INTEGER NOT NULL,
"day" INTEGER NOT NULL,
"period" INTEGER NOT NULL,
"room" TEXT NOT NULL,
"courseCode" TEXT NOT NULL,

CONSTRAINT "CourseSchedule_pkey" PRIMARY KEY ("module","day","period","room")
);

-- CreateTable
CREATE TABLE "_CourseToUser" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL
);

-- CreateIndex
CREATE UNIQUE INDEX "Course_code_key" ON "Course"("code");

-- CreateIndex
CREATE UNIQUE INDEX "_CourseToUser_AB_unique" ON "_CourseToUser"("A", "B");

-- CreateIndex
CREATE INDEX "_CourseToUser_B_index" ON "_CourseToUser"("B");

-- AddForeignKey
ALTER TABLE "CourseSchedule" ADD CONSTRAINT "CourseSchedule_courseCode_fkey" FOREIGN KEY ("courseCode") REFERENCES "Course"("code") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_CourseToUser" ADD CONSTRAINT "_CourseToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "Course"("code") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_CourseToUser" ADD CONSTRAINT "_CourseToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
Warnings:

- The primary key for the `CourseSchedule` table will be changed. If it partially fails, the table could be left without primary key constraint.
- Changed the type of `module` on the `CourseSchedule` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
- Changed the type of `day` on the `CourseSchedule` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.

*/
-- CreateEnum
CREATE TYPE "Module" AS ENUM ('SpringA', 'SpringB', 'SpringC', 'FallA', 'FallB', 'FallC', 'SummerVacation', 'SpringVacation', 'Annual', 'Unknown');

-- CreateEnum
CREATE TYPE "Day" AS ENUM ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Intensive', 'Appointment', 'AnyTime', 'Unknown');

-- AlterTable
ALTER TABLE "CourseSchedule" DROP CONSTRAINT "CourseSchedule_pkey",
DROP COLUMN "module",
ADD COLUMN "module" "Module" NOT NULL,
DROP COLUMN "day",
ADD COLUMN "day" "Day" NOT NULL,
ADD CONSTRAINT "CourseSchedule_pkey" PRIMARY KEY ("module", "day", "period", "room");
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:

- Added the required column `instructor` to the `Course` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "Course" ADD COLUMN "instructor" TEXT NOT NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
Warnings:

- Added the required column `error` to the `Course` table without a default value. This is not possible if the table is not empty.
- Added the required column `lastUpdate` to the `Course` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "Course" ADD COLUMN "error" BOOLEAN NOT NULL,
ADD COLUMN "lastUpdate" TIMESTAMP(3) NOT NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Warnings:

- You are about to drop the column `credits` on the `Course` table. All the data in the column will be lost.
- You are about to drop the column `error` on the `Course` table. All the data in the column will be lost.
- You are about to drop the column `instructor` on the `Course` table. All the data in the column will be lost.
- You are about to drop the column `lastUpdate` on the `Course` table. All the data in the column will be lost.
- You are about to drop the column `name` on the `Course` table. All the data in the column will be lost.
- You are about to drop the column `overview` on the `Course` table. All the data in the column will be lost.
- You are about to drop the column `recommendedGrade` on the `Course` table. All the data in the column will be lost.
- You are about to drop the column `remarks` on the `Course` table. All the data in the column will be lost.
- You are about to drop the column `type` on the `Course` table. All the data in the column will be lost.
- You are about to drop the `CourseSchedule` table. If the table is not empty, all the data it contains will be lost.

*/
-- DropForeignKey
ALTER TABLE "CourseSchedule" DROP CONSTRAINT "CourseSchedule_courseCode_fkey";

-- AlterTable
ALTER TABLE "Course" DROP COLUMN "credits",
DROP COLUMN "error",
DROP COLUMN "instructor",
DROP COLUMN "lastUpdate",
DROP COLUMN "name",
DROP COLUMN "overview",
DROP COLUMN "recommendedGrade",
DROP COLUMN "remarks",
DROP COLUMN "type";

-- DropTable
DROP TABLE "CourseSchedule";

-- DropEnum
DROP TYPE "Day";

-- DropEnum
DROP TYPE "Module";
27 changes: 27 additions & 0 deletions prisma/migrations/20250117110807_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Warnings:

- The primary key for the `Course` table will be changed. If it partially fails, the table could be left without primary key constraint.
- You are about to drop the `_CourseToUser` table. If the table is not empty, all the data it contains will be lost.
- Added the required column `userId` to the `Course` table without a default value. This is not possible if the table is not empty.

*/
-- DropForeignKey
ALTER TABLE "_CourseToUser" DROP CONSTRAINT "_CourseToUser_A_fkey";

-- DropForeignKey
ALTER TABLE "_CourseToUser" DROP CONSTRAINT "_CourseToUser_B_fkey";

-- DropIndex
DROP INDEX "Course_code_key";

-- AlterTable
ALTER TABLE "Course" DROP CONSTRAINT "Course_pkey",
ADD COLUMN "userId" TEXT NOT NULL,
ADD CONSTRAINT "Course_pkey" PRIMARY KEY ("userId", "code");

-- DropTable
DROP TABLE "_CourseToUser";

-- AddForeignKey
ALTER TABLE "Course" ADD CONSTRAINT "Course_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
12 changes: 11 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ model User {

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

courses Course[]
}

model Account {
Expand Down Expand Up @@ -56,7 +58,7 @@ model VerificationToken {
identifier String
token String
expires DateTime

@@id([identifier, token])
}

Expand All @@ -74,3 +76,11 @@ model Authenticator {

@@id([userId, credentialID])
}

model Course {
userId String
code String
user User @relation(fields: [userId], references: [id])

@@id([userId, code])
}
8 changes: 3 additions & 5 deletions src/app/adjust/WeekView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import React, { useState } from "react"
import { formatTime, getEventPosition } from "../../lib/draft/utils"
import { Period } from "@/lib/scheduling"
import { coursePeriodsThroughWeeks } from "@/lib/course"
import { CoursePeriod } from "@/lib/course"
import { Course } from "@/third-party/twinte-parser-type"
import { Button } from "@/components/ui/button"
import { ChevronLeft, ChevronRight } from "lucide-react"
Expand All @@ -13,7 +13,7 @@ type WeekViewProps = {
currentDate: Date
handlePeriodClick: (period: Period) => Promise<void>
isButtonActive: boolean
courses: Course[]
coursePeriods: CoursePeriod[]
}

const handleClassClick = (courseWithPeriod: CourseWithPeriod) => {
Expand All @@ -35,7 +35,7 @@ export function WeekView({
currentDate,
handlePeriodClick,
isButtonActive,
courses,
coursePeriods,
}: WeekViewProps) {
const [currentWeekIndex, setCurrentWeekIndex] = useState(0)

Expand All @@ -47,8 +47,6 @@ export function WeekView({

const hours = Array.from({ length: 24 }, (_, i) => i)

const coursePeriods = coursePeriodsThroughWeeks(courses, currentDate)

const handleNextWeek = () => {
setCurrentWeekIndex(prevIndex => Math.min(prevIndex + 1, 3))
}
Expand Down
53 changes: 28 additions & 25 deletions src/app/adjust/candidate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { WeekView } from "./WeekView"
import { Course } from "@/third-party/twinte-parser-type"
import { YesNoDialog } from "@/components/ui/dialog"
import { coursePeriodsThroughWeeks } from "@/lib/course"
import { CoursePeriod } from "@/lib/course"
import { getUserCourseCodes } from "@/lib/server"

type Props = {
title: string
Expand All @@ -20,6 +22,7 @@ type Props = {
selectedDurationMinute: number
/** 履修中の講義一覧 */
courses: Course[]
allCourses: Course[]
}

export default function Candidate(props: Props) {
Expand All @@ -34,18 +37,38 @@ export default function Candidate(props: Props) {
setIsButtonActive(props.title.trim() !== "")
}, [props.title])

// 自分の授業とその時間の配列
const coursePeriods: CoursePeriod[] = coursePeriodsThroughWeeks(props.courses, new Date())

async function handleSchedule() {
setIsButtonActive(false)
try {
const coursePeriods = coursePeriodsThroughWeeks(props.courses, new Date())
// 招待相手ごとの授業とその時間の配列
const coursesOfGuests: Course[][] = (
await Promise.all(props.selectedUserIds.map(getUserCourseCodes))
).map(codes => props.allCourses.filter(({ code }) => codes.includes(code)))

const coursePeriodsOfGuests = coursesOfGuests.map(courses =>
coursePeriodsThroughWeeks(courses, new Date()),
)

console.debug("自分の授業とその時間の配列", coursePeriods)
console.debug("招待相手ごとの授業とその時間の配列", coursePeriodsOfGuests)

// 自分と招待相手全員を合わせた授業時間の配列
const classPeriodsOfUsers: Period[] = [...coursePeriodsOfGuests, coursePeriods].flatMap(
coursePeriods => coursePeriods.flatMap(({ periods }) => periods),
)

console.debug("すべての授業の、授業時間の配列", classPeriodsOfUsers)

const periods = [
...(await periodsOfUsers(props.selectedUserIds, props.excludePeriod)),
coursePeriods.flatMap(({ periods }) => periods),
classPeriodsOfUsers,
]
console.debug("授業とそれぞれの授業時間の配列", coursePeriods)

const freePeriods = await findFreePeriods(props.selectedDurationMinute, periods)
console.log("freePfreePeriods:", freePeriods)
console.log("freePeriods:", freePeriods)

setFreePeriods(freePeriods)
} catch (e) {
Expand Down Expand Up @@ -122,31 +145,11 @@ export default function Candidate(props: Props) {
handlePeriodClick={handlePeriodClick}
periods={freePeriods}
isButtonActive={isButtonActive}
courses={props.courses}
coursePeriods={coursePeriods}
/>
) : (
""
)}

{/* <ul className="py-4 space-y-2">

{freePeriods.map(period => (
<li key={period.start.toString()}>
<Button
disabled={!isButtonActive}
variant={selectedPeriod?.start === period.start ? "secondary" : "ghost"}
className="w-full justify-between font-normal"
onClick={() => handlePeriodClick(period)}
>
<span className="flex items-center mr-2 h-4 w-4">
<span>{formatDate(period.start)}</span>
<span className="px-2">~</span>
<span>{formatDate(period.end)}</span>
</span>
</Button>
</li>
))}
</ul> */}
</div>
)
}
21 changes: 17 additions & 4 deletions src/app/adjust/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,20 @@ import ImportFileButton from "@/components/ImportFileButton"
// { id: 6, name: "しゅんたろう", mail: "hiromichiosato@gmail.com" },
// ]

export default function SchedulePlanner(props: { isSignedIn: boolean; users: User[] }) {
const { isSignedIn, users } = props
export default function SchedulePlanner(props: {
currentUserId: string | null
users: User[]
allCourses?: Course[]
courses?: Course[]
}) {
const { currentUserId, users } = props
const isSignedIn = currentUserId != null
const [title, setTitle] = useState("")
const [selectedUserIds, setSelectedUserIds] = useState<string[]>([])
const [selectedDurationMinute, setSelectedDurationMinute] = useState<number>(60)
const [excludePeriod, setExcludePeriod] = useState<ExcludePeriod>({ start: 22, end: 8 })
const [courses, setCourses] = useState<Course[]>([])
const [courses, setCourses] = useState<Course[]>(props.courses ?? [])
const [allCourses, setAllCourses] = useState<Course[]>(props.allCourses ?? [])

return (
<div className="max-w-4xl mx-auto mt-10 p-6 bg-white rounded-lg shadow-md">
Expand All @@ -62,7 +69,12 @@ export default function SchedulePlanner(props: { isSignedIn: boolean; users: Use
<Exclusion dispatch={setExcludePeriod} defaultValue={excludePeriod} />
{isSignedIn ? (
<div className="pl-8">
<ImportFileButton setCourses={setCourses} />
<ImportFileButton
setCourses={setCourses}
allCourses={allCourses}
setAllCourses={setAllCourses}
currentUserId={currentUserId}
/>
</div>
) : (
""
Expand Down Expand Up @@ -104,6 +116,7 @@ export default function SchedulePlanner(props: { isSignedIn: boolean; users: Use
title={title}
users={users}
courses={courses}
allCourses={allCourses}
/>
<ToastContainer />
</div>
Expand Down
19 changes: 18 additions & 1 deletion src/app/adjust/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,27 @@ import { db } from "@/lib/prisma"
import SchedulePlanner from "@/app/adjust/client"
import { getServerSession } from "next-auth"
import { authOptions } from "@/lib/auth"
import { fetchCourses } from "@/third-party/twinte-parser"
import { Course } from "@/third-party/twinte-parser-type"
import { getUserCourseCodes } from "@/lib/server"

export default async function AdjustPage() {
const session = await getServerSession(authOptions)
const users = (await db.allUsers()).filter(user => user.email !== session?.user.email)
const allCourses = (await fetchCourses()) as Course[]
// console.log(allCourses)

return <SchedulePlanner users={users} isSignedIn={!!session} />
const userCourseIds = session?.user.id ? await getUserCourseCodes(session.user.id) : undefined
console.log("userCourseIds:", userCourseIds)

const courses = allCourses.filter(course => userCourseIds?.includes(course.code))

return (
<SchedulePlanner
users={users}
currentUserId={session?.user.id ?? null}
allCourses={allCourses}
courses={userCourseIds ? courses : undefined}
/>
)
}
Loading