-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathHospitalScheduler.hs
More file actions
232 lines (215 loc) · 11.5 KB
/
HospitalScheduler.hs
File metadata and controls
232 lines (215 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
import Data.List (minimumBy, sortBy)
import Data.Ord (comparing)
import System.IO
run :: IO String
run =
do
hSetBuffering stdin LineBuffering -- this allows the backspace key to work on Mac's terminal
openingTerminalText
putStrLn("Enter 'yes' to begin loading today's patients schedule:")
ans <- getLine
if (ans `elem` ["y","yes","YES","Yes"])
then do
putStrLn("")
putStrLn("Enter the name of the .csv file which contains your patients list (including .csv):")
csvFileName <- getLine
putStrLn("")
file <- readCsv (csvFileName)
if (file == [[""]]) then return "ERROR: Input .csv file is empty."
else do
let inputPatientsList = (readCsvToPatients file)
let inputPatientsAsTuples = patientsToTuples inputPatientsList
let inputPatientsForPrint = tuplesToPrintableString inputPatientsAsTuples
putStrLn("**LOADED FILE** " ++ csvFileName ++ " contains the following patients:")
putStrLn("---------------------------------------------")
putStrLn(inputPatientsForPrint)
putStrLn("Would you like to add another patient to this list? Enter 'yes' or 'no':")
ans <- getLine
putStrLn("")
if (ans `elem` ["y","yes","YES","Yes"])
then do
putStrLn("Enter the patient's name, appointment start time, and appointment end time (using a 24 hour clock).")
putStrLn("For example: 'John Doe, Thirteen, Fifteen' (not including the '' symbols).")
inputPatientAsString <- getLine
putStrLn("")
putStrLn("")
let inputPatientAsStringList = splitSep (== ',') inputPatientAsString
let newPatient = createPatient inputPatientAsStringList
let newPatientAsListOfPatient = newPatient : []
let newPatientsList = inputPatientsList ++ newPatientAsListOfPatient
let newPatientsAsTuples = patientsToTuples newPatientsList
let newPatientsForPrint = tuplesToPrintableString newPatientsAsTuples
putStrLn("**UPDATED LIST** today's patients list now contains the following patients:")
putStrLn("---------------------------------------------")
putStrLn(newPatientsForPrint)
let sortedPatientsList = sortPatients (sortBy (comparing endTime) newPatientsList)
let sortedTuplesListDr1 = patientsToTuples sortedPatientsList
let sortedTuplesListDr2 = patientsToTuples (sortPatients (removeSeenPatients newPatientsList sortedPatientsList))
let finalPatientsListDr1 = tuplesToPrintableString sortedTuplesListDr1
let finalPatientsListDr2 = tuplesToPrintableString sortedTuplesListDr2
putStrLn("Enter the name for the .txt file in which Hospital Scheduler will save its output (including .txt):")
txtFileName <- getLine
let numOfDrs = show (length (minDrsList newPatientsList))
putStrLn("")
putStrLn("")
putStrLn("***************************************************************")
putStrLn ("NOTE: To see all of today's patients, you would need " ++ numOfDrs ++ " doctors.")
putStrLn("***************************************************************")
printPatients finalPatientsListDr1 finalPatientsListDr2 txtFileName
closingTerminalTextDr1
putStrLn(finalPatientsListDr1)
closingTerminalTextDr2
putStrLn(finalPatientsListDr2)
putStrLn("")
putStrLn("")
return "Process complete."
else do
let sortedPatientsList = sortPatients (sortBy (comparing endTime) inputPatientsList)
let sortedTuplesListDr1 = patientsToTuples sortedPatientsList
let sortedTuplesListDr2 = patientsToTuples (sortPatients (removeSeenPatients inputPatientsList sortedPatientsList))
let finalPatientsListDr1 = tuplesToPrintableString sortedTuplesListDr1
let finalPatientsListDr2 = tuplesToPrintableString sortedTuplesListDr2
putStrLn("Enter the name for the .txt file in which Hospital Scheduler will save its output (including .txt):")
txtFileName <- getLine
let numOfDrs = show (length (minDrsList inputPatientsList))
putStrLn("")
putStrLn("")
putStrLn("***************************************************************")
putStrLn ("NOTE: To see all of today's patients, you would need " ++ numOfDrs ++ " doctors.")
putStrLn("***************************************************************")
printPatients finalPatientsListDr1 finalPatientsListDr2 txtFileName
closingTerminalTextDr1
putStrLn(finalPatientsListDr1)
closingTerminalTextDr2
putStrLn(finalPatientsListDr2)
putStrLn("")
putStrLn("")
return "Process complete."
else return "ERROR: Invalid input. Program closing."
-- data type for the Time (in order)
data Time = Zero | One | Two | Three | Four | Five
| Six | Seven | Eight | Nine | Ten | Eleven
| Twelve | Thirteen | Fourteen | Fifteen
| Sixteen | Seventeen | Eighteen | Nineteen
| Twenty | Twenty_One | Twenty_Two | Twenty_Three
deriving (Ord, Eq, Show, Read)
-- data type for a Patient
data Patient = No_Patient |
Patient { name :: String
, startTime :: Time
, endTime :: Time
} deriving (Ord, Eq, Show)
-- text that is printed on the terminal after the user enters: 'run'
-- welcoming messages for the user
openingTerminalText :: IO ()
openingTerminalText = do
putStrLn("")
putStrLn("************************")
putStrLn("Hospital Scheduler 2019")
putStrLn("************************")
putStrLn("")
putStrLn("")
putStrLn("Hello! Welcome to Hospital Scheduler.")
-- text that is printed on the terminal after the user enters the name for the output file
-- the header and sub-header for Dr 1's patient schedule
closingTerminalTextDr1 :: IO ()
closingTerminalTextDr1 = do
putStrLn("")
putStrLn("")
putStrLn("------------------------------")
putStrLn("1st Doctor's Patient Schedule:")
putStrLn("------------------------------")
putStrLn("NAME, START TIME, END TIME")
-- text that is printed on the terminal after closingTerminalTextDr1 is printed
-- the header and sub-header for Dr 2's patient schedule
closingTerminalTextDr2 :: IO ()
closingTerminalTextDr2 = do
putStrLn("")
putStrLn("")
putStrLn("------------------------------")
putStrLn("2nd Doctor's Patient Schedule:")
putStrLn("------------------------------")
putStrLn("NAME, START TIME, END TIME")
-- returns the minimum number of doctors necessary to see the given list of patients
-- returns the list of all schedules needed to see all patients
minDrsList :: [Patient] -> [[Patient]]
minDrsList [] = []
minDrsList lst = seenPatients : (minDrsList unseenPatients)
where
seenPatients = sortPatients lst
unseenPatients = removeSeenPatients lst seenPatients
-- convert list of name and time tuples tuples into a single string
-- separate each patient onto his/her own line
-- concat each patient's name to his/her startTime, with a comma and a space between
tuplesToPrintableString :: [(String,Time,Time)] -> String
tuplesToPrintableString [] = ""
tuplesToPrintableString lst = concatMap printify lst
where
printify (name, startTime, endTime) = (show name) ++ ", " ++ (show startTime) ++ ", " ++ (show endTime) ++ "\n"
-- print Dr1 and Dr2's schedules to output .txt file
-- .txt file will be named according to the user's input
printPatients :: String -> String -> String -> IO ()
printPatients [] _ txtFileName = writeFile txtFileName "ERROR: no patients in the inputted .csv file."
printPatients lst1 lst2 txtFileName =
writeFile txtFileName textToPrint
where
textToPrint =
"------------------------------" ++
"\n" ++
"1st Doctor's Patient Schedule:" ++
"\n" ++
"------------------------------" ++
"\n" ++
"NAME, START TIME, END TIME" ++
"\n" ++
lst1 ++
"\n" ++
"\n" ++
"------------------------------" ++
"\n" ++
"2nd Doctor's Patient Schedule:" ++
"\n" ++
"------------------------------" ++
"\n" ++
"NAME, START TIME, END TIME" ++
"\n" ++
lst2
-- creates a list of patients from the inputted csv file
readCsvToPatients :: [[String]] -> [Patient]
readCsvToPatients [[]] = []
readCsvToPatients [] = []
readCsvToPatients lst = map createPatient lst
-- creates a patient
createPatient :: [String] -> Patient
createPatient (a:b:c:d) = Patient a (read b :: Time) (read c :: Time)
-- given a list of patients, returns a list of patients sorted in the optimal order
sortPatients :: [Patient] -> [Patient]
sortPatients [] = []
sortPatients (h:t) = min : sortPatients (removeConflicts min (sortBy (comparing startTime) (h:t)))
where min = minimumBy (comparing endTime) (h:t)
-- convert list of patients to list of name and time tuples
patientsToTuples :: [Patient] -> [(String,Time,Time)]
patientsToTuples [] = []
patientsToTuples (h:t) = (name h, startTime h, endTime h) : patientsToTuples t
-- filters the patients already seen from the list of all patients given
-- removeSeenPatients :: allPatients firstList -> newPatientsList
removeSeenPatients :: [Patient] -> [Patient] -> [Patient]
removeSeenPatients [] _ = []
removeSeenPatients allP firstP = filter (\x -> not (x `elem` firstP)) allP
-- removes the patients whose appointments conflict with that of the already chosen patient
removeConflicts :: Patient -> [Patient] -> [Patient]
removeConflicts p [] = []
removeConflicts p (h:t)
| endTime p > startTime h = removeConflicts p t
| otherwise = h : removeConflicts p t
-- credit to David Poole, Homework 3 Question 3
splitSep :: (a -> Bool) -> [a] -> [[a]]
splitSep f [] = [[]]
splitSep f (h:t)
| f h = [] : splitSep f t
| otherwise = ((h:t1):t2) where t1:t2 = splitSep f t
-- credit to David Poole, Homework 3 Question 3
readCsv fileName =
do
file <- readFile fileName
return [splitSep (== ',') line | line <- splitSep (== '\n') file]