Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- Refactor graph helper functions from `app/Svg/Database.hs` to `app/Models/Graph.hs`
- Refactor functions for performing matrix operations from `app/Svg/Parser.hs` to `app/Util/Matrix.hs`
- Updated documentation in `app/Util/Blaze.hs`
- Moved the `Course` data type from `Database/Tables.hs` into `Models/Course.hs`, renamed it to `CourseData`

## [0.7.2] - 2025-12-10

Expand Down
15 changes: 0 additions & 15 deletions app/Database/Tables.hs
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,6 @@ data MeetTime = MeetTime {meetInfo :: Meeting, timeInfo :: [Time'] }
data MeetTime' = MeetTime' { meetData :: Meeting, timeData :: [Time] }
deriving (Show, Generic)

-- | A Course. TODO: remove this data type (it's redundant).
data Course =
Course { breadth :: Maybe T.Text,
description :: Maybe T.Text,
title :: Maybe T.Text,
prereqString :: Maybe T.Text,
allMeetingTimes :: Maybe [MeetTime'],
name :: !T.Text,
exclusions :: Maybe T.Text,
distribution :: Maybe T.Text,
coreqs :: Maybe T.Text,
videoUrls :: [T.Text]
} deriving (Show, Generic)

instance ToJSON Course
instance ToJSON Program
instance ToJSON Time
instance ToJSON MeetTime'
Expand Down
51 changes: 40 additions & 11 deletions app/Models/Course.hs
Original file line number Diff line number Diff line change
@@ -1,23 +1,52 @@
{-# LANGUAGE DeriveGeneric #-}

module Models.Course
(buildCourse,
(CourseData (..),
buildCourse,
returnCourse,
prereqsForCourse,
getDeptCourses,
insertCourse) where

import Config (runDb)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Aeson (ToJSON)
import Data.Maybe (fromMaybe)
import qualified Data.Text as T (Text, append, filter, snoc, toUpper)
import Database.Persist.Class (selectKeysList)
import Database.Persist.Sqlite (Entity, PersistValue (PersistText), SqlPersistM, entityVal, get,
insert_, rawSql, selectFirst, selectList, (<-.), (==.))
import Database.Tables hiding (breadth, distribution)
import Database.Tables (Breadth (breadthDescription),
Courses (coursesBreadth, coursesCode, coursesCoreqs, coursesDescription, coursesDistribution, coursesExclusions, coursesPrereqString, coursesTitle, coursesVideoUrls),
Distribution (distributionDescription),
EntityField (BreadthDescription, CoursesCode, DistributionDescription, MeetingCode),
Key, MeetTime', Meeting (meetingCode))
import GHC.Generics (Generic)
import Models.Meeting (buildMeetTimes, meetingQuery)

-- | The data for a single course, as returned by the back-end to the front-end.
-- This is different from the schema-defined 'Courses' type (in "Database.Tables")
-- 'Courses' describes how a course is stored in the database, whereas
-- 'CourseData' describes the shape of the information sent to the client
-- when a course is requested.
data CourseData =
CourseData { breadth :: Maybe T.Text,
description :: Maybe T.Text,
title :: Maybe T.Text,
prereqString :: Maybe T.Text,
allMeetingTimes :: Maybe [MeetTime'],
name :: !T.Text,
exclusions :: Maybe T.Text,
distribution :: Maybe T.Text,
coreqs :: Maybe T.Text,
videoUrls :: [T.Text]
} deriving (Show, Generic)

instance ToJSON CourseData

-- | Queries the database for all information about @course@,
-- constructs and returns a Course value.
returnCourse :: T.Text -> IO (Maybe Course)
-- constructs and returns a CourseData value.
returnCourse :: T.Text -> IO (Maybe CourseData)
returnCourse lowerStr = runDb $ do
let courseStr = T.toUpper lowerStr
-- TODO: require the client to pass the full course code
Expand All @@ -44,13 +73,13 @@ getDescriptionD (Just key) = do
maybeDistribution <- get key
return $ fmap distributionDescription maybeDistribution

-- | Builds a Course structure from a tuple from the Courses table.
-- | Builds a CourseData structure from a tuple from the Courses table.
-- Some fields still need to be added in.
buildCourse :: [MeetTime'] -> Courses -> SqlPersistM Course
buildCourse :: [MeetTime'] -> Courses -> SqlPersistM CourseData
buildCourse allMeetings course = do
cBreadth <- getDescriptionB (coursesBreadth course)
cDistribution <- getDescriptionD (coursesDistribution course)
return $ Course cBreadth
return $ CourseData cBreadth
-- TODO: Remove the filter and allow double-quotes
(fmap (T.filter (/='\"')) (coursesDescription course))
(fmap (T.filter (/='\"')) (coursesTitle course))
Expand All @@ -77,7 +106,7 @@ prereqsForCourse courseCode = runDb $ do
fromMaybe "" $ coursesPrereqString $ entityVal courseEntity)
) :: SqlPersistM (Either String (T.Text, T.Text))

getDeptCourses :: MonadIO m => T.Text -> m [Course]
getDeptCourses :: MonadIO m => T.Text -> m [CourseData]
getDeptCourses dept = liftIO $ runDb $ do
courses :: [Entity Courses] <- rawSql "SELECT ?? FROM courses WHERE code LIKE ?" [PersistText $ T.snoc dept '%']
let deptCourses = map entityVal courses
Expand Down Expand Up @@ -111,10 +140,10 @@ getBreadthKey description_ = do

-- | Inserts course into the Courses table.
insertCourse :: (Courses, T.Text, T.Text) -> SqlPersistM ()
insertCourse (course, breadth, distribution) = do
insertCourse (course, breadthDesc, distributionDesc) = do
maybeCourse <- selectFirst [CoursesCode ==. coursesCode course] []
breadthKey <- getBreadthKey breadth
distributionKey <- getDistributionKey distribution
breadthKey <- getBreadthKey breadthDesc
distributionKey <- getDistributionKey distributionDesc
case maybeCourse of
Nothing -> insert_ $ course {coursesBreadth = breadthKey,
coursesDistribution = distributionKey}
Expand Down