scheduling-0.1.0.0: src/Control/Scheduling/Sheets.hs
module Control.Scheduling.Sheets (readSpreadsheet, readState, writeForms, writeSchedule, sheetLookup) where
import Prelude hiding (group)
import qualified Network.Google.Sheets as S
import Network.Google.Resource.Sheets.Spreadsheets.Get
import Network.Google.Sheets hiding (Text, sheet)
import Network.Google
import Data.Aeson.Types
import Data.Text (Text, unpack, pack)
import qualified Data.Text as T
import Text.Printf (printf, PrintfArg(..), fmtPrecision, fmtChar, errorBadFormat, formatString, vFmt, IsChar)
import Control.Lens hiding (zoom)
import Control.Exception (try, tryJust)
import Control.Monad (join)
import Data.Maybe (catMaybes, fromJust, fromMaybe)
import Data.Time.Clock (UTCTime(..), secondsToNominalDiffTime)
import Data.Time.Format
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Set (Set)
import qualified Data.Set as Set
import System.Random
import Data.Int (Int32)
import Control.Scheduling.State
import Control.Scheduling.Person
import Control.Scheduling.TimeSpan
import Control.Scheduling.Preference
import Data.Time
import Text.Read (readMaybe)
import Data.List (intercalate, sort)
import Debug.Trace (traceShowId)
import qualified Data.Text as Text
readState :: Maybe [TimeSpan] -> Spreadsheet -> State
readState allSlots ssheet = simpleState & faculty .~ faculty' & prospects .~ prospects' & slots .~ allSlots & individualMeetings .~ indiv & requestedMeetings .~ requestedMeetings'
where
prospects' = (getPeople "Prospects") ssheet --
--prospects' = (getAvailability "Prospect Availability" ssheet . getPeople "Prospects") ssheet
faculty' = (getPeople "Interviewers") ssheet --(getAvailability "Interviewer Availability" ssheet . getPeople "Interviewers") ssheet
requestedMeetings' = Just $ (getIndividualPrefs "Individual Preferences" ssheet) ++ (getGroupPrefs "Group Preferences" ssheet)
slu = ssheet^.sheetLookup
flu = Map.fromList $ [(T.unwords [T.take 1 (p ^. firstName), p ^. lastName], (p ^. firstName, p ^. lastName)) | p <- prospects']
indiv = Just $ if "Interviewer Schedule" `Map.member` slu then Map.fromList $ [((f ^. firstName, f ^. lastName), getFacultySchedule "Interviewer Schedule" ssheet f flu) | f <- faculty'] else Map.empty
writeForms :: Text -> Maybe Int32 -> Maybe Int32 -> Maybe Int32 -> State -> IO ()
writeForms sid aid pid ppid state = do
case pid of
Nothing -> return ()
Just tid -> deleteTab sid tid
case aid of
Nothing -> return ()
Just tid -> deleteTab sid tid
case ppid of
Nothing -> return ()
Just tid -> deleteTab sid tid
createPreferenceForm sid (state ^. faculty) (state ^. prospects)
createAvailabilityForm "Interviewer Availability" sid (state ^. faculty) (fromJust $ state ^. slots)
createAvailabilityForm "Prospect Availability" sid (state ^. prospects) (fromJust $ state ^. slots)
return ()
tab :: Text -> Lens' Spreadsheet Sheet
tab name = lens (\ss -> fromJust $ ssheet ss name) undefined
addTab sid name nRows nCols index = do
env <- newEnv <&> (envScopes .~ spreadsheetsScope)
let gp = gridProperties & gpColumnCount ?~ nCols & gpRowCount ?~ nRows & gpFrozenRowCount ?~ 1 & gpFrozenColumnCount ?~ 2
sp = sheetProperties & sTitle ?~ name & sGridProperties ?~ gp & sIndex ?~ (fromIntegral index)
asr = asrProperties ?~ sp $ addSheetRequest
rv' <- try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [(reqAddSheet .~ Just asr $ request')] $ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
let Right rv = rv'
return $ fromJust $ (fromJust $ (fromJust $ (head $ rv ^. busrReplies) ^. rAddSheet) ^. aProperties) ^. sSheetId
tabId :: Sheet -> Maybe Int32
tabId s = join $ s ^. sProperties <&> view sSheetId
deleteTab :: Text -> Int32 -> IO ()
deleteTab sid tid = do
env <- newEnv <&> (envScopes .~ spreadsheetsScope)
let req = deleteSheetRequest & dsrSheetId .~ Just tid
resp <- try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [(reqDeleteSheet .~ Just req $ request')] $ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
return ()
preferenceEntryFormat :: Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int -> [ConditionalFormatRule]
preferenceEntryFormat tid sr sc er ec n = [neg]
where
cv = [conditionValue & cvUserEnteredValue ?~ (pack . show) i | i <- [0, n]]
bc = booleanCondition & bcValues .~ cv & bcType ?~ NumberNotBetween
cf = cellFormat & cfBackgRoundColor ?~ (color & cRed ?~ 0.9 & cAlpha ?~ 0.1)
br = booleanRule & brFormat ?~ cf & brCondition ?~ bc
range = gridRange & grSheetId ?~ tid & grStartRowIndex ?~ sr & grEndRowIndex ?~ (fromIntegral $ er + 1) & grStartColumnIndex ?~ sc & grEndColumnIndex ?~ (fromIntegral $ ec + 1)
neg = conditionalFormatRule & cfrBooleanRule ?~ br & cfrRanges .~ [range]
availabilityEntryFormat :: Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> [ConditionalFormatRule]
availabilityEntryFormat tid sr sc er ec = [neg]
where
bc = booleanCondition & bcType ?~ NotBlank
cf = cellFormat & cfBackgRoundColor ?~ (color & cGreen ?~ 0.9 & cAlpha ?~ 0.1)
br = booleanRule & brFormat ?~ cf & brCondition ?~ bc
range = gridRange & grSheetId ?~ tid & grStartRowIndex ?~ sr & grEndRowIndex ?~ (fromIntegral $ er + 1) & grStartColumnIndex ?~ sc & grEndColumnIndex ?~ (fromIntegral $ ec + 1)
neg = conditionalFormatRule & cfrBooleanRule ?~ br & cfrRanges .~ [range]
headerFormat :: TextFormatRun
headerFormat = textFormatRun & tfrStartIndex .~ Just 0 & tfrFormat .~ Just (textFormat & tfBold .~ Just True)
--prefHelp = extendedValue & evStringValue ?~ "Instructions: indicate how much time you would like with each prospect, from 0-4 15-minute increments. 0 indicates you *will not* meet with the student, while a blank indicates you are *willing* to do so, to help round out the schedule. The form will highlight invalid entries in red."
prefHelp = extendedValue & evStringValue ?~ "Instructions: enter '1' if you consider the student a potential advisee (one-on-one, hour-long meeting), '0' if you won't meet with the student *at all* (use sparingly, if at all), blank indicates you will meet with the student as part of a group" -- , but don't consider them a match as an advisee."
schedHelp = extendedValue & evStringValue ?~ "Instructions: enter any value to indicate you are available at that time (the particular value doesn't matter)"
--"how much time you would like with each prospect, from 0-4 15-minute increments. 0 indicates you *will not* meet with the student, while a blank indicates you are *willing* to do so, to help round out the schedule. The form will highlight invalid entries in red."
createPreferenceForm sid facs prosps = do
env <- newEnv <&> (envScopes .~ spreadsheetsScope)
let nRows = fromIntegral $ length prosps + 2
nCols = fromIntegral $ length facs + 2
tid <- addTab sid "Interviewer Preferences" nRows nCols 3
let range = gridRange & grStartRowIndex .~ Just 0 & grEndRowIndex .~ Just nRows & grStartColumnIndex .~ Just 0 & grEndColumnIndex .~ Just nCols & grSheetId .~ Just tid
header = map (\x -> x & cdTextFormatRuns .~ [headerFormat]) ([cellData & cdUserEnteredValue ?~ (extendedValue & evStringValue ?~ "First"), cellData & cdUserEnteredValue ?~ (extendedValue & evStringValue ?~ "Last")] ++ [cellData & cdUserEnteredValue .~ Just (extendedValue & evStringValue .~ Just _lastName) | Person{..} <- facs])
rows = [rowData & rdValues .~ header] ++ [preferenceRow i (fromIntegral nCols) p | (i, p) <- zip [1..] prosps]
help = [rowData & rdValues .~ [cellData & cdUserEnteredValue ?~ prefHelp]]
req = updateCellsRequest & updRange .~ Just range & updFields .~ (Just . GFieldMask) "*" & updRows .~ rows
req' = updateCellsRequest & updRange ?~ (gridRange & grStartRowIndex ?~ (nRows - 1) & grEndRowIndex ?~ nRows & grStartColumnIndex ?~ 0 & grEndColumnIndex ?~ 1 & grSheetId ?~ tid) & updRows .~ help & updFields ?~ GFieldMask "*"
fmts = [addConditionalFormatRuleRequest & acfrrRule ?~ fmt | fmt <- preferenceEntryFormat tid 1 2 (nRows - 1) nCols 4]
fmtReqs = [reqAddConditionalFormatRule ?~ fmtReq $ request' | fmtReq <- fmts]
resize = autoResizeDimensionsRequest & ardrDimensions ?~ (dimensionRange & drDimension ?~ DRDColumns & drSheetId ?~ tid & drStartIndex ?~ 0 & drEndIndex ?~ nCols)
try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [(reqUpdateCells .~ Just req $ request')] ++ fmtReqs ++ [reqAutoResizeDimensions .~ Just resize $ request']$ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [reqAutoResizeDimensions .~ Just resize $ request']$ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [(reqUpdateCells .~ Just req' $ request')] $ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
return ()
preferenceRow :: Int -> Int -> Person -> RowData
preferenceRow row nCols prosp = rowData & rdValues .~ (filled ++ rest)
where
f = prosp ^. firstName
l = prosp ^. lastName
--b = prosp ^. biography
u = fromMaybe "www.google.com" (prosp ^. application)
f' = T.concat ["=HYPERLINK(\"", u, "\", \"", f, "\")"]
l' = T.concat ["=HYPERLINK(\"", u, "\", \"", l, "\")"]
fv = extendedValue & evFormulaValue .~ Just f'
lv = extendedValue & evFormulaValue .~ Just l'
filled = [] -- [cellData & cdUserEnteredValue .~ Just fv & cdNote .~ b, cellData & cdUserEnteredValue .~ Just lv & cdNote .~ b]
rest = replicate (nCols - (length filled)) (cellData & cdNote ?~ "")
availabilityRow :: Int -> Int -> TimeSpan -> RowData
availabilityRow row nCols slot = rowData & rdValues .~ (filled ++ rest)
where
f = slot ^. start
l = slot ^. end
f' = (pack . formatTime defaultTimeLocale "%a %R") f
l' = (pack . formatTime defaultTimeLocale "%a %R") l
fv = extendedValue & evStringValue .~ Just f'
lv = extendedValue & evStringValue .~ Just l'
filled = [cellData & cdUserEnteredValue .~ Just fv, cellData & cdUserEnteredValue .~ Just lv]
rest = replicate (nCols - (length filled)) cellData
writeGrid sid tid xs = do
undefined
createAvailabilityForm name sid facs slots = do
env <- newEnv <&> (envScopes .~ spreadsheetsScope)
let nRows = fromIntegral $ length slots + 2
nCols = fromIntegral $ length facs + 2
tid <- addTab sid name nRows nCols 3
let range = gridRange & grStartRowIndex .~ Just 0 & grEndRowIndex .~ Just nRows & grStartColumnIndex .~ Just 0 & grEndColumnIndex .~ Just nCols & grSheetId .~ Just tid
header = map (\x -> x & cdTextFormatRuns .~ [headerFormat]) ([cellData & cdUserEnteredValue ?~ (extendedValue & evStringValue ?~ "Start"), cellData & cdUserEnteredValue ?~ (extendedValue & evStringValue ?~ "End")] ++ [cellData & cdUserEnteredValue .~ Just (extendedValue & evStringValue .~ Just _lastName) | Person{..} <- facs])
rows = [rowData & rdValues .~ header] ++ [availabilityRow i (fromIntegral nCols) p | (i, p) <- zip [1..] slots]
help = [rowData & rdValues .~ [cellData & cdUserEnteredValue ?~ schedHelp]]
req = updateCellsRequest & updRange .~ Just range & updFields .~ (Just . GFieldMask) "*" & updRows .~ rows
req' = updateCellsRequest & updRange ?~ (gridRange & grStartRowIndex ?~ (nRows - 1) & grEndRowIndex ?~ nRows & grStartColumnIndex ?~ 0 & grEndColumnIndex ?~ 1 & grSheetId ?~ tid) & updRows .~ help & updFields ?~ GFieldMask "*"
fmts = [addConditionalFormatRuleRequest & acfrrRule ?~ fmt | fmt <- availabilityEntryFormat tid 1 2 nRows nCols]
fmtReqs = [reqAddConditionalFormatRule ?~ fmtReq $ request' | fmtReq <- fmts]
resize = autoResizeDimensionsRequest & ardrDimensions ?~ (dimensionRange & drDimension ?~ DRDColumns & drSheetId ?~ tid & drStartIndex ?~ 0 & drEndIndex ?~ nCols)
try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [(reqUpdateCells .~ Just req $ request')] ++ fmtReqs ++ [reqAutoResizeDimensions .~ Just resize $ request']$ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [(reqUpdateCells .~ Just req' $ request')] $ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
return ()
writeSchedule :: Text -> Maybe Int32 -> Maybe Int32 -> State -> [TimeSpan] -> IO ()
writeSchedule sid tidF tidP state ts = do
let ims = fromMaybe Map.empty (state ^. individualMeetings)
--gms = fromMaybe Map.empty (state ^. groupMeetings)
case tidF of
Nothing -> return ()
Just tid -> deleteTab sid tid
let nRows = fromIntegral $ 1 + (length $ state ^. faculty)
nCols = fromIntegral $ 2 + (length ts)
env <- newEnv <&> (envScopes .~ spreadsheetsScope)
tidF' <- addTab sid "Interviewer Schedule" nRows nCols 3
let range = gridRange & grStartRowIndex .~ Just 0 & grEndRowIndex .~ Just nRows & grStartColumnIndex .~ Just 0 & grEndColumnIndex .~ Just nCols & grSheetId .~ Just tidF'
header = map (\x -> x & cdTextFormatRuns .~ [headerFormat]) ([cellData & cdUserEnteredValue ?~ (extendedValue & evStringValue ?~ "First"), cellData & cdUserEnteredValue ?~ (extendedValue & evStringValue ?~ "Last")] ++ [cellData & cdUserEnteredValue .~ Just (extendedValue & evStringValue .~ Just (pack . formatTime defaultTimeLocale "%a %R" $ t ^. start)) | t <- ts])
rows = [rowData & rdValues .~ header] ++ [scheduleFacultyRow ts n v | (n, v) <- (Map.toList . fromJust) (state ^. individualMeetings)]
fmtReqs = []
req = updateCellsRequest & updRange .~ Just range & updFields .~ (Just . GFieldMask) "*" & updRows .~ rows
try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [(reqUpdateCells .~ Just req $ request')] ++ fmtReqs $ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
case tidP of
Nothing -> return ()
Just tid -> deleteTab sid tid
let nRows = fromIntegral $ 1 + (length $ state ^. prospects)
nCols = fromIntegral $ 2 + (length ts)
env <- newEnv <&> (envScopes .~ spreadsheetsScope)
tidP' <- addTab sid "Prospect Schedule" nRows nCols 2
let range = gridRange & grStartRowIndex .~ Just 0 & grEndRowIndex .~ Just nRows & grStartColumnIndex .~ Just 0 & grEndColumnIndex .~ Just nCols & grSheetId .~ Just tidP'
header = map (\x -> x & cdTextFormatRuns .~ [headerFormat]) ([cellData & cdUserEnteredValue ?~ (extendedValue & evStringValue ?~ "First"), cellData & cdUserEnteredValue ?~ (extendedValue & evStringValue ?~ "Last")] ++ [cellData & cdUserEnteredValue .~ Just (extendedValue & evStringValue .~ Just (pack . formatTime defaultTimeLocale "%a %R" $ t ^. start)) | t <- ts])
rows = [rowData & rdValues .~ header] ++ [scheduleProspectRow ts (p ^. fullName) (fromMaybe Map.empty $ state ^. individualMeetings)| p <- (state ^. prospects)]
fmtReqs = []
req = updateCellsRequest & updRange .~ Just range & updFields .~ (Just . GFieldMask) "*" & updRows .~ rows
x <- try . runResourceT . runGoogle env $ send (spreadsheetsBatchUpdate sid (busrRequests .~ [(reqUpdateCells .~ Just req $ request')] ++ fmtReqs $ batchUpdateSpreadsheetRequest)) :: IO (Either Error BatchUpdateSpreadsheetResponse)
--print x
return ()
scheduleFacultyRow :: [TimeSpan] -> (Text, Text) -> Map (Text, Text) [TimeSpan] -> RowData
scheduleFacultyRow ts (first, last) vs = rowData & rdValues .~ ([first', last'] ++ rest')
where
fv = extendedValue & evStringValue .~ Just first
lv = extendedValue & evStringValue .~ Just last
first' = cellData & cdUserEnteredValue .~ Just fv
last' = cellData & cdUserEnteredValue .~ Just lv
restMap = Map.fromList $ concat [[(x, pack $ [head $ unpack f] ++ " " ++ (unpack l)) | x <- xs] | ((f, l), xs) <- Map.toList vs]
--gRestMap = Map.fromList $ concat [[(x, T.intercalate "/" (map (id . snd) ns)) | x <- xs] | (ns, xs) <- Map.toList gvs]
--rest = [extendedValue & evStringValue ?~ (Map.findWithDefault (if elem t knownEvents then "EVENT" else (Map.findWithDefault "" t gRestMap)) t restMap) | t <- ts]
rest = [extendedValue & evStringValue ?~ (Map.findWithDefault "" t restMap) | t <- ts]
rest' = [cellData & cdUserEnteredValue ?~ v | v <- rest]
scheduleProspectRow :: [TimeSpan] -> (Text, Text) -> Map (Text, Text) (Map (Text, Text) [TimeSpan]) -> RowData
scheduleProspectRow ts (first, last) vs = rowData & rdValues .~ ([first', last'] ++ rest')
where
fv = extendedValue & evStringValue .~ Just first
lv = extendedValue & evStringValue .~ Just last
first' = cellData & cdUserEnteredValue .~ Just fv
last' = cellData & cdUserEnteredValue .~ Just lv
rel = concat [[(ss, Text.intercalate " " [Text.take 1 $ fst f, snd f]) | (p, ss) <- Map.toList ps, p == (first, last)] | (f, ps) <- Map.toList vs]
--rel' = (Map.fromList . map (\(a, b) -> (b, a)) . Map.toList) $
rel' = Map.fromListWith (\a b -> Text.intercalate ", " [a, b]) rel
restMap = Map.fromList $ concat [[(t, s) | t <- ts] | (ts, s) <- Map.toList rel']
--vs''' = Map.fromListWith (\a b -> a) [(v, k) | (k, v) <- Map.toList $ Map.mapKeys (\(a, b) -> Text.intercalate " " [a,b]) vs]
--restMap = Map.fromList $ concat [ concat [[(t, fs) | t <- vs''] | ((pf, pl), vs'') <- Map.toList vs', (pf, pl) == (first, last)] | (fs, vs') <- Map.toList vs''']
--restMap = Map.fromList $ concat [ concat [[(t, pack $ [head $ unpack ff] ++ " " ++ (unpack fl)) | t <- vs''] | ((pf, pl), vs'') <- Map.toList vs', (pf, pl) == (first, last)] | ((ff, fl), vs') <- Map.toList vs]
--gRestMap = Map.fromList $ concat [ concat [[(t, pack $ [head $ unpack ff] ++ " " ++ (unpack fl)) | t <- vs''] | (pns, vs'') <- Map.toList vs', (first, last) `elem` pns] | ((ff, fl), vs') <- Map.toList gvs]
--gRestMap = Map.fromList []
-- restMap = Map.fromList $ concat [[(x, pack $ [head $ unpack f] ++ " " ++ (unpack l)) | x <- xs] | ((f, l), xs) <- Map.toList vs]
rest = [extendedValue & evStringValue ?~ (Map.findWithDefault "" t restMap) | t <- ts]
rest' = [cellData & cdUserEnteredValue ?~ v | v <- rest]
-- createSchedule sid facs prosps slots = do
-- env <- newEnv <&> (envScopes .~ spreadsheetsScope)
-- addTab sid "Schedule" (fromIntegral $ length prosps + 1) (fromIntegral $ length facs + 2)
-- let cols = ['A'..'Z']
-- facRange = pack $ printf "'Schedule'!A1:%v1" (cols !! (length facs + 2))
-- slotRange = pack $ printf "'Schedule'!A2:B%d" (length slots + 1)
-- facVals = ( vrMajorDimension .~ Just VRMDRows $ vrValues .~ [[String "Start", String "End"] ++ [String _lastName | Person{..} <- facs]] $ vrRange .~ Just facRange $ valueRange)
-- slotVals = ( vrMajorDimension .~ Just VRMDRows $ vrValues .~ [[String $ (pack . formatTime defaultTimeLocale "%a %R") (_start s), String $ (pack . formatTime defaultTimeLocale "%a %R") (_end s)] | s <- slots] $ vrRange .~ Just slotRange $ valueRange)
-- runResourceT . runGoogle env $ send (svuValueInputOption .~ Just "USER_ENTERED" $ spreadsheetsValuesUpdate sid facVals facRange)
-- runResourceT . runGoogle env $ send (svuValueInputOption .~ Just "USER_ENTERED" $ spreadsheetsValuesUpdate sid slotVals slotRange)
readSpreadsheet sid = do
env <- newEnv <&> (envScopes .~ spreadsheetsScope)
runResourceT . runGoogle env $ send (sgIncludeGridData .~ Just True $ spreadsheetsGet sid)
--readValues sid spec = do
-- env <- newEnv <&> (envScopes .~ spreadsheetsScope)
-- runResourceT . runGoogle env $ send (spreadsheetsValuesGet sid spec)
-- writeValues sid sheet startRow startColumn endRow endColumn values = do
-- env <- newEnv <&> (envScopes .~ spreadsheetsScope)
-- let cols = ['A'..'Z']
-- return ()
sheetName :: Sheet -> Maybe Text
sheetName s = join $ s ^. sProperties <&> view sTitle
sheetId :: Sheet -> Maybe Int32
sheetId s = join $ s ^. sProperties <&> view sSheetId
rowToPerson :: [Text] -> RowData -> Maybe Person
rowToPerson cols rd = do
let cells = rd ^. rdValues
cells' = map (\x -> (x ^. cdFormattedValue, x^.cdHyperlink)) cells
feats = Map.fromList $ zip cols cells'
first = (fromJust . fst) $ feats Map.! "First"
last = (fromJust .fst) $ feats Map.! "Last"
minMtg = 1 -- (read . unpack) $ fromMaybe "3" (fst $ feats Map.! "Minimum meetings") :: Int
maxMtg = 2 -- (read . unpack) $ fromMaybe "3" (fst $ feats Map.! "Maximum meetings") :: Int
maxSz = 2 -- (read . unpack) $ fromMaybe "2" (fst $ feats Map.! "Maximum size") :: Int
reqOnly = True -- (fromMaybe "FALSE" (fst $ feats Map.! "Requested only")) == "TRUE"
e = (fst . fromMaybe (Nothing, undefined)) $ "Email" `Map.lookup` feats
z = (fst . fromMaybe (Nothing, undefined)) $ "Zoom" `Map.lookup` feats
g = Just "3" -- (fst . fromJust) $ "Group" `Map.lookup` feats
a = Just "4" -- (snd . fromMaybe (Nothing, Just "")) $ "App" `Map.lookup` feats
return $ simplePerson first last & email .~ e & zoom .~ z & minMeetings .~ minMtg & maxMeetings .~ maxMtg & requestedOnly .~ reqOnly & maxMeetingSize .~ maxSz & group .~ g & application .~ a
rowToSlot :: RowData -> Maybe TimeSpan
rowToSlot rd = do
let time:_ = rd ^. rdValues
time' <- time ^. cdFormattedValue
let startTime = parseTimeOrError True defaultTimeLocale "" (unpack time')
return $ TimeSpan startTime startTime
ssheet :: Spreadsheet -> Text -> Maybe Sheet
ssheet ssheet name = case ss of
[] -> Nothing
s:_ -> Just s
where
ss = [s | s <- ssheet ^. sprSheets, sheetName s == Just name]
sheetLookup :: Lens' Spreadsheet (Map Text Int32)
sheetLookup = lens getter (\x y -> undefined)
where
getter ss = Map.fromList $ map (\s -> ((fromJust . sheetName) s, (fromJust . sheetId) s)) (ss ^. sprSheets)
rowToGroupPreference row = simplePreference & intervieweeParticipants .~ (Set.fromList [prosp]) & interviewerParticipants .~ (Set.fromList ints) & priority .~ 2
where
first:last:interviewers = rowToStrings row
prosp = (first, last)
ints = map ((\(a, b) -> (a, Text.strip b)) . Text.breakOn " ") (filter (\x -> x /= "") interviewers)
getGroupPrefs :: Text -> Spreadsheet -> [Preference]
getGroupPrefs sn ss = map rowToGroupPreference rows
where
(sheet:_) = [s | s <- ss ^. sprSheets, sheetName s == Just sn]
(ds:_) = sheet ^. sData
rows = ds ^. gdRowData . _tail
rowToIndividualPreferences cols row = catMaybes $ map (cellToIndividualPreference (first, last)) (zip cols entries)
where
_:_:first:last:entries = rowToStrings row
cellToIndividualPreference interviewee (interviewer, value) = case value of "" -> Nothing
"x" -> Just $ simplePreference & intervieweeParticipants .~ (Set.fromList [interviewee]) & interviewerParticipants .~ (Set.fromList [interviewer]) & priority .~ 1 & required .~ True
_ -> Just $ simplePreference & intervieweeParticipants .~ (Set.fromList [interviewee]) & interviewerParticipants .~ (Set.fromList [interviewer]) & priority .~ 0 & required .~ False
getIndividualPrefs :: Text -> Spreadsheet -> [Preference]
getIndividualPrefs sn ss = concat (map (rowToIndividualPreferences cols) rows)
where
(sheet:_) = [s | s <- ss ^. sprSheets, sheetName s == Just sn]
(ds:_) = sheet ^. sData
firsts = (drop 4 . rowToStrings . head) $ ds ^. gdRowData
lasts = (drop 4 . rowToStrings . head . drop 1) $ ds ^. gdRowData
cols = zip firsts lasts
rows = (drop 2) $ ds ^. gdRowData
getPeople :: Text -> Spreadsheet -> [Person]
getPeople sn ss = catMaybes $ map (rowToPerson cols) rows
where
(sheet:_) = [s | s <- ss ^. sprSheets, sheetName s == Just sn]
(ds:_) = sheet ^. sData
cols = rowToStrings $ head $ ds ^. gdRowData
rows = ds ^. gdRowData . _tail
rowToStrings :: RowData -> [Text]
rowToStrings r = map (\x -> fromMaybe "" (x ^. cdFormattedValue)) (r ^. rdValues)
mon = parseTimeOrError True defaultTimeLocale "%Y-%-m-%-d" "2021-03-08" :: Day
tue = parseTimeOrError True defaultTimeLocale "%Y-%-m-%-d" "2021-03-09" :: Day
wed = parseTimeOrError True defaultTimeLocale "%Y-%-m-%-d" "2021-03-10" :: Day
thu = parseTimeOrError True defaultTimeLocale "%Y-%-m-%-d" "2021-03-11" :: Day
fri = parseTimeOrError True defaultTimeLocale "%Y-%-m-%-d" "2021-03-12" :: Day
--sat = parseTimeOrError True defaultTimeLocale "%Y-%-m-%-d" "2021-02-06" :: Day
stringsToSlot :: Text -> Text -> TimeSpan
stringsToSlot s e = TimeSpan start end
where
sd:st:[] = map T.unpack (T.splitOn " " s)
ed:et:[] = map T.unpack (T.splitOn " " e)
d = case sd of "Mon" -> mon
"Tue" -> tue
"Wed" -> wed
"Thu" -> thu
"Fri" -> fri
--"Sat" -> sat
start = UTCTime d (timeOfDayToTime (parseTimeOrError True defaultTimeLocale "%R" st :: TimeOfDay))
end = UTCTime d (timeOfDayToTime (parseTimeOrError True defaultTimeLocale "%R" et :: TimeOfDay))
getFacultySchedule :: Text -> Spreadsheet -> Person -> Map Text (Text, Text) -> Map (Text, Text) [TimeSpan]
getFacultySchedule tabName ss fac plu = Map.map fixTimes scheds
where
rows = [r | r <- map rowToStrings $ (head $ ss ^. tab tabName . sData) ^. gdRowData]
(_:_:slots) = head rows
row = drop 2 $ head [r | r@(f:l:_) <- rows, (f, l) == (fac^.firstName, fac^.lastName)]
scheds = Map.fromListWith (++) [(plu Map.! n, [ts]) | (n, ts) <- zip row slots, n /= ""]
fixTimes times = times'
where
times' = sort $ map fixTime times
--s = minimum times'
--e = maximum times'
fixTime time = TimeSpan s e
where
sd = Text.take 3 time
d = case sd of "Mon" -> mon
"Tue" -> tue
"Wed" -> wed
"Thu" -> thu
"Fri" -> fri
--"Sat" -> sat
t = timeOfDayToTime (parseTimeOrError True defaultTimeLocale "%R" (Text.unpack $ Text.drop 4 time) :: TimeOfDay)
s = UTCTime d t
e = addUTCTime (secondsToNominalDiffTime (60*60)) s
-- getAvailability :: Text -> Spreadsheet -> [Person] -> [Person]
-- getAvailability tabName ss fac = fac'
-- where
-- (_:_:names):xs = map rowToStrings $ (head $ ss ^. tab tabName . sData) ^. gdRowData
-- avails = concat $ map (\(s:e:xs') -> [(stringsToSlot s e, n, v) | (n, v) <- zip names xs', v /= ""]) (init xs)
-- fac' = map (\f -> f & availability ?~ [t | (t, n, v) <- avails, f ^. lastName == n]) fac
getAvailability :: Text -> Spreadsheet -> [Person] -> [Person]
getAvailability tabName ss fac = fac --fac'
where
(_:_:firsts):(_:_:lasts):xs = map rowToStrings $ (head $ ss ^. tab tabName . sData) ^. gdRowData
names = zip firsts lasts
avails = concat $ map (\(s:e:xs') -> [(stringsToSlot s e, n, v) | (n, v) <- zip names xs', v /= ""]) (init xs)
fac' = map (\f -> f & availability ?~ [t | (t, (first, last), v) <- avails, first==f ^. firstName && last==f ^. lastName]) fac
defaultSlots gran = do
let dates = ["2021-03-" ++ (show d) | d <- [8,9,10,11,12]]
slots <- sequence $ [stringsToSlots d d "08:00" "19:00" gran | d <- dates]
return $ concat slots
getPreferences :: Spreadsheet -> [Person] -> [Person] -> [Person]
getPreferences ss prosps fac = fac'
where
(_:_:_:names):xs = map rowToStrings $ (head $ ss ^. tab "Individual Preferences" . sData) ^. gdRowData
prefs = concat $ map (\(_:_:l:xs'') -> [(l, n, 1 :: Int) | (n, v) <- zip names xs'', v == "x"]) (init xs)
plu = Map.fromList [(p ^. lastName, p) | p <- prosps]
--fac' = map (\f -> f & preferences ?~ Map.fromList [((pf, pl), v) | (pf, pl, n, v) <- prefs, f ^. lastName == n]) fac
fac' = map (\f -> f & preferences ?~ Map.fromList [(plu Map.! pl, v) | (pl, n, v) <- prefs, f ^. lastName == n]) fac