Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/cms code #81

Merged
merged 12 commits into from
Jul 16, 2020
9 changes: 4 additions & 5 deletions client/src/Utils/requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const getActivityToolbox = async (id, jwt) => (await axios.get(`${cms}/ac
}
})).data

export const getMentor = async (jwt) => (await axios.get(`${cms}/mentors/me`, {
export const getMentor = async (jwt) => (await axios.get(`${cms}/classroom-managers/me`, {
headers: {
Authorization:
`Bearer ${jwt}`
Expand All @@ -34,18 +34,17 @@ export const getClassroom = async (id, jwt) => (await axios.get(`${cms}/classroo

export const getClassrooms = async (ids, jwt) => ( Promise.all(ids.map( id => getClassroom(id,jwt) )))

export const getActivities = async (jwt) => (await axios.get(`${cms}/sessions/student/activities`, {
export const getActivities = async (jwt) => (await axios.get(`${cms}/activities`, {
headers: {
'Authorization':
`Bearer ${jwt}`
}
})).data

export const getStudents = async (code) => (await axios.get(`${cms}/sessions/code/${code}`)).data
export const getStudents = async (code) => (await axios.get(`${cms}/classroom/join/${code}`)).data

export const postJoin = async (code, ids) => (await axios.post(`${cms}/sessions/join`, {
export const postJoin = async (code, ids) => (await axios.post(`${cms}/classrooms/join/${code}`, {
"students": ids,
"code": code
}
)).data

Expand Down
13 changes: 13 additions & 0 deletions cms/api/classroom-manager/config/routes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"routes": [
{
"method": "GET",
"path": "/classroom-managers/me",
"handler": "classroom-manager.me",
"config": {
"policies": ["global::isClassroomManager"]
}
}
]
}

24 changes: 24 additions & 0 deletions cms/api/classroom-manager/controllers/classroom-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict'

const { sanitizeEntity } = require("strapi-utils/lib")

module.exports = {

/**
* Get the currrent classroom manager that is logged in
*
* @return {Mentor || Teacher}
*/
async me(ctx) {

const { id } = ctx.state.user

// get the classroom manager
const { classroomManager, type } = await strapi.services['classroom-manager'].findById(id)

// remove private fields and return the classroom manager
return type === 'mentor' ?
sanitizeEntity(classroomManager, { model: strapi.models.mentor }) :
sanitizeEntity(classroomManager, { model: strapi.models.teacher })
}
}
14 changes: 14 additions & 0 deletions cms/api/classroom-manager/services/classroom-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

// find a classroom manager by user id
module.exports.findById = async (id) => {

const [ mentor, teacher ] = await Promise.all(['mentor', 'teacher'].map(async type =>
strapi.services[type].findOne({ user: id })
))

return {
classroomManager: mentor ? mentor : teacher,
type: mentor ? 'mentor' : 'teacher'
}
}
18 changes: 17 additions & 1 deletion cms/api/classroom/config/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@
"path": "/classrooms/:id",
"handler": "classroom.findOne",
"config": {
"policies": ["global::isMentor", "global::hasClassroom"]
"policies": ["global::isClassroomManager", "global::hasClassroom"]
}
},
{
"method": "GET",
"path": "/classroom/join/:code",
"handler": "classroom.initJoin",
"config": {
"policies": []
}
},
{
Expand All @@ -32,6 +40,14 @@
"policies": []
}
},
{
"method": "POST",
"path": "/classrooms/join/:code",
"handler": "classroom.join",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/classrooms/:id",
Expand Down
175 changes: 169 additions & 6 deletions cms/api/classroom/controllers/classroom.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,171 @@
'use strict';
'use strict'

/**
* Read the documentation (https://strapi.io/documentation/v3.x/concepts/controllers.html#core-controllers)
* to customize this controller
*/
const { sanitizeEntity } = require('strapi-utils')

module.exports = {};
module.exports = {

/**
* Get the students belonging to a valid classroom by code
*
* @return {Array<Student>}
*/
async initJoin(ctx) {

const { code } = ctx.params
const { classroom } = await strapi.services.classroom.findByCode(code)

// check if the classroom exists
let response
if (classroom) {
// return the students from the classroom
response = classroom.students.map(student => { return {
id: student.id,
name: student.name,
character: student.character
}})
}

return response
},

/**
* Create a new classroom with a unqiue code
*
* @param {String} name
* @param {String} school
*
* @return {Classroom}
*/
async create(ctx) {

// ensure request was not sent as formdata
if (ctx.is('multipart')) return ctx.badRequest(
'Multipart requests are not accepted!',
{ id: 'Classroom.create.format.invalid', error: 'ValidationError' }
)

// ensure the request has the right number of params
const params = Object.keys(ctx.request.body).length
if (params !== 2) return ctx.badRequest(
'Invalid number of params!',
{ id: 'Classroom.create.body.invalid', error: 'ValidationError' }
)

// validate the request
const { name, school } = ctx.request.body
if (!name || !strapi.services.validator.isInt(school)) return ctx.badRequest(
'A name and a school must be provided!',
{ id: 'Classroom.create.body.invalid', error: 'ValidationError' }
)

// ensure the school is valid
const validSchool = await strapi.services.school.findOne({ id: school })
if (validSchool === null) return ctx.notFound(
'The school provided is invalid!',
{ id: 'Classroom.create.school.invalid', error: 'ValidationError' }
)

// add a unique code to the request body
ctx.request.body.code = await strapi.services.classroom.getUniqueCode()

// remove private fields and return the new classroom
const classroom = await strapi.services.classroom.create(ctx.request.body)
return sanitizeEntity(classroom, { model: strapi.models.classroom })
},

/**
* Join a classroom and create a new student session
*
* @param {Integer} students
*
* @return {JWT, Students}
*/
async join(ctx) {

// ensure request was not sent as formdata
if (ctx.is('multipart')) return ctx.badRequest(
'Multipart requests are not accepted!',
{ id: 'Classroom.join.format.invalid', error: 'ValidationError' }
)

// ensure the request has the right number of params
const params = Object.keys(ctx.request.body).length
if (params !== 1) return ctx.badRequest(
'Invalid number of params!',
{ id: 'Classroom.join.body.invalid', error: 'ValidationError' }
)

// validate the request
const { code } = ctx.params
const { students } = ctx.request.body
if(!strapi.services.validator.isIntArray(students)) return ctx.badRequest(
'Must provide at least one integer studentId!',
{ id: 'Classroom.join.body.invalid', error: 'ValidationError'}
)

// ensure the code corresponds to an active classroom
const { classroom } = await strapi.services.classroom.findByCode(code)
if (!classroom) return ctx.notFound(
'The code provided does not correspond to a valid classroom!',
{ id: 'Classroom.join.code.invalid', error: 'ValidationError' }
)

// ensure all the students belong to the classroom
for (let student of students) {
if (classroom.students.find(cs => cs.id === student) === undefined) return ctx.notFound(
'One or more of the students do not belong to the classroom!',
{ id: 'Classroom.join.studentId.invalid', error: 'ValidationError' }
)
}

// create a new session for the students
const session = await strapi.services.session.create({ classroom: classroom.id, students })

// return a jwt for future requests and the students
return {
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
ids: students,
session: session.id,
isStudent: true
}),
students
}
// this bypasses the local authentication and requires custom
// handling of the resulting token in the permissions policy
},

/**
* Update a classroom
*
* @param {classroom}
*
* @return {classroom}
*/
// async update(ctx) {

// // ensure request was not sent as formdata
// if (ctx.is('multipart')) return ctx.badRequest(
// 'Multipart requests are not accepted!',
// { id: 'Session.create.format.invalid', error: 'ValidationError' }
// )

// // ensure the update is not to the code, classroom, or students
// const { code, classroom, students } = ctx.request.body
// if (code || classroom || students) return ctx.badRequest(
// 'A session\'s code, classroom, and students can not be updated!',
// { id: 'Session.update.body.invalid', error: 'ValidationError' }
// )

// const { id } = ctx.params
// const { active } = ctx.request.body

// // when reactivating a session, get a new code
// // in case the original one was issued
// if (active && active === true) ctx.request.body.code = await strapi.services.session.getUniqueCode()

// // update the session
// // remove private fields and return the updated session
// let session = await strapi.services.session.update({ id }, ctx.request.body)
// return sanitizeEntity(session, { model: strapi.models.session })
// },
}
23 changes: 14 additions & 9 deletions cms/api/classroom/models/classroom.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,29 @@
"name": {
"type": "string"
},
"teacher": {
"type": "string"
},
"school": {
"model": "school",
"via": "classrooms"
},
"sessions": {
"via": "classroom",
"collection": "session"
"collection": "session",
"via": "classroom"
},
"mentors": {
"via": "classrooms",
"collection": "mentor"
"collection": "mentor",
"via": "classrooms"
},
"students": {
"via": "classroom",
"collection": "student"
"collection": "student",
"via": "classroom"
},
"code": {
"type": "string",
"required": true
},
"teachers": {
"collection": "teacher",
"via": "classrooms"
}
}
}
47 changes: 42 additions & 5 deletions cms/api/classroom/services/classroom.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,45 @@
'use strict';

/**
* Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
* to customize this service
*/
// find a classroom by code
module.exports.findByCode = async (code) => {

module.exports = {};
// get the whole classroom model
const model = await strapi.query('classroom').model.where('code', code).fetch()

// return the model and the classroom
return {
model,
classroom: model ? model.toJSON() : undefined
}
}

const randomCode = (n) => {

let code = ''
for (n; n > 0; n--)
code += Math.floor(Math.random()*10)
return code
}

// TODO: add a maximum number of tries
// generate a code that is unique amongst classroom
module.exports.getUniqueCode = async (currentCode = undefined) => {

let code = currentCode
let codeExists
do {
// allow an existing code to be passed,
// checking if it active and returning
// a new code if it is being used
if (!code) {
// get a four digit code
code = randomCode(4)
}

// check if a classroom is using the code
const classroom = await strapi.query('classroom').findOne({ code })
codeExists = classroom !== null
}
while (codeExists)
return code
}
Loading