-
Notifications
You must be signed in to change notification settings - Fork 2
/
model.go
179 lines (163 loc) · 4.65 KB
/
model.go
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
package main
import (
"sync"
"time"
)
// This file contains the logic for our application.
// See persistence.go for disk (JSON) storage.
// A QueueEntry represents a ticket in the Queue. A user can have multiple
// tickets, but only one has WasServed set to true. We store all tickets
// so that we can compute statistics later by parsing the persistence.json file.
type QueueEntry struct {
CSid string
Name string
TaskInfo string
JoinedAt time.Time
WasServed bool
ServedAt time.Time
}
// Queue is the underlying thread-safe data structure (mutex + queue).
type Queue struct {
Mutex sync.Mutex // To handle concurrency, prevents multiple users from touching the DS.
Entries []QueueEntry // Contains the actual tickets.
IsOpen bool // Whether the queue is open or closed.
}
// Main in-memory data structure.
var queue = Queue{Entries: []QueueEntry{}, IsOpen: false}
// JoinQueue adds the student with name and CSid to the queue.
// Returns how many students are ahead of the new student in the queue,
// and the estimated wait time in seconds.
// If the student has requested help more than MaxNumTimesHelped,
// returns how many times the students has asked for help already, and -1
func JoinQueue(name string, CSid string, taskInfo string) (uint, int) {
timesHelped := NumTimesHelped(CSid)
if timesHelped < config.MaxNumTimesHelped {
entry := QueueEntry{CSid, name, taskInfo, time.Now(), false, time.Now()}
queue.Mutex.Lock()
// How many un-served students joined before me?
var rsf uint = 0
for _, entry := range queue.Entries {
if !entry.WasServed {
rsf++
}
}
queue.Entries = append(queue.Entries, entry)
UpdateDiskCopy()
queue.Mutex.Unlock()
return rsf, int(EstimatedWaitTime())
}
return timesHelped, -1
}
// HasJoinedQueue returns true if the user with given CSid has joined the queue
// and has not been served yet.
func HasJoinedQueue(CSid string) bool {
queue.Mutex.Lock()
for _, entry := range queue.Entries {
if entry.CSid == CSid && !entry.WasServed {
queue.Mutex.Unlock()
return true
}
}
queue.Mutex.Unlock()
return false
}
// ServeStudent marks the student with given CSid as served.
func ServeStudent(CSid string) {
queue.Mutex.Lock()
for i, entry := range queue.Entries {
if entry.CSid == CSid {
if !entry.WasServed {
queue.Entries[i].ServedAt = time.Now()
}
queue.Entries[i].WasServed = true
}
}
UpdateDiskCopy()
queue.Mutex.Unlock()
}
// UnservedEntries returns all tickets that have not been served yet.
func UnservedEntries() []QueueEntry {
var acc []QueueEntry
queue.Mutex.Lock()
for _, entry := range queue.Entries {
if !entry.WasServed {
acc = append(acc, entry)
}
}
queue.Mutex.Unlock()
return acc
}
// NumTimesHelped returns the number of times the given CSid was helped in the last 24 hours.
func NumTimesHelped(CSid string) uint {
var acc uint = 0
queue.Mutex.Lock()
for _, entry := range queue.Entries {
if entry.CSid == CSid && entry.WasServed && entry.ServedAt.After(time.Now().AddDate(0, 0, -1)) {
acc++
}
}
queue.Mutex.Unlock()
return acc
}
// EstimatedWaitTime returns the estimated wait time in seconds for a students that joins the
// queue right now, based on served entries from the past 30 minutes.
func EstimatedWaitTime() float64 {
thirtyMinsAgo := time.Now().Add(-30 * time.Minute)
var acc time.Duration
count := 0
queue.Mutex.Lock()
for _, entry := range queue.Entries {
if entry.WasServed && entry.ServedAt.After(thirtyMinsAgo) {
waitTime := entry.ServedAt.Sub(entry.JoinedAt)
acc += waitTime
count++
}
}
queue.Mutex.Unlock()
if count == 0 {
// Nobody was served yet, no estimate available.
return 0
}
return acc.Seconds() / float64(count)
}
// QueuePositionForCSID returns whether the given CSid is waiting in the queue,
// and their position.
func QueuePositionForCSID(CSid string) (bool, uint) {
entries := UnservedEntries()
var acc uint = 0
for _, entry := range entries {
if entry.CSid == CSid {
return true, acc
}
acc++
}
return false, 0
}
// Returns the total number of times students received help
// throughout the term.
func TotalNumStudentsHelped() uint {
queue.Mutex.Lock()
tot := uint(len(queue.Entries))
queue.Mutex.Unlock()
return tot
}
// Returns whether the queue is open.
func IsQueueOpen() bool {
queue.Mutex.Lock()
result := queue.IsOpen
queue.Mutex.Unlock()
return result
}
// Opens the queue, letting students join it.
func OpenQueue() {
queue.Mutex.Lock()
queue.IsOpen = true
queue.Mutex.Unlock()
}
// Closes the queue, preventing students from joining.
// Closing the queue does not kick existing students out.
func CloseQueue() {
queue.Mutex.Lock()
queue.IsOpen = false
queue.Mutex.Unlock()
}