scheduling-0.1.0.0: app/Main.hs
module Main where
import qualified Data.Map as Map
import Data.Map (Map)
import Data.Text (Text)
import Options.Generic (Generic, ParseRecord, Unwrapped, Wrapped, unwrapRecord, (:::), type (<?>)(..))
import Data.Text (Text)
import System.IO (stdout)
import Data.Maybe (fromMaybe, catMaybes, fromJust)
import Text.Printf (printf)
import Control.Monad
import Control.Scheduling
import Data.Time
import Data.Time.Format
import Control.Lens hiding (Wrapped, Unwrapped)
import qualified Data.ByteString.Lazy as BS
import Debug.Trace (traceShowId)
--import Control.Monad.Log
import qualified Network.Google.Sheets as S
import Network.Google.Resource.Sheets.Spreadsheets.Get
import Network.Google.Sheets hiding (Text, sheet)
sheetName :: Sheet -> Maybe Text
sheetName s = join $ s ^. sProperties <&> view sTitle
events = [ ("Welcome", ("Mon 08:00", "Mon 09:00"))
, ("Shared lunch", ("Wed 12:00", "Wed 13:00"))
]
data Args w = Init { access :: w ::: Maybe Text <?> "Access code for Google Sheets endpoint"
, force :: w ::: Bool <?> "If set, existing tables will be overwritten"
, windows :: w ::: [String] <?> "Time windows (can specify multiple times): format is 'YYYY-MM-DD HH:MM-HH:MM'"
--, granularity :: w ::: Maybe Integer <?> "Time granularity, in minutes, for scheduling"
}
| Show { access :: w ::: Maybe Text <?> "Access code for Google Sheets endpoint"
}
| Solve { access :: w ::: Maybe Text <?> "Access code for Google Sheets endpoint"
, force :: w ::: Bool <?> "If set, existing tables will be overwritten"
}
| Emails { access :: w ::: Maybe Text <?> "Access code for Google Sheets endpoint"
, jhuFacultyEmails :: w ::: String <?> "Path to write JHU faculty emails"
, nonJhuFacultyEmails :: w ::: String <?> "Path to write non-JHU-faculty emails"
--, nonFacultyEmails :: w ::: String <?> "Path to write non-faculty emails"
, prospectEmails :: w ::: String <?> "Path to write prospect emails"
}
deriving (Generic)
instance ParseRecord (Args Wrapped)
deriving instance Show (Args Unwrapped)
-- add resources!
generateSlots gran wins = do
let dates = ["2022-01-" ++ (show d) | d <- [17..21]]
toDrop <- sequence $ [] -- stringsToSlots "2021-03-08" "2021-03-08" "17:00" "17:00" gran
--, stringsToSlots "2021-03-10" "2021-03-10" "17:00" "19:00" gran
--, stringsToSlots "2021-03-12" "2021-03-12" "17:00" "19:00" gran
--]
slots <- sequence $ [stringsToSlots d d "08:00" "22:00" gran | d <- dates]
let toDrop' = concat toDrop
slots' = [s | s <- concat slots, not $ s `elem` toDrop']
return slots'
getCurrentCount :: State -> Map (Text, Text) Int
getCurrentCount state = Map.fromListWith (+) (pro ++ base)
where
fac = (Map.toList . fromMaybe Map.empty) $ state^.individualMeetings
base = map (\p -> ((p^.firstName, p^.lastName), 0)) (state ^. prospects)
mts = concat $ map (Map.toList . snd) fac
pro = map ((,1) . fst) mts
getCurrentCount' :: State -> [((Text, Text), [(Text, Text)])]
getCurrentCount' state = fac --Map.fromListWith (+) (pro ++ base)
where
fac = (map (\(k, v) -> (k, Map.keys v)) . Map.toList . fromMaybe Map.empty) $ state^.individualMeetings
--base = map (\p -> ((p^.firstName, p^.lastName), Map.size $ p^.)) (state ^. faculty)
--mts = concat $ map (Map.toList . snd) fac
--pro = map ((,1) . fst) mts
main :: IO ()
main = do
ps <- unwrapRecord "" :: IO (Args Unwrapped)
let sid = fromMaybe "NULL" (access ps)
ssheet <- readSpreadsheet sid
let state = readState (generateSlots 60 []) ssheet
case ps of Init{..} -> do
let slu = ssheet^.sheetLookup
state' = state { _slots=generateSlots 60 [] }
if ((slu Map.!? "Interviewer Preferences") == Nothing && (slu Map.!? "Interviewer Availability") == Nothing) || force == True
then writeForms sid (slu Map.!? "Interviewer Availability") (slu Map.!? "Interviewer Preferences") (slu Map.!? "Prospect Availability") state'
else putStrLn "Refusing to overwrite existing forms (you may specify '--force' if you're sure)"
Show{..} -> do
--let mtg = (Map.toList . Map.map (\x -> length $ Map.keys x) . fromJust) $ state^.individualMeetings
--putStrLn $ (show . sum . map snd) mtg
--putStrLn $ unlines (map show mtg)
putStrLn $ show $ (state^.individualMeetings)
putStrLn $ unlines $ map show (getCurrentCount' state)
--print $ getCurrentCount state
return ()
--putStrLn $ show $ (head $ state^.faculty) ^.availability
--putStrLn $ show $ state^.faculty
--putStrLn $ show $ state^.requestedMeetings
--putStrLn $ show $ state^.individualMeetings
--printSchedules (compressSlots state)
Solve{..} -> do
let slu = ssheet^.sheetLookup
state' = state { _slots=generateSlots 60 [] }
--let state' = compressSlots $ state { _slots= Just $ [s | s <- (fromMaybe [] (defaultSlots 60))]}
state'' <- solveSchedule state'
if ((slu Map.!? "Interviewer Schedule") == Nothing && (slu Map.!? "Prospect Schedule" == Nothing)) || force == True
then writeSchedule sid (slu Map.!? "Interviewer Schedule") (slu Map.!? "Prospect Schedule") state'' (fromJust $ generateSlots 60 [])
else putStrLn "Refusing to overwrite existing schedule (you may specify '--force' if you're sure)"
--print $ getCurrentCount state''
--print $ map (\(x, y) -> (x, length y)) (getCurrentCount' state'')
--putStrLn $ unlines $ map (show .
return ()
Emails{..} -> do
emails <- generateEmails prospectEmails jhuFacultyEmails nonJhuFacultyEmails state
sequence $ map (\(f, m) -> BS.writeFile f m) emails
return ()