From 9d0276b7e7fb290d6e6fb4a04e09e825e76d537e Mon Sep 17 00:00:00 2001 From: 0rphee <0rph3e@proton.me> Date: Wed, 5 Jun 2024 19:31:16 -0600 Subject: [PATCH] ical support working --- .gitignore | 1 + schedules.xlsx | Bin 10553 -> 14305 bytes src/CmdLineOpts.hs | 32 +++++--------- src/PPrint.hs | 4 +- src/Validation.hs | 7 +-- src/WriteiCal.hs | 107 ++++++++++++++++++++++++++++----------------- stack.yaml | 2 +- 7 files changed, 84 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index 68c66d1..20fadef 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ Temporary Items .apdisk schedule-maker-test.svg +*.ics diff --git a/schedules.xlsx b/schedules.xlsx index 1733e32276ed87b6d81a5ac7a533c12926cbcb0f..a6421e57bba6cf92056a22ba436cc5dbc6902afa 100644 GIT binary patch literal 14305 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 F{|6M#K|%lk literal 10553 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 D%g#o; diff --git a/src/CmdLineOpts.hs b/src/CmdLineOpts.hs index b610047..c827a5d 100644 --- a/src/CmdLineOpts.hs +++ b/src/CmdLineOpts.hs @@ -1,6 +1,6 @@ {-# LANGUAGE LambdaCase #-} -module CmdLineOpts (options, Options (..), execParser, ExampleYamlLanguage (..),) where +module CmdLineOpts (options, Options (..), execParser, ExampleYamlLanguage (..)) where import Options.Applicative @@ -9,7 +9,7 @@ data Options !FilePath -- yamlSource !Bool -- prettyPrintToStdout !FilePath -- outputFilePath - !(Maybe FilePath) -- create *.ical schedule + !Bool -- create *.ical schedule | PrintExampleYaml !ExampleYamlLanguage -- True english, False spanish data ExampleYamlLanguage @@ -33,7 +33,7 @@ options = opts = languageParser <|> normalOpts normalOpts :: Parser Options -normalOpts = NormalOptions <$> yamlPath <*> prettyPrintStdout <*> outputPath <*> icalPath +normalOpts = NormalOptions <$> yamlPath <*> prettyPrintStdout <*> outputPath <*> writeICal languageParser :: Parser Options languageParser = @@ -81,22 +81,10 @@ outputPath = <> short 'o' ) -icalPath :: Parser (Maybe FilePath) -icalPath = optional first <|> second - where first = - strOption - ( metavar "FILENAME" - <> help "Write output to FILE (.ical)" - <> action "directory" - <> action "file" - <> long "ical" - <> short 'i' - ) - second = - flag Nothing (Just "schedules.ical") - ( - help "Write output to FILE (.ical)" - <> long "ical" - <> short 'i' - - ) +writeICal :: Parser Bool +writeICal = + switch + ( help "Write the schedules to iCal files (schedule1.ics, schedule2.ics)" + <> long "ical" + <> short 'i' + ) diff --git a/src/PPrint.hs b/src/PPrint.hs index 57bdeee..070f336 100644 --- a/src/PPrint.hs +++ b/src/PPrint.hs @@ -57,8 +57,8 @@ separateWith lineStyle lineChar numOfLines l r = l <> emptyLines <> separatingLi annotateErrors :: [Error] -> Doc AnsiStyle annotateErrors es = annotate - (color Red <> bold) - (concatWith (separateWith bold '-' 1) (map annotateError es)) + (color Red <> bold) $ + concatWith (separateWith bold '-' 1) (map annotateError es) <> line annotateSubjectList :: [IDandSubj] -> Doc AnsiStyle annotateSubjectList ss = concatWith (separateWith (colorDull Yellow) '-' 1) (map annotateSubject ss) diff --git a/src/Validation.hs b/src/Validation.hs index c67388c..595dbb3 100644 --- a/src/Validation.hs +++ b/src/Validation.hs @@ -17,7 +17,7 @@ import System.Console.Terminal.Size import System.IO (stdout) import Types import WriteXlsx (saveExcel) -import WriteiCal (saveICal) +import WriteiCal (saveMultipleICals) intervalsOverlap :: Interval -> Interval -> Bool intervalsOverlap (MkInterval a b) (MkInterval x y) @@ -124,7 +124,7 @@ collectValidationResults xs = do runProgLogic :: Options -> IO () runProgLogic = \case PrintExampleYaml lang -> printYaml lang - NormalOptions yamlSource prettyPrintToStdout outputFilePath mayICalFilePath -> do + NormalOptions yamlSource prettyPrintToStdout outputFilePath writeICals -> do res <- decodeFileEither yamlSource -- "test-english.yaml" sz <- size >>= \case @@ -141,5 +141,6 @@ runProgLogic = \case Right lists -> do when prettyPrintToStdout $ prettyRender (annotateSubjectLists lists) - maybe (pure ()) (saveICal lists) mayICalFilePath + when writeICals $ saveMultipleICals lists + saveExcel lists outputFilePath diff --git a/src/WriteiCal.hs b/src/WriteiCal.hs index c4dd66e..10acf7f 100644 --- a/src/WriteiCal.hs +++ b/src/WriteiCal.hs @@ -1,10 +1,11 @@ {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} -module WriteiCal (saveICal) where +module WriteiCal (saveMultipleICals) where import Data.ByteString.Lazy.Char8 qualified as BSL import Data.Default +import Data.Foldable (traverse_) import Data.Function ((&)) import Data.Map.Strict as M import Data.Text qualified as TS @@ -54,49 +55,65 @@ emptyVEvent = veOther = def } -toVCal :: Day -> [IDandSubj] -> VCalendar -toVCal weekStartDay subjects = - emptyVCalendar - { vcEvents = vEventMap - } +toVCal :: Day -> [IDandSubj] -> IO VCalendar +toVCal weekStartDay subjects = do + emap <- vEventMap + pure $ + emptyVCalendar + { vcEvents = emap + } where - vEventMap :: Map (TL.Text, Maybe (Either Date DateTime)) VEvent - vEventMap = ((\(txt, ev) -> ((txt, Nothing), ev)) <$> vEventList) & M.fromList + vEventMap :: IO (Map (TL.Text, Maybe (Either Date DateTime)) VEvent) + vEventMap = do + elist <- vEventList + pure $ ((\(txt, ev) -> ((txt, Nothing), ev)) <$> elist) & M.fromList - vEventList :: [(TL.Text, VEvent)] - vEventList = concatMap idandsubjToVEvents subjects + vEventList :: IO [(TL.Text, VEvent)] + vEventList = concat <$> traverse idandsubjToVEvents subjects - idandsubjToVEvents :: IDandSubj -> [(TL.Text, VEvent)] - idandsubjToVEvents (IDandSubj (subId, subj)) = fmap (classToEvent subId subj.subjName subj.subjProfessor) subj.subjclasses + idandsubjToVEvents :: IDandSubj -> IO [(TL.Text, VEvent)] + idandsubjToVEvents (IDandSubj (subId, subj)) = traverse (classToEvent subId subj.subjName subj.subjProfessor) subj.subjclasses - classToEvent :: TS.Text -> TS.Text -> TS.Text -> Class -> (TL.Text, VEvent) -- T.Text: UID value - classToEvent subId name teacher individualClass = - ( uidText, - emptyVEvent - { veSummary = - Just $ - Summary - { summaryValue = TL.fromStrict (name <> "(" <> subId <> ")"), - summaryLanguage = def, - summaryAltRep = def, - summaryOther = def - }, - veUID = UID uidText def, - veDTStart = Just startDatetime, - veDTEndDuration = Just $ Left endDatetime, - veDescription = - Just $ - Description - { descriptionValue = TL.fromStrict teacher, - descriptionLanguage = def, - descriptionAltRep = def, - descriptionOther = def - } - } - ) + classToEvent :: TS.Text -> TS.Text -> TS.Text -> Class -> IO (TL.Text, VEvent) -- T.Text: UID value + classToEvent subId name teacher individualClass = do + uidText <- getUidText + pure $ + ( uidText, + emptyVEvent + { veSummary = + Just $ + Summary + { summaryValue = TL.fromStrict (name <> "(" <> subId <> ")"), + summaryLanguage = def, + summaryAltRep = def, + summaryOther = def + }, + veUID = UID uidText def, + veDTStart = Just startDatetime, + veDTEndDuration = Just $ Left endDatetime, + veDescription = + Just $ + Description + { descriptionValue = TL.fromStrict teacher, + descriptionLanguage = def, + descriptionAltRep = def, + descriptionOther = def + } + } + ) where - uidText :: TL.Text - uidText = TL.fromStrict subId <> TL.pack (show $ getClassDayOffset individualClass) + getUidText :: IO TL.Text + getUidText = do + time <- TL.pack . show <$> getSystemTime + let res = + TL.fromStrict subId + <> "-" + <> TL.replace " " "_" (TL.fromStrict name) + <> "-" + <> TL.pack (show $ getClassDayOffset individualClass) + <> "-" + <> time + pure res dayOfClass :: Day dayOfClass = addDays (getClassDayOffset individualClass) weekStartDay @@ -136,11 +153,19 @@ renderICal :: [IDandSubj] -> IO BSL.ByteString renderICal idAndSubj = do (LocalTime today _) <- getLocalTime let nextMonday = addDays 1 $ sundayAfter today - let vcal = toVCal nextMonday idAndSubj + vcal <- toVCal nextMonday idAndSubj pure $ printICalendar def vcal - saveICal :: [IDandSubj] -> FilePath -> IO () saveICal idAndSubj filepath = do renderedICal <- renderICal idAndSubj BSL.writeFile filepath renderedICal + +saveMultipleICals :: [[IDandSubj]] -> IO () +saveMultipleICals schedules = traverse_ (uncurry saveICal) schedulesWithNames + where + joinLists :: [IDandSubj] -> Int -> ([IDandSubj], FilePath) + joinLists singleSchedule scheduleNumber = (singleSchedule, "schedule" <> show scheduleNumber <> ".ics") + + schedulesWithNames :: [([IDandSubj], FilePath)] + schedulesWithNames = zipWith joinLists schedules [1 ..] diff --git a/stack.yaml b/stack.yaml index e300406..4bb5983 100644 --- a/stack.yaml +++ b/stack.yaml @@ -44,7 +44,7 @@ ghc-options: # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a # extra-deps: - - git: git@github.com:0rphee/iCalendar.git + - git: https://github.com/0rphee/iCalendar.git commit: "b31ccf7c1f68532f5c6beb0e50128290fa5d03f3" - mime-0.4.0.2@sha256:208947d9d1a19d08850be67ecb28c6e776db697f3bba05bd9d682e51a59f241f,983