Skip to content

Commit

Permalink
Merge pull request #153 from RA341/submissions-files
Browse files Browse the repository at this point in the history
enforce max submissions for assignment
  • Loading branch information
jessehartloff authored Oct 28, 2024
2 parents e86f3e0 + 802d83b commit 4369778
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 35 deletions.
32 changes: 3 additions & 29 deletions devU-api/src/entities/assignment/assignment.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import AssignmentService from './assignment.service'
import { GenericResponse, NotFound, Updated } from '../../utils/apiResponse.utils'

import { serialize } from './assignment.serializer'
import { BucketNames, downloadFile, uploadFile } from '../../fileStorage'
import { generateFilename } from '../../utils/fileUpload.utils'
import { BucketNames, downloadFile } from '../../fileStorage'

export async function detail(req: Request, res: Response, next: NextFunction) {
try {
Expand Down Expand Up @@ -85,34 +84,9 @@ export async function getReleased(req: Request, res: Response, next: NextFunctio
}


async function processFiles(req: Request) {
let fileHashes: string[] = []
let fileNames: string[] = []

// save files
if (req.files) {
console.log()
if (Array.isArray(req.files)) {
for (let index = 0; index < req.files.length; index++) {
const item = req.files[index]
const filename = generateFilename(item.originalname, item.size)
await uploadFile(BucketNames.ASSIGNMENTSATTACHMENTS, item, filename)
fileHashes.push(filename)
fileNames.push(item.originalname)
}
} else {
console.warn(`Files where not in array format ${req.files}`)
}
} else {
console.warn(`No files where processed`)
}

return { fileHashes, fileNames }
}

export async function post(req: Request, res: Response, next: NextFunction) {
try {
const { fileNames, fileHashes } = await processFiles(req)
const { fileNames, fileHashes } = await AssignmentService.processFiles(req)

req.body['attachmentsFilenames'] = fileNames
req.body['attachmentsHashes'] = fileHashes
Expand All @@ -130,7 +104,7 @@ export async function post(req: Request, res: Response, next: NextFunction) {

export async function put(req: Request, res: Response, next: NextFunction) {
try {
const { fileNames, fileHashes } = await processFiles(req)
const { fileNames, fileHashes } = await AssignmentService.processFiles(req)

req.body['attachmentsFilenames'] = fileNames
req.body['attachmentsHashes'] = fileHashes
Expand Down
37 changes: 36 additions & 1 deletion devU-api/src/entities/assignment/assignment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { dataSource } from '../../database'
import AssignmentModel from './assignment.model'

import { Assignment } from 'devu-shared-modules'
import { Request } from 'express'
import { generateFilename } from '../../utils/fileUpload.utils'
import { BucketNames, uploadFile } from '../../fileStorage'

const connect = () => dataSource.getRepository(AssignmentModel)

Expand Down Expand Up @@ -40,7 +43,7 @@ export async function update(assignment: Assignment) {
maxSubmissions,
disableHandins,
attachmentsHashes,
attachmentsFilenames
attachmentsFilenames,
})
}

Expand Down Expand Up @@ -78,6 +81,36 @@ export async function isReleased(id: number) {
return startDate && startDate < currentDate
}

async function getMaxSubmissionsAndDeadline(id: number) {
return await connect().findOne({ where: { id: id, deletedAt: IsNull() }, select: ['maxSubmissions', 'maxFileSize', 'disableHandins', 'endDate'] })
}

async function processFiles(req: Request) {
let fileHashes: string[] = []
let fileNames: string[] = []

// save files
if (req.files) {
console.log()
if (Array.isArray(req.files)) {
for (let index = 0; index < req.files.length; index++) {
const item = req.files[index]
const filename = generateFilename(item.originalname, item.size)
await uploadFile(BucketNames.ASSIGNMENTSATTACHMENTS, item, filename)
fileHashes.push(filename)
fileNames.push(item.originalname)
}
} else {
console.warn(`Files where not in array format ${req.files}`)
}
} else {
console.warn(`No files where processed`)
}

return { fileHashes, fileNames }
}


export default {
create,
retrieve,
Expand All @@ -87,4 +120,6 @@ export default {
listByCourse,
listByCourseReleased,
isReleased,
getMaxSubmissionsForAssignment: getMaxSubmissionsAndDeadline,
processFiles,
}
77 changes: 77 additions & 0 deletions devU-api/src/entities/submission/submission.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// middleware to enforce mac submissions

import { Request, Response, NextFunction } from 'express'
import SubmissionService from './submission.service'
import AssignmentService from '../assignment/assignment.service'

// TODO discuss how to bypass this when an instructor wants to eg bypass for a specific student
// checks number of submissions and checks if the assignment is beyond the deadline
async function checkSubmissions(req: Request, res: Response, next: NextFunction) {
const userID = req.currentUser?.userId
const assignmentId = req.body.assignmentId

if (!userID) {
return res.status(403).send('userid is missing')
}

try {
const assignmentInfo = await AssignmentService.getMaxSubmissionsForAssignment(assignmentId)

if (assignmentInfo == null) return res.status(403).send('could not retrieve assignment info')

if (assignmentInfo!.disableHandins) {
// console.debug('Handins are now disabled')
return res.status(403).json({ 'Error': 'Handins are now disabled for this assignment' })
}

const currentTime = new Date()

if (assignmentInfo!.endDate < currentTime) {
// console.debug('Submission after enddate')
return res.status(403).json({
'Error': 'Submission after end date',
'endDate': assignmentInfo!.endDate,
'currentTime': currentTime,
})
}

if (assignmentInfo!.maxSubmissions == null) {
console.debug('Max submissions are not specified, skipping check')
// check file size
if (req.file && req.file.size > assignmentInfo!.maxFileSize!) {
return res.status(403).json({
'error': 'file is bigger than allowed max file size',
'allowed size': assignmentInfo!.maxFileSize!,
'file size': req.file.size,
})
}

return next()
}

const submissions = await SubmissionService.listByAssignment(assignmentId, userID)
// check submissions
if (submissions.length >= assignmentInfo!.maxSubmissions!) {
return res.status(403).json({
'error': 'max submissions reached.',
'Max submissions': assignmentInfo!.maxSubmissions!,
'Current submissions': submissions.length,
})
}

// check file size
if (req.file && req.file.size > assignmentInfo!.maxFileSize!) {
return res.status(403).json({
'error': 'file is bigger than allowed max file size',
'allowed size': assignmentInfo!.maxFileSize!,
'file size': req.file.size,
})
}

next()
} catch (e) {
return res.status(500).send(e)
}
}

export { checkSubmissions }
3 changes: 2 additions & 1 deletion devU-api/src/entities/submission/submission.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { asInt } from '../../middleware/validator/generic.validator'

// Controller
import SubmissionController from '../submission/submission.controller'
import { checkSubmissions } from './submission.middleware'

const Router = express.Router({ mergeParams: true })
const upload = Multer()
Expand Down Expand Up @@ -97,7 +98,7 @@ Router.get('/user/:userId', isAuthorized('enrolled'), asInt('userId'), Submissio
* schema:
* $ref: '#/components/schemas/Submission'
*/
Router.post('/', isAuthorized('enrolled'), upload.single('files'), validator, SubmissionController.post)
Router.post('/', isAuthorized('enrolled'), upload.single('files'), validator, checkSubmissions, SubmissionController.post)
// TODO: submissionCreateSelf or submissionCreateAll

/**
Expand Down
2 changes: 1 addition & 1 deletion devU-api/src/entities/submission/submission.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export async function create(submission: Submission, file?: Express.Multer.File
if (!content.filepaths) {
content.filepaths = []
}
content.filepaths.push(filename)
content.filepaths.push(`${bucket}/${filename}`)
submission.content = JSON.stringify(content)

await fileConn().save(fileModel)
Expand Down
3 changes: 1 addition & 2 deletions devU-api/src/entities/submission/submission.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { check } from 'express-validator'

import validate from '../../middleware/validator/generic.validator'

const userId = check('userId').isNumeric()
const assignmentId = check('assignmentId').isNumeric()
const courseId = check('courseId').isNumeric()
const content = check('content').isString()
Expand All @@ -17,6 +16,6 @@ const file = check('file')
}
})

const validator = [courseId, assignmentId, userId, content, file, validate]
const validator = [courseId, assignmentId, content, file, validate]

export default validator
4 changes: 3 additions & 1 deletion devU-api/src/fileUpload/fileUpload.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export async function detail(req: Request, res: Response, next: NextFunction) {

if (!file) return res.status(404).json(NotFound)

res.status(200).json(file)
res.setHeader('Content-Type', 'application/octet-stream')
res.setHeader('Content-Length', file.length)
res.send(file)
} catch (err) {
next(err)
}
Expand Down

0 comments on commit 4369778

Please sign in to comment.