-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtask-client.rkt
More file actions
269 lines (230 loc) · 9.43 KB
/
Copy pathtask-client.rkt
File metadata and controls
269 lines (230 loc) · 9.43 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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#lang racket
(require json)
(require racket/tcp)
(require racket/date)
; procedures for manipulating the task list
(provide addTask)
(provide editTask)
(provide deleteTask)
(provide overrideTaskList)
(provide updateTaskList)
; procedures for accessing the task objects
(provide getTodaysTasks)
(provide readTaskList)
(provide getTaskWithID)
; procedures for changing settings
(provide changeWorkHours)
; procedures for creating and displaying due dates
(provide dateString)
(provide simpleMakeDate)
(provide getDay)
(provide getMonth)
(provide getYear)
; file path to store json data in
(define taskFile "tasks")
; list of all fields in a task object
(define taskFields (list
'name
'due
'ID
'priority
'duration))
(define emptyTask
(make-immutable-hasheq
(map (lambda (key)
(cons key ""))
taskFields)))
; read the task list from the json file
(define (readTaskList)
(call-with-input-file taskFile read-json))
; Display the entire task list
(define (displayTasks)
(displayTaskList (hash-ref (readTaskList) 'tasks)))
(define (displayTodaysTasks)
(displayTaskList (getTodaysTasks)))
; Display the given task list
(define (displayTaskList list)
(if (null? list)
(void)
(begin
(writeTaskObject (car list))
(displayTaskList (cdr list)))))
; Find a task by ID and change the given fields to their values.
; the fields argument is a list of (key, value) pairs to be updated to the given values
(define (editTask id fields)
(let ([oldTask (getTaskWithID id)])
(begin
(deleteTask id) ;delete old version of the task
(let ([taskList (readTaskList)]
[out (open-output-file taskFile #:exists 'truncate)])
(begin
(write-json ;add new version of task to task list
(makeTaskList
(cons (changeTaskObject oldTask fields) (hash-ref taskList 'tasks))
(hash-ref taskList 'nextID)
(hash-ref taskList 'workHours))
out)
(close-output-port out))))))
; delete a task with a given ID
(define (deleteTask id)
(let ([taskList (readTaskList)]
[out (open-output-file taskFile #:exists 'truncate)])
(begin
(write-json
(makeTaskList
(filter (lambda (t) (not (taskID? t id))) (hash-ref taskList 'tasks))
(hash-ref taskList 'nextID)
(hash-ref taskList 'workHours))
out)
(close-output-port out))))
(define (getTaskWithID id)
(let ([taskList (readTaskList)])
(car (filter (lambda (t) (taskID? t id)) (hash-ref taskList 'tasks)))))
; returns true if a task has the same ID as the given ID
(define (taskID? task id)
(= id (hash-ref task 'ID)))
; Display a single Task object
(define (writeTaskObject Task)
(begin
(write 'Task: )
(write (hash-ref Task 'name))
(write ', Due: )
(let ([due (hash-ref Task 'due)])
(if (equal? "" due)
(write "")
(write (dateString due))))
(write ', Duration: )
(write (hash-ref Task 'duration))
(write 'hours)
(write ', Priority: )
(writeln (hash-ref Task 'priority))))
; Creates a date object without caring about time, day of the week, or day of the year, and auto-filling some fields
(define (simpleMakeDate day month year)
(date->seconds (make-date 0 0 0 day month year 0 0 (date-dst? (current-date)) (date-time-zone-offset (current-date)))))
; pulls string to display a date
(define (dateString seconds)
(date->string (seconds->date seconds)))
; extracts day from a unix timestamp
(define (getDay seconds)
(date-day (seconds->date seconds)))
; extracts month from unix timestamp
(define (getMonth seconds)
(date-month (seconds->date seconds)))
; extracts year from unix timestamp
(define (getYear seconds)
(date-year (seconds->date seconds)))
; change the given fields in a task object to the given values
(define (changeTaskObject task fields)
(make-immutable-hasheq
(map (lambda (key)
(let ([field (filter (lambda (k) (equal? (car k) key)) fields)])
(if (null? field) ; if the field is being changed, take the new one. else use the old one.
(cons key (hash-ref task key))
(car field))))
taskFields)))
; Create a new task object
(define (makeTask fields)
(changeTaskObject emptyTask fields))
; Create a new task list object
(define (makeTaskList taskPairs id workHours)
(make-immutable-hasheq
(list
(cons 'nextID id)
(cons 'workHours workHours)
(cons 'tasks taskPairs))))
; change desired working hours of a task list
(define (changeWorkHours hours)
(let ([taskList (readTaskList)]
[out (open-output-file taskFile #:exists 'truncate)])
(begin
(write-json (makeTaskList (hash-ref taskList 'tasks) (hash-ref taskList 'nextID) hours) out)
(close-output-port out))))
; Create a new task with specified fields and add it to the task list. Write result in json file.
; the id can either be a numerical ID, or the 'auto symbol, which will automatically generate an id based on the task list
(define (addTask id fields)
(let ([taskList (readTaskList)]
[out (open-output-file taskFile #:exists 'truncate)])
(begin
(write-json
(makeTaskList
(cons ; add new task to list of existing tasks
(let ([filteredFields (filter (lambda (x) (not (equal? (car x) 'ID))) fields)])
(makeTask
(cons
(cons 'ID (if (equal? id 'auto) ; if an ID is provided, use it. Otherwise generate automatically
(hash-ref taskList 'nextID)
id))
fields)))
(hash-ref taskList 'tasks))
(if (equal? id 'auto) ; if we auto-generated the task ID, increment the 'next ID' value on the list object
(+ (hash-ref taskList 'nextID) 1)
(hash-ref taskList 'nextID))
(hash-ref taskList 'workHours))
out)
(close-output-port out))))
; Create a new empty task list
(define (overrideTaskList)
(let ([out (open-output-file taskFile #:exists 'truncate)])
(begin
(write-json (makeTaskList '() 0 8) out)
(close-output-port out))))
; sort by due date, grab tasks until duration totals to certain value
; once we add tags, we can filter our certain tags pretty trivially before sorting
(define (getTodaysTasks)
(let ([taskList (readTaskList)])
(reverse (getTasksForNHours (hash-ref taskList 'workHours)
(sortByPriorityDue (hash-ref taskList 'tasks))))))
(define (sortByPriorityDue tasks)
(append (sort (filter (lambda (x) (equal? "very high" (hash-ref x 'priority))) tasks) < #:key (lambda (x) (hash-ref x 'due)))
(sort (filter (lambda (x) (equal? "high" (hash-ref x 'priority))) tasks) < #:key (lambda (x) (hash-ref x 'due)))
(sort (filter (lambda (x) (equal? "medium" (hash-ref x 'priority))) tasks) < #:key (lambda (x) (hash-ref x 'due)))
(sort (filter (lambda (x) (equal? "low" (hash-ref x 'priority))) tasks) < #:key (lambda (x) (hash-ref x 'due)))
(sort (filter (lambda (x) (equal? "very low" (hash-ref x 'priority))) tasks) < #:key (lambda (x) (hash-ref x 'due)))))
; Extract tasks until total duration hours reaches/exceeds given value. Skip tasks with no duration
(define (getTasksForNHours hours tasks)
(define (iter desiredHours currentHours allTasks newTasks)
(if (or (< desiredHours currentHours) (null? allTasks))
newTasks
(iter desiredHours
(+ currentHours
(hash-ref (car allTasks) 'duration))
(cdr allTasks)
(cons (car allTasks) newTasks))))
(let ([durationTasks (filter (lambda (x) (number? (hash-ref x 'duration))) tasks)])
(iter hours 0 durationTasks '())))
; Updates tasks in a task list
(define (updateTaskList tasks)
(let ([hours (hash-ref (readTaskList) 'workHours)])
(begin
(overrideTaskList)
(map (lambda (x) (addTask 'auto x)) tasks)
(changeWorkHours hours))))
; Code for testing
(define (tests)
(begin (overrideTaskList)
(addTask 'auto (list (cons 'name "OPL Exploration 1") (cons 'due (simpleMakeDate 12 3 2017)) (cons 'priority "high") (cons 'duration 3)))
(addTask 'auto (list (cons 'name "OPL Partner Declarations") (cons 'due (simpleMakeDate 19 3 2017)) (cons 'priority "low") (cons 'duration 4)))
(addTask 'auto (list (cons 'name "OPL Exploration 2") (cons 'due (simpleMakeDate 26 3 2017)) (cons 'priority "medium") (cons 'duration 2)))
(addTask 'auto (list (cons 'name "Prepare OPL Final Presentation") (cons 'due (simpleMakeDate 28 4 2017)) (cons 'priority "high") (cons 'duration 5)))
(editTask 1 (list (cons 'priority "medium") (cons 'duration 3)))
(displayTodaysTasks)))
; Connects to the uml cs server and defines in and out and input and output ports
(define-values (in out) (tcp-connect "localhost" 8080))
; Procedure for sending current tasks to server
(define (sync-up)
(write-json (readTaskList) out)
(flush-output out))
; Procedure for replacing current tasks with those on the server
(define (sync-down-override)
(write-json (string->jsexpr "{\"sync-down-override\":0}") out)
(flush-output out)
(overrideTaskList)
(updateTaskList (map (lambda (n)(hash->list n)) (hash-ref (read-json in) 'tasks))))
(define (clear)
(write-json (string->jsexpr "{\"clear\":0}") out)
(flush-output out))
(define (quit)
(write-json (string->jsexpr "{\"quit\":0}") out)
(flush-output out)
(close-input-port in)
(close-output-port out))