Skip to content

Commit

Permalink
Merge branch 'develop' into task-86-update_for_student's_submissions_…
Browse files Browse the repository at this point in the history
…page
  • Loading branch information
ashwaqaljanahi2021 authored Oct 28, 2024
2 parents c256f05 + a61de37 commit 3acedd2
Show file tree
Hide file tree
Showing 32 changed files with 994 additions and 315 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
5 changes: 5 additions & 0 deletions devU-api/src/entities/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export async function retrieve(id: number) {
return await connect().findOneBy({ id, deletedAt: IsNull() })
}

export async function retrieveByEmail(email: string) {
return await connect().findOneBy({ email: email, deletedAt: IsNull() })
}

export async function list() {
return await connect().findBy({ deletedAt: IsNull() })
}
Expand Down Expand Up @@ -56,6 +60,7 @@ export async function ensure(userInfo: User) {
export default {
create,
retrieve,
retrieveByEmail,
update,
_delete,
list,
Expand Down
51 changes: 48 additions & 3 deletions devU-api/src/entities/userCourse/userCourse.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,16 @@ export async function detailByUser(req: Request, res: Response, next: NextFuncti
export async function post(req: Request, res: Response, next: NextFunction) {
try {
const userCourse = await UserCourseService.create(req.body)
if (userCourse === null) {
return res.status(409).json('User already enrolled')
}

const response = serialize(userCourse)

res.status(201).json(response)
} catch (err) {
if (err instanceof Error) {
res.status(400).json(new GenericResponse(err.message))
res.status(400).json(new GenericResponse(err.message))
}
}
}
Expand All @@ -88,7 +92,10 @@ export async function put(req: Request, res: Response, next: NextFunction) {
req.body.courseId = parseInt(req.params.id)
const currentUser = req.currentUser?.userId
if (!currentUser) return res.status(401).json({ message: 'Unauthorized' })
const results = await UserCourseService.update(req.body, currentUser)

req.body.userId = currentUser

const results = await UserCourseService.update(req.body)
if (!results.affected) return res.status(404).json(NotFound)

res.status(200).json(Updated)
Expand Down Expand Up @@ -128,4 +135,42 @@ export async function _delete(req: Request, res: Response, next: NextFunction) {
}
}

export default { get, getByCourse, getAll, detail, detailByUser, post, put, _delete, checkEnroll }
export async function addStudents(req: Request, res: Response, next: NextFunction) {
try {
const userEmails = req.body['users'] as string[]
if (!userEmails || userEmails.length == 0) return res.status(422).json({ message: 'users field not found or is empty' })
const courseId = parseInt(req.params.courseId)

const result = await UserCourseService.bulkCreate(userEmails, courseId, false)
res.status(201).json(result)
} catch (err) {
next(err)
}
}

export async function dropStudents(req: Request, res: Response, next: NextFunction) {
try {
const userEmails = req.body['users'] as string[]
if (!userEmails || userEmails.length == 0) return res.status(422).json({ message: 'users field not found or is empty' })
const courseId = parseInt(req.params.courseId)

const result = await UserCourseService.bulkCreate(userEmails, courseId, true)
res.status(201).json(result)
} catch (err) {
next(err)
}
}

export default {
get,
getByCourse,
getAll,
detail,
detailByUser,
post,
put,
_delete,
checkEnroll,
addStudents,
dropStudents,
}
Loading

0 comments on commit 3acedd2

Please sign in to comment.