/
attendance.js
348 lines (318 loc) · 12.4 KB
/
attendance.js
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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
'use strict'
const csv = require('fast-csv')
const moment = require('moment')
const dateService = require('../services/date.service')
const hdfValidator = require('../lib/validator/hdf-validator')
const hdfConfirmValidator = require('../lib/validator/hdf-confirm-validator')
const headteacherDeclarationService = require('../services/headteacher-declaration.service')
const pupilDataService = require('../services/data-access/pupil.data.service')
const pupilPresenter = require('../helpers/pupil-presenter')
const hdfPresenter = require('../helpers/hdf-presenter')
const schoolDataService = require('../services/data-access/school.data.service')
const checkWindowV2Service = require('../services/check-window-v2.service')
const scoreService = require('../services/score.service')
const businessAvailabilityService = require('../services/business-availability.service')
const ValidationError = require('../lib/validation-error')
const attendanceCodeService = require('../services/attendance.service')
const controller = {}
controller.getResults = async (req, res, next) => {
res.locals.pageTitle = 'Results'
const checkWindowData = await checkWindowV2Service.getActiveCheckWindow()
const pupils = await pupilDataService.sqlFindPupilsBySchoolId(req.user.schoolId)
const school = await schoolDataService.sqlFindOneById(req.user.schoolId)
let pupilsFormatted = await Promise.all(pupils.map(async (p) => {
const fullName = `${p.foreName} ${p.lastName}`
const score = await scoreService.getScorePercentage(p.id)
const hasScore = (score !== undefined)
return {
fullName,
hasScore,
score,
urlSlug: p.urlSlug
}
})).catch((error) => next(error))
req.breadcrumbs(res.locals.pageTitle)
pupilsFormatted = pupilsFormatted.filter((p) => p.hasScore)
const hdfSubmitted = await headteacherDeclarationService.isHdfSubmittedForCurrentCheck(req.user.School, checkWindowData && checkWindowData.id)
if (hdfSubmitted &&
(typeof pupilsFormatted === 'object' && Object.keys(pupilsFormatted).length > 0)) {
return res.render('school/results', {
breadcrumbs: req.breadcrumbs(),
pupils: pupilsFormatted,
school
})
} else {
return res.render('school/no-results', {
breadcrumbs: req.breadcrumbs()
})
}
}
// TODO: refactor this into a service call
controller.downloadResults = async (req, res, next) => {
// TODO: refactor to make it smaller
const csvStream = csv.createWriteStream()
const pupils = await pupilDataService.sqlFindPupilsBySchoolId(req.user.schoolId)
const schoolData = await schoolDataService.sqlFindOneById(pupils[0].school_id)
// Format the pupils
let pupilsFormatted = await Promise.all(pupils.map(async (p) => {
const fullName = `${p.foreName} ${p.lastName}`
const dob = dateService.formatUKDate(p.dateOfBirth)
const answersSet = null // await pupilService.fetchAnswers(p.id) // method has been removed!
if (!answersSet) return
let answers = answersSet.answers && answersSet.answers.sort((a1, a2) => {
const f1 = a1.factor1 - a2.factor1
if (f1 !== 0) return f1
return a1.factor2 - a2.factor2
})
answers = answers.map(a => {
const question = `${a.factor1}x${a.factor2}`
const pupilAnswer = a.input
const answerMark = a.isCorrect ? 1 : 0
return {
question,
pupilAnswer,
answerMark
}
})
const pupilScore = answersSet && answersSet.result
if (!pupilScore || typeof pupilScore.correct !== 'number') return
const totalMark = pupilScore && pupilScore.correct.toString()
return {
fullName,
dob,
answers,
totalMark
}
})).catch((error) => next(error))
pupilsFormatted = pupilsFormatted.filter((p) => p)
const pupilStructure = pupilsFormatted[0]
const csvContent = []
const csvHeaders = ['Full Name', 'Date of Birth']
// Generate the row headers
pupilStructure.answers.forEach((answer, i) => {
const question = `Question ${i + 1}`
const input = `Answer ${i + 1}`
const mark = `Mark ${i + 1}`
csvHeaders.push(question, input, mark)
})
csvHeaders.push('Score')
// Generate the pupils
const csvPupils = []
pupilsFormatted.forEach((p, i) => {
csvPupils[i] = []
Object.keys(p).forEach((k) => {
if (k === 'answers') {
p[k].forEach((a) => {
Object.keys(a).forEach((ak) => csvPupils[i].push(a[ak].toString()))
})
} else {
csvPupils[i].push(p[k])
}
})
})
csvContent.push(csvHeaders)
csvPupils.forEach((p) => csvContent.push(p))
const checkDate = moment(moment.now()).format('DD MMM YYYY')
res.setHeader('Content-disposition', `attachment filename=Results ${schoolData.leaCode}${schoolData.estabCode} ${checkDate}.csv`)
res.setHeader('content-type', 'text/csv')
csvContent.forEach((row) => csvStream.write(row))
csvStream.pipe(res)
csvStream.end()
}
controller.getReviewPupilDetails = async (req, res, next) => {
res.locals.pageTitle = 'Review pupil details'
req.breadcrumbs("Headteacher's declaration form", '/attendance/declaration-form')
req.breadcrumbs(res.locals.pageTitle)
try {
const pupils = await headteacherDeclarationService.findPupilsForSchool(req.user.schoolId)
if (!pupils) {
return next('No pupils found')
}
const pupilsSortedWithFlags = pupilPresenter.getPupilsSortedWithIdentificationFlags(pupils)
return res.render('hdf/review-pupil-details', {
breadcrumbs: req.breadcrumbs(),
pupils: pupilsSortedWithFlags
})
} catch (error) {
return next(error)
}
}
controller.getEditReason = async (req, res, next) => {
res.locals.pageTitle = 'Edit reason for not taking the check'
req.breadcrumbs("Headteacher's declaration form", '/attendance/declaration-form')
req.breadcrumbs('Review pupil details', '/attendance/review-pupil-details')
req.breadcrumbs('Edit reason')
if (!req.params.urlSlug) {
return res.redirect('/attendance/review-pupil-details')
}
let pupil, attendanceCodes
try {
pupil = await headteacherDeclarationService.findPupilBySlugAndSchoolId(req.params.urlSlug, req.user.schoolId)
attendanceCodes = await attendanceCodeService.getAttendanceCodes()
} catch (error) {
return next(error)
}
if (!pupil) {
return next(new Error('Pupil not found in school'))
}
return res.render('hdf/attendance-edit-reason', {
breadcrumbs: req.breadcrumbs(),
pupil: pupil,
attendanceCodes: attendanceCodes
})
}
controller.postSubmitEditReason = async (req, res, next) => {
const { urlSlug, attendanceCode } = req.body
try {
const pupil = await headteacherDeclarationService.findPupilBySlugAndSchoolId(urlSlug, req.user.schoolId)
await headteacherDeclarationService.updatePupilsAttendanceCode([pupil.id], attendanceCode, req.user.id)
req.flash('info', `Outcome updated for ${pupil.lastName}, ${pupil.foreName} `)
req.flash('urlSlug', pupil.urlSlug)
return res.redirect('/attendance/review-pupil-details')
} catch (error) {
return next(error)
}
}
controller.getConfirmSubmit = async (req, res, next) => {
res.locals.pageTitle = 'Confirm and submit'
req.breadcrumbs("Headteacher's declaration form", '/attendance/declaration-form')
req.breadcrumbs('Review pupil details', '/attendance/review-pupil-details')
req.breadcrumbs(res.locals.pageTitle)
try {
const checkWindowData = await checkWindowV2Service.getActiveCheckWindow()
const availabilityData = await businessAvailabilityService.getAvailabilityData(req.user.schoolId, checkWindowData, req.user.timezone)
const hdfEligibility = await headteacherDeclarationService.getEligibilityForSchool(req.user.schoolId, checkWindowData.checkEndDate, req.user.timezone)
if (!hdfEligibility) {
return res.render('hdf/declaration-form', {
hdfEligibility,
formData: req.body,
error: new ValidationError(),
breadcrumbs: req.breadcrumbs()
})
}
if (!availabilityData.hdfAvailable) {
return res.render('availability/section-unavailable', {
title: res.locals.pageTitle,
breadcrumbs: req.breadcrumbs()
})
}
return res.render('hdf/confirm-and-submit', {
formData: req.body,
error: res.error || new ValidationError(),
breadcrumbs: req.breadcrumbs()
})
} catch (error) {
return next(error)
}
}
controller.postConfirmSubmit = async (req, res, next) => {
let validationError = await hdfConfirmValidator.validate(req.body)
if (validationError.hasError()) {
res.error = validationError
return controller.getConfirmSubmit(req, res, next)
}
// Re-validate the hdf form data
const hdfFormData = req.session.hdfFormData
validationError = await hdfValidator.validate(hdfFormData)
if (validationError.hasError()) {
return next(new Error('Invalid HDF form data'))
}
try {
const checkWindowData = await checkWindowV2Service.getActiveCheckWindow()
await headteacherDeclarationService
.submitDeclaration({ ...hdfFormData, ...req.body }, req.user.id, req.user.schoolId, checkWindowData.checkEndDate, req.user.timezone)
} catch (error) {
return next(error)
}
return res.redirect('/attendance/submitted')
}
controller.getDeclarationForm = async (req, res, next) => {
res.locals.pageTitle = "Headteacher's declaration form"
req.breadcrumbs(res.locals.pageTitle)
let hdfEligibility
try {
const checkWindowData = await checkWindowV2Service.getActiveCheckWindow()
const availabilityData = await businessAvailabilityService.getAvailabilityData(req.user.schoolId, checkWindowData, req.user.timezone)
const hdfSubmitted = await headteacherDeclarationService.isHdfSubmittedForCurrentCheck(req.user.schoolId, checkWindowData && checkWindowData.id)
if (!availabilityData.hdfAvailable) {
return res.render('availability/section-unavailable', {
title: res.locals.pageTitle,
breadcrumbs: req.breadcrumbs()
})
}
if (hdfSubmitted) {
return res.redirect('/attendance/submitted')
}
hdfEligibility = await headteacherDeclarationService.getEligibilityForSchool(req.user.schoolId, checkWindowData.checkEndDate, req.user.timezone)
} catch (error) {
return next(error)
}
return res.render('hdf/declaration-form', {
hdfEligibility,
formData: req.body,
error: new ValidationError(),
breadcrumbs: req.breadcrumbs()
})
}
controller.postDeclarationForm = async (req, res, next) => {
const { firstName, lastName, isHeadteacher, jobTitle } = req.body
const form = { firstName, lastName, isHeadteacher, jobTitle }
const checkWindowData = await checkWindowV2Service.getActiveCheckWindow()
let hdfEligibility
try {
hdfEligibility = await headteacherDeclarationService.getEligibilityForSchool(req.user.schoolId, checkWindowData.checkEndDate, req.user.timezone)
} catch (error) {
return next(error)
}
const validationError = await hdfValidator.validate(form)
if (validationError.hasError()) {
res.locals.pageTitle = "Headteacher's declaration form"
req.breadcrumbs(res.locals.pageTitle)
return res.render('hdf/declaration-form', {
hdfEligibility,
formData: form,
error: validationError,
breadcrumbs: req.breadcrumbs()
})
}
req.session.hdfFormData = form
return res.redirect('/attendance/review-pupil-details')
}
controller.getHDFSubmitted = async (req, res, next) => {
res.locals.pageTitle = "Headteacher's declaration form"
req.breadcrumbs(res.locals.pageTitle)
try {
const hdf = await headteacherDeclarationService.findLatestHdfForSchool(req.user.School)
if (!hdf) {
return res.redirect('/attendance/declaration-form')
}
const resultsDate = hdfPresenter.getResultsDate(hdf)
return res.render('hdf/submitted', {
breadcrumbs: req.breadcrumbs(),
signedDayAndDate: dateService.formatShortGdsDate(hdf.signedDate),
hdf,
canViewResults: hdfPresenter.getCanViewResults(resultsDate)
})
} catch (error) {
return next(error)
}
}
controller.getHDFSubmittedForm = async (req, res, next) => {
res.locals.pageTitle = 'View submission'
req.breadcrumbs("Headteacher's declaration form", '/attendance/declaration-form')
req.breadcrumbs(res.locals.pageTitle)
try {
const hdf = await headteacherDeclarationService.findLatestHdfForSchool(req.user.School)
if (!hdf) {
return res.redirect('/attendance/declaration-form')
}
return res.render('hdf/submitted-form', {
breadcrumbs: req.breadcrumbs(),
hdf: hdf,
signedDate: dateService.formatFullGdsDate(hdf.signedDate)
})
} catch (error) {
return next(error)
}
}
module.exports = controller