diff --git a/backend/web/server/plugins/aftral-lms/consts.js b/backend/web/server/plugins/aftral-lms/consts.js index e000e518aa..41e8e1a7d2 100644 --- a/backend/web/server/plugins/aftral-lms/consts.js +++ b/backend/web/server/plugins/aftral-lms/consts.js @@ -163,7 +163,7 @@ module.exports={ BLOCK_STATUS, BLOCK_STATUS_TO_COME, BLOCK_STATUS_CURRENT, BLOCK_STATUS_FINISHED, BLOCK_STATUS_UNAVAILABLE, FEED_TYPE, FEED_TYPE_GENERAL, FEED_TYPE_GROUP, FEED_TYPE_SESSION, RESOURCE_TYPE_EXCEL, RESOURCE_TYPE_PDF, RESOURCE_TYPE_PPT, RESOURCE_TYPE_VIDEO, RESOURCE_TYPE_WORD, - ACHIEVEMENT_RULE, ACHIEVEMENT_RULE_SUCCESS, + ACHIEVEMENT_RULE, ACHIEVEMENT_RULE_SUCCESS, ACHIEVEMENT_RULE_CONSULT, ACHIEVEMENT_RULE_DOWNLOAD, SCALE, RESOURCE_TYPE_EXT, RESOURCE_TYPE_LINK, DEFAULT_ACHIEVEMENT_RULE, AVAILABLE_ACHIEVEMENT_RULES, ACHIEVEMENT_RULE_CHECK, } \ No newline at end of file diff --git a/backend/web/server/plugins/aftral-lms/functions.js b/backend/web/server/plugins/aftral-lms/functions.js index 9c9c90adc1..3470d5dbd7 100644 --- a/backend/web/server/plugins/aftral-lms/functions.js +++ b/backend/web/server/plugins/aftral-lms/functions.js @@ -14,6 +14,7 @@ const Post = require('../../models/Post') require('../../models/Module') require('../../models/Sequence') require('../../models/Search') +require('../../models/Chapter') //Added chapter, it was removed somehow const { computeStatistics } = require('./statistics') const { searchUsers, searchBlocks } = require('./search') const { getUserHomeworks, getResourceType, getAchievementRules, getBlockSpentTime, getBlockSpentTimeStr, getResourcesCount, getFinishedResourcesCount } = require('./resources') @@ -29,6 +30,7 @@ const Resource = require('../../models/Resource') const { parseAsync } = require('@babel/core') const Progress = require('../../models/Progress') const { BadRequestError } = require('../../utils/errors') +const { getTraineeResources } = require('./user') const GENERAL_FEED_ID='FFFFFFFFFFFFFFFFFFFFFFFF' @@ -75,7 +77,6 @@ declareComputedField({model: 'program', field: 'available_codes', requires: 'cod declareComputedField({model: 'resource', field: 'mine', getterFn: isResourceMine}) - declareEnumField({model: 'feed', field: 'type', enumValues: FEED_TYPE}) declareEnumField({model: 'post', field: '_feed_type', enumValues: FEED_TYPE}) @@ -85,6 +86,7 @@ declareEnumField({model: 'purchase', field: 'status', enumValues: PURCHASE_STATU const USER_MODELS=['user', 'loggedUser', 'contact'] USER_MODELS.forEach(model => { declareEnumField({model, field: 'role', instance: 'String', enumValues: ROLES}) + declareComputedField({model, field: 'resources', getterFn: getTraineeResources}) }) // search start diff --git a/backend/web/server/plugins/aftral-lms/schemas/BlockSchema.js b/backend/web/server/plugins/aftral-lms/schemas/BlockSchema.js index 08f329cbd5..453a1e2f77 100644 --- a/backend/web/server/plugins/aftral-lms/schemas/BlockSchema.js +++ b/backend/web/server/plugins/aftral-lms/schemas/BlockSchema.js @@ -217,7 +217,6 @@ BlockSchema.pre('validate', async function(next) { // If this is a type resource and achievement rule is success and this is not a scorm, // must select between min/max notes and scale if (!this._locked && this.type=='resource') { - console.log(this) const resourceType= this.resource_type || await getAttribute('resource_type')(null, null, this) const allowedAchievementRules=AVAILABLE_ACHIEVEMENT_RULES[resourceType] // TODO allowedAchievementRules may be null if the block is not still inserted in DB diff --git a/backend/web/server/plugins/aftral-lms/schemas/UserSchema.js b/backend/web/server/plugins/aftral-lms/schemas/UserSchema.js index e5085d1811..f0749119df 100644 --- a/backend/web/server/plugins/aftral-lms/schemas/UserSchema.js +++ b/backend/web/server/plugins/aftral-lms/schemas/UserSchema.js @@ -41,7 +41,12 @@ const UserSchema = new Schema({ type: Schema.Types.ObjectId, ref: 'program', required: false, - } + }, + resources: [{ + type: Schema.Types.ObjectId, + ref: 'resource', + required: false + }], }, schemaOptions) /* eslint-disable prefer-arrow-callback */ diff --git a/backend/web/server/plugins/aftral-lms/user.js b/backend/web/server/plugins/aftral-lms/user.js new file mode 100644 index 0000000000..b01c079017 --- /dev/null +++ b/backend/web/server/plugins/aftral-lms/user.js @@ -0,0 +1,50 @@ +const mongoose = require('mongoose') +const Session = require('../../models/Session') +const { getBlockResources } = require('./resources') +const Block = require('../../models/Block') +const Resource = require('../../models/Resource') +const Program = require('../../models/Program') +const Chapter = require('../../models/Chapter') +const Module = require('../../models/Module') +const Sequence = require('../../models/Sequence') + +const getRelatedDocuments = async (Model, ids) => { + return await Model.find({ _id: { $in: ids } }).populate('children') +} + +const getIdsFromChildren = (documents) => { + return documents.flatMap(doc => doc.children.map(child => child._id)) +} + +const getTraineeResources = async (userId, params, data) => { + const sessions = await Session.find({ trainees: data._id }).populate('children') + + const programIds = getIdsFromChildren(sessions) + const programs = await getRelatedDocuments(Program, programIds) + + let modules + + if (programs[0]?.children[0]?.type === 'chapter') { + const chapterIds = getIdsFromChildren(programs) + const chapters = await getRelatedDocuments(Chapter, chapterIds) + + const moduleIds = getIdsFromChildren(chapters) + modules = await getRelatedDocuments(Module, moduleIds) + } else { + const moduleIds = getIdsFromChildren(programs) + modules = await getRelatedDocuments(Module, moduleIds) + } + + const sequenceIds = getIdsFromChildren(modules) + const sequences = await getRelatedDocuments(Sequence, sequenceIds) + + const resourceIds = getIdsFromChildren(sequences) + const resources = await Resource.find({ _id: { $in: resourceIds } }) + + return resources +} + + +module.exports = { + getTraineeResources +} \ No newline at end of file diff --git a/backend/web/tests/aftral-lms/user.test.js b/backend/web/tests/aftral-lms/user.test.js new file mode 100644 index 0000000000..a4b01c774f --- /dev/null +++ b/backend/web/tests/aftral-lms/user.test.js @@ -0,0 +1,161 @@ +const mongoose = require('mongoose') +const { MONGOOSE_OPTIONS, loadFromDb } = require('../../server/utils/database') +require('../../server/plugins/aftral-lms/functions') +const User = require('../../server/models/User') +const Resource = require('../../server/models/Resource') +const Sequence = require('../../server/models/Sequence') +const Module = require('../../server/models/Module') +const Program = require('../../server/models/Program') +const Session = require('../../server/models/Session') +const { ROLE_APPRENANT, ROLE_FORMATEUR, RESOURCE_TYPE_PDF, ACHIEVEMENT_RULE_CHECK, ACHIEVEMENT_RULE_SUCCESS, ACHIEVEMENT_RULE_CONSULT, RESOURCE_TYPE_VIDEO, ACHIEVEMENT_RULE_DOWNLOAD, ROLE_CONCEPTEUR } = require('../../server/plugins/aftral-lms/consts') +const ProductCode = require('../../server/models/ProductCode') +const { addChildAction } = require('../../server/plugins/aftral-lms/actions') +const { getBlockResources } = require('../../server/plugins/aftral-lms/resources') +const Block = require('../../server/models/Block') + +jest.setTimeout(60000) + +describe('User', () => { + let conceptor, trainee, trainer, session, program, productCode, modulee1, modulee2, sequence1, sequence2, sequence3, resource1, resource2, resource3, resource4 + beforeAll(async () => { + await mongoose.connect(`mongodb://localhost/aftral-test`, MONGOOSE_OPTIONS) + + trainee = await User.create({ + role: ROLE_APPRENANT, + email: `a@a.com`, + password: `Je déteste Python`, + firstname: `John`, + lastname: `Doe`, + }) + + trainer = await User.create({ + role: ROLE_FORMATEUR, + email: `b@b.com`, + password: `J'enseigne le Python`, + firstname: `Jeanette`, + lastname: `Doe`, + }) + + conceptor = await User.create({ + role: ROLE_CONCEPTEUR, + email: `c@c.com`, + password: `J'impose le Python`, + firstname: `Jack`, + lastname: `Doe`, + }) + + session = await Block.create({ + type: 'session', + name: `Test session`, + creator: trainer._id, + start_date: new Date(), + end_date: new Date('2025-07-07'), + trainees:[trainee._id] + }) + + productCode = await ProductCode.create({code: 'Product Code Test'}) + + program = await Block.create({ + type: 'program', + codes:[productCode.id], + name: `Test program`, + creator: trainer._id + }) + + modulee1 = await Block.create({ + type: 'module', + name: `Test Module 1`, + creator: trainer._id + }) + + modulee2 = await Block.create({ + type: 'module', + name: `Test Module 2`, + creator: trainer._id + }) + + sequence1 = await Block.create({ + type: 'sequence', + name: `Test Sequence 1`, + creator: trainer._id + }) + + sequence2 = await Block.create({ + type: 'sequence', + name: `Test Sequence 2`, + creator: trainer._id + }) + + sequence3 = await Block.create({ + type: 'sequence', + name: `Test Sequence 3`, + creator: trainer._id + }) + + resource1 = await Block.create({ + type: 'resource', + name: `Test Resource 1`, + resource_type: RESOURCE_TYPE_VIDEO, + creator: trainer._id, + url: `hi.pdf`, + achievement_rule: ACHIEVEMENT_RULE_DOWNLOAD + }) + + resource2 = await Block.create({ + type: 'resource', + name: `Test Resource 2`, + resource_type: RESOURCE_TYPE_VIDEO, + creator: trainer._id, + url: `hi.pdf`, + achievement_rule: ACHIEVEMENT_RULE_DOWNLOAD + }) + + resource3 = await Block.create({ + type: 'resource', + name: `Test Resource 3`, + resource_type: RESOURCE_TYPE_VIDEO, + creator: trainer._id, + url: `hi.pdf`, + achievement_rule: ACHIEVEMENT_RULE_DOWNLOAD + }) + + resource4 = await Block.create({ + type: 'resource', + name: `Test Resource 4`, + resource_type: RESOURCE_TYPE_VIDEO, + creator: trainer._id, + url: `hi.pdf`, + achievement_rule: ACHIEVEMENT_RULE_DOWNLOAD + }) + + await addChildAction({parent: session._id, child: program._id}, conceptor) + await addChildAction({parent: program._id, child: modulee1._id}, conceptor) + await addChildAction({parent: program._id, child: modulee2._id}, conceptor) + await addChildAction({parent: modulee1._id, child: sequence1._id}, conceptor) + await addChildAction({parent: modulee1._id, child: sequence2._id}, conceptor) + await addChildAction({parent: modulee2._id, child: sequence3._id}, conceptor) + await addChildAction({parent: sequence1._id, child: resource1._id}, conceptor) + await addChildAction({parent: sequence1._id, child: resource2._id}, conceptor) + await addChildAction({parent: sequence2._id, child: resource3._id}, conceptor) + await addChildAction({parent: sequence3._id, child: resource4._id}, conceptor) + + await session.save() + await program.save() + await program.save() + await modulee1.save() + await modulee2.save() + await sequence1.save() + await sequence2.save() + await sequence3.save() + }) + + afterAll(async () => { + await mongoose.connection.dropDatabase() + await mongoose.connection.close() + }) + + it(`it must user's resources`, async () => { + const [u] = await loadFromDb({model:'user', fields:['resources'], id:trainee._id}) + expect(u.resources.length).toEqual(4) + }) +}) \ No newline at end of file