Skip to content

Commit

Permalink
BREAKING CHANGES: included syllabus in search
Browse files Browse the repository at this point in the history
  • Loading branch information
ImJustChew committed Nov 7, 2023
1 parent b5cfc44 commit 1d32463
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 15 deletions.
24 changes: 24 additions & 0 deletions database/indexes/courses.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ create index courses_teacher_en ON courses USING pgroonga(teacher_en);
create index courses_department ON courses USING pgroonga(department);
create index courses_course ON courses USING pgroonga(course);
create index courses_raw_id ON courses USING pgroonga(raw_id);
create index courses_keywords ON course_syllabus USING pgroonga(keywords);

CREATE OR REPLACE FUNCTION search_courses(keyword text)
RETURNS SETOF courses AS
Expand All @@ -26,4 +27,27 @@ BEGIN
END IF;
END
$func$
LANGUAGE plpgsql;

DROP FUNCTION search_courses_with_syllabus;

CREATE OR REPLACE FUNCTION search_courses_with_syllabus(keyword text)
RETURNS SETOF courses_with_syllabus AS
$func$
BEGIN
IF trim(keyword) = '' THEN
-- Keyword is blank, return all courses
RETURN QUERY SELECT * FROM courses_with_syllabus;
ELSE
-- Keyword is not blank, perform the search
RETURN QUERY SELECT * FROM courses_with_syllabus
WHERE name_zh &@~ keyword
OR teacher_zh &@~ keyword
OR venues &@~ keyword
OR name_en &@~ keyword
OR teacher_en &@~ keyword
OR raw_id &@ keyword;
END IF;
END
$func$
LANGUAGE plpgsql;
7 changes: 7 additions & 0 deletions database/views/courses_with_syllabus.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE OR REPLACE VIEW courses_with_syllabus AS
SELECT
c.*,
cs.brief,
cs.keywords
FROM courses c
LEFT JOIN course_syllabus cs ON c.raw_id = cs.raw_id;
22 changes: 22 additions & 0 deletions src/app/[lang]/courses/[courseId]/DownloadSyllabus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client';
import supabase from "@/config/supabase";
import { RawCourseID } from "@/types/courses";
import { Button } from "@mui/joy";
import { DownloadCloud } from "react-feather";

const DownloadSyllabus = ({ courseId }: { courseId: RawCourseID }) => {
const handleDownload = async () => {

const fileURL = `${process.env.NEXT_PUBLIC_SUPABASE_URL}storage/v1/object/public/syllabus/${encodeURIComponent(courseId)}.pdf`;
const link = document.createElement('a')
link.href = fileURL
link.setAttribute('download', courseId+'.pdf')
document.body.appendChild(link)
link.click()
link.remove()
}

return <Button variant="outlined" startDecorator={<DownloadCloud />} onClick={handleDownload}>Download PDF</Button>
}

export default DownloadSyllabus;
22 changes: 17 additions & 5 deletions src/app/[lang]/courses/[courseId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import Fade from "@/components/Animation/Fade";
import { getDictionary } from "@/dictionaries/dictionaries";
import {getCourse, getCoursePTTReview} from '@/lib/course';
import {getCourse, getCoursePTTReview, getCourseWithSyllabus} from '@/lib/course';
import { LangProps } from "@/types/pages";
import {Accordion, AccordionDetails, AccordionGroup, AccordionSummary, Alert, Chip, Divider, Button} from '@mui/joy';
import { format } from "date-fns";
import { ResolvingMetadata } from "next";
import {AlertTriangle, Minus, Plus} from 'react-feather';
import {AlertTriangle, DownloadCloud, Minus, Plus} from 'react-feather';
import { redirect } from 'next/navigation'
import CourseTagList from "@/components/Courses/CourseTagsList";
import {useSettings} from '@/hooks/contexts/settings';
Expand All @@ -14,6 +14,7 @@ import SelectCourseButton from '@/components/Courses/SelectCourseButton';
import { createTimetableFromCourses } from "@/helpers/timetable";
import Timetable from "@/components/Timetable/Timetable";
import { MinimalCourse } from "@/types/courses";
import DownloadSyllabus from "./DownloadSyllabus";

type PageProps = {
params: { courseId? : string }
Expand All @@ -29,7 +30,7 @@ export async function generateMetadata({ params }: PageProps, parent: ResolvingM

const CourseDetailPage = async ({ params }: PageProps & LangProps) => {
const courseId = decodeURI(params.courseId as string);
const course = await getCourse(courseId);
const course = await getCourseWithSyllabus(courseId);

const reviews = await getCoursePTTReview(courseId);
const dict = await getDictionary(params.lang);
Expand Down Expand Up @@ -71,9 +72,16 @@ const CourseDetailPage = async ({ params }: PageProps & LangProps) => {
<Divider/>
<div className="grid grid-cols-1 md:grid-cols-[auto_320px] gap-6 ">
<div className="space-y-4">
<div className="">
<h3 className="font-semibold text-xl mb-2">Brief</h3>
<p className="whitespace-pre-line text-sm">{course.course_syllabus.brief}</p>
</div>
<div className="">
<h3 className="font-semibold text-xl mb-2">{dict.course.details.description}</h3>
<p>{course?.note}</p>
<p className="whitespace-pre-line text-sm">
{course.course_syllabus.content ?? <>
<DownloadSyllabus courseId={course.raw_id}/>
</>}</p>
</div>
{course?.prerequisites && <div className="">
<h3 className="font-semibold text-xl mb-2">儅修</h3>
Expand Down Expand Up @@ -101,9 +109,13 @@ const CourseDetailPage = async ({ params }: PageProps & LangProps) => {
</div>}
</div>
<div className="space-y-2">
<div>
<h3 className="font-semibold text-base mb-2">Note</h3>
<p>{course.note}</p>
</div>
<div>
<h3 className="font-semibold text-base mb-2">{dict.course.details.restrictions}</h3>
<p>{course.restrictions}</p>
<p>{course.restrictions ?? "無"}</p>
</div>
<div>
<h3 className="font-semibold text-base mb-2">{dict.course.details.compulsory}</h3>
Expand Down
8 changes: 4 additions & 4 deletions src/app/[lang]/courses/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';;
import CourseListItem from "@/components/Courses/CourseListItem";
import InputControl from "@/components/FormComponents/InputControl";
import supabase, { CourseDefinition } from "@/config/supabase";
import supabase, {CourseDefinition, CourseSyllabusView} from '@/config/supabase';
import { Button, CircularProgress, Divider, Drawer, IconButton, LinearProgress, Stack } from "@mui/joy";
import { NextPage } from "next";
import { useEffect, useState, Fragment, useRef, use, useMemo } from "react";
Expand Down Expand Up @@ -56,7 +56,7 @@ const emptyFilters: RefineControlFormTypes = {

const CoursePage: NextPage = () => {
const dict = useDictionary();
const [courses, setCourses] = useState<CourseDefinition[]>([]);
const [courses, setCourses] = useState<CourseSyllabusView[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [totalCount, setTotalCount] = useState<number>(0);
const [headIndex, setHeadIndex] = useState<number>(0);
Expand Down Expand Up @@ -134,7 +134,7 @@ const CoursePage: NextPage = () => {
setLoading(true);
//Query for courses
try {
let temp = supabase.rpc('search_courses', {
let temp = supabase.rpc('search_courses_with_syllabus', {
keyword: filters.textSearch,
}, { count: 'exact' })
if (filters.level.length)
Expand Down Expand Up @@ -196,7 +196,7 @@ const CoursePage: NextPage = () => {
if (error) console.error(error);
else {
console.log(courses)
setCourses(courses as CourseDefinition[]);
setCourses(courses as CourseSyllabusView[]);
setHeadIndex(index);
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/components/Courses/CourseListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { CourseDefinition } from '@/config/supabase';
import {CourseDefinition, CourseSyllabusView} from '@/config/supabase';
import useDictionary from '@/dictionaries/useDictionary';
import { useSettings } from '@/hooks/contexts/settings';
import { Button, Tooltip } from '@mui/joy';
Expand All @@ -9,8 +9,7 @@ import Link from 'next/link';
import CourseTagList from './CourseTagsList';
import SelectCourseButton from './SelectCourseButton';


const CourseListItem: FC<{ course: CourseDefinition }> = ({ course }) => {
const CourseListItem: FC<{ course: CourseSyllabusView }> = ({ course }) => {
const dict = useDictionary();

return <div className="text-gray-600 dark:text-gray-400 px-4 border-b border-gray-200 dark:border-neutral-800 pb-4">
Expand All @@ -21,6 +20,7 @@ const CourseListItem: FC<{ course: CourseDefinition }> = ({ course }) => {
<h3 className="text-sm text-gray-800 dark:text-gray-300 mt-0 break-words">{course.name_en} - <span className='w-max'>{(course.teacher_en ?? []).join(',')}</span></h3>
</div>
<div className="space-y-1 text-black dark:text-neutral-200">
<p className='text-sm whitespace-pre-line'>{course.brief}</p>
<p className='text-sm whitespace-pre-line'>{course.restrictions}</p>
<p className='text-sm whitespace-pre-line'>{course.note}</p>
{course.prerequisites &&
Expand All @@ -40,8 +40,8 @@ const CourseListItem: FC<{ course: CourseDefinition }> = ({ course }) => {
<p>No Venues</p>
}
</div>
<CourseTagList course={course}/>
<SelectCourseButton courseId={course.raw_id}/>
<CourseTagList course={course as unknown as CourseDefinition}/>
<SelectCourseButton courseId={course.raw_id as string}/>
</div>
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions src/config/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { createClient } from "@supabase/supabase-js";
const supabase = createClient<Database>(process.env.NEXT_PUBLIC_SUPABASE_URL ?? "", process.env.NEXT_PUBLIC_SUPABASE_KEY ?? "");

export type CourseDefinition = Database['public']['Tables']['courses']['Row'];
export type CourseSyllabusDefinition = Database['public']['Tables']['course_syllabus']['Row'];
export type CourseJoinWithSyllabus = CourseDefinition & { course_syllabus: CourseSyllabusDefinition };
export type CourseSyllabusView = Database['public']['Views']['courses_with_syllabus']['Row'];
export type AlertDefinition = Database['public']['Tables']['alerts']['Row'];
export type BusScheduleDefinition = Database['public']['Tables']['bus_schedule']['Row'];
export type CdsCourseDefinition = Database['public']['Tables']['cds_courses']['Row'];
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useUserTimetable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const useUserTimetable = (loadCourse = true) => {
const { courses, timetableTheme, setCourses } = useSettings();

const { data: allCourseData = [], error, isLoading } = useSWR(['courses', courses], async ([table, courseCodes]) => {
const { data = [], error } = await supabase.from('courses').select("*").in('raw_id', courseCodes);
const { data = [], error } = await supabase.from('courses_with_syllabus').select("*").in('raw_id', courseCodes);
if(error) throw error;
if(!data) throw new Error('No data');
return data;
Expand Down
33 changes: 33 additions & 0 deletions src/lib/course.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import supabase from '@/config/supabase';
import { parse } from 'node-html-parser';
import {CourseJoinWithSyllabus} from '@/config/supabase';


export const getCourseWithSyllabus = async (courseId: string) => {
const { data, error } = await supabase
.from('courses')
.select(`
*,
course_syllabus (
*
)
`)
.eq('raw_id', courseId);
if(error) {
console.error(error)
return null;
}
else return data![0] as unknown as CourseJoinWithSyllabus;
}


export const getCourse = async (courseId: string) => {
const { data, error } = await supabase.from('courses').select('*').eq('raw_id', courseId);
Expand All @@ -10,6 +30,19 @@ export const getCourse = async (courseId: string) => {
else return data![0];
}


export const getMinimalCourse = async (courseId: string) => {
const { data, error } = await supabase
.from('courses')
.select('raw_id, name_zh, name_en, department, course, class, credits, venues, times, teacher_zh, language')
.eq('raw_id', courseId);
if(error) {
console.error(error)
return null;
}
else return data![0];
}

export const getCoursePTTReview = async (courseId: string) => {
const course = await getCourse(courseId);
//TODO: use better search to find the posts
Expand Down
98 changes: 98 additions & 0 deletions src/types/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,12 @@ export interface Database {
columns: ["raw_id"]
referencedRelation: "courses"
referencedColumns: ["raw_id"]
},
{
foreignKeyName: "course_syllabus_raw_id_fkey"
columns: ["raw_id"]
referencedRelation: "courses_with_syllabus"
referencedColumns: ["raw_id"]
}
]
}
Expand Down Expand Up @@ -783,6 +789,51 @@ export interface Database {
}
}
Views: {
courses_with_syllabus: {
Row: {
brief: string | null
capacity: number | null
class: string | null
closed_mark: string | null
compulsory_for: string[] | null
content: string | null
course: string | null
credits: number | null
cross_discipline: string[] | null
department: string | null
elective_for: string[] | null
first_specialization: string[] | null
ge_target: string | null
ge_type: string | null
has_file: boolean | null
keywords: string | null
language: string | null
name_en: string | null
name_zh: string | null
no_extra_selection: boolean | null
note: string | null
prerequisites: string | null
raw_1_2_specialization: string | null
raw_compulsory_elective: string | null
raw_cross_discipline: string | null
raw_extra_selection: string | null
raw_id: string | null
raw_teacher_en: string | null
raw_teacher_zh: string | null
raw_time: string | null
raw_venue: string | null
reserve: number | null
restrictions: string | null
second_specialization: string[] | null
semester: string | null
tags: string[] | null
teacher_en: string[] | null
teacher_zh: string[] | null
times: string[] | null
venues: string[] | null
}
Relationships: []
}
distinct_classes: {
Row: {
class: string | null
Expand Down Expand Up @@ -1415,6 +1466,53 @@ export interface Database {
venues: string[]
}[]
}
search_courses_with_syllabus: {
Args: {
keyword: string
}
Returns: {
brief: string | null
capacity: number | null
class: string | null
closed_mark: string | null
compulsory_for: string[] | null
content: string | null
course: string | null
credits: number | null
cross_discipline: string[] | null
department: string | null
elective_for: string[] | null
first_specialization: string[] | null
ge_target: string | null
ge_type: string | null
has_file: boolean | null
keywords: string | null
language: string | null
name_en: string | null
name_zh: string | null
no_extra_selection: boolean | null
note: string | null
prerequisites: string | null
raw_1_2_specialization: string | null
raw_compulsory_elective: string | null
raw_cross_discipline: string | null
raw_extra_selection: string | null
raw_id: string | null
raw_teacher_en: string | null
raw_teacher_zh: string | null
raw_time: string | null
raw_venue: string | null
reserve: number | null
restrictions: string | null
second_specialization: string[] | null
semester: string | null
tags: string[] | null
teacher_en: string[] | null
teacher_zh: string[] | null
times: string[] | null
venues: string[] | null
}[]
}
set_limit: {
Args: {
"": number
Expand Down

0 comments on commit 1d32463

Please sign in to comment.