api/lib/domain/services/user-service.js

Summary

Maintainability
A
3 hrs
Test Coverage
const _ = require('lodash');

const { UserNotFoundError } = require('../errors');
const UserCompetence = require('../../../lib/domain/models/UserCompetence');

const userRepository = require('../../../lib/infrastructure/repositories/user-repository');
const assessmentRepository = require('../../../lib/infrastructure/repositories/assessment-repository');
const challengeRepository = require('../../../lib/infrastructure/repositories/challenge-repository');
const answerRepository = require('../../../lib/infrastructure/repositories/answer-repository');
const competenceRepository = require('../../../lib/infrastructure/repositories/competence-repository');
const courseRepository = require('../../../lib/infrastructure/repositories/course-repository');

function _findCorrectAnswersByAssessments(assessments) {

  const answersByAssessmentsPromises = assessments.map((assessment) => answerRepository.findCorrectAnswersByAssessment(assessment.id));

  return Promise.all(answersByAssessmentsPromises)
    .then(answersByAssessments => {
      return answersByAssessments.reduce((answersInJSON, answersByAssessment) => {
        answersByAssessment.models.forEach(answer => {
          answersInJSON.push(answer);
        });
        return answersInJSON;
      }, []);
    });
}

function _getCompetenceByChallengeCompetenceId(competences, challenge) {
  return challenge ? competences.find((competence) => competence.id === challenge.competence) : null;
}

function _loadRequiredChallengesInformationsAndAnswers(answers) {
  return Promise.all([
    challengeRepository.list(), competenceRepository.list(), answers
  ]);
}

function _castCompetencesToUserCompetences([challenges, competences, answers]) {
  competences = competences.reduce((result, value) => {
    result.push(new UserCompetence(value));
    return result;
  }, []);

  return [challenges, competences, answers];
}

function _findChallengeBySkill(challenges, skill) {
  return _(challenges).filter((challenge) => {
    return challenge.hasSkill(skill) && challenge.isPublished();
  }).value();
}

function _skillHasAtLeastOneChallengeInTheReferentiel(skill, challenges) {
  const challengesBySkill = _findChallengeBySkill(challenges, skill);
  return challengesBySkill.length > 0;
}

function _addCourseIdAndPixToCompetence(competences, courses, assessments) {
  competences.forEach((competence) => {
    const currentCourse =  courses.find(course => course.competences[0] === competence.id);
    const assessment = assessments.find(assessment => currentCourse.id === assessment.courseId);
    if (assessment) {
      competence.pixScore = assessment.getPixScore();
      competence.estimatedLevel = assessment.getLevel();
    } else {
      competence.pixScore = 0;
      competence.estimatedLevel = 0;
    }
  });

  return competences;
}

function _sortThreeMostDifficultSkillsInDesc(skills) {
  return _(skills)
    .sortBy('difficulty')
    .reverse()
    .take(3)
    .value();
}

function _limitSkillsToTheThreeHighestOrderedByDifficultyDesc(competences) {
  competences.forEach((competence) => {
    competence.skills = _sortThreeMostDifficultSkillsInDesc(competence.skills);
  });
  return competences;
}

function _getRelatedChallengeById(challenges, answer) {
  return challenges.find((challenge) => challenge.id === answer.get('challengeId'));
}

function _getChallengeById(challenges, challengeId) {
  return _(challenges).find((challenge) => challenge.id === challengeId);
}

function _filterAssessmentWithEstimatedLevelGreaterThanZero(assessments) {
  return _(assessments).filter(assessment => assessment.getLastAssessmentResult().level >= 1).values();
}

module.exports = {
  isUserExistingByEmail(email) {
    return userRepository
      .findByEmail(email)
      .then(() => true)
      .catch(() => {
        throw new UserNotFoundError();
      });
  },

  isUserExistingById(id) {
    return userRepository
      .findUserById(id)
      .then(() => true)
      .catch(() => {
        throw new UserNotFoundError();
      });
  },

  getProfileToCertify(userId, limitDate) {
    let coursesFromAdaptativeCourses;
    let userLastAssessments;
    return courseRepository.getAdaptiveCourses()
      .then((courses) => {
        coursesFromAdaptativeCourses = courses;
        return assessmentRepository.findLastCompletedAssessmentsForEachCoursesByUser(userId, limitDate);
      })
      .then((lastAssessments) => {
        userLastAssessments = lastAssessments;
        return _filterAssessmentWithEstimatedLevelGreaterThanZero(lastAssessments);
      })
      .then(_findCorrectAnswersByAssessments)
      .then(_loadRequiredChallengesInformationsAndAnswers)
      .then(_castCompetencesToUserCompetences)
      .then(([challenges, userCompetences, answers]) => {
        answers.forEach((answer) => {
          const challenge = _getRelatedChallengeById(challenges, answer);
          const competence = _getCompetenceByChallengeCompetenceId(userCompetences, challenge);

          if (challenge && competence) {
            challenge.skills
              .filter(skill => _skillHasAtLeastOneChallengeInTheReferentiel(skill, challenges))
              .forEach(publishedSkill => competence.addSkill(publishedSkill));
          }
        });

        userCompetences = _limitSkillsToTheThreeHighestOrderedByDifficultyDesc(userCompetences);
        const challengeIdsAlreadyAnswered = answers.map(answer => answer.get('challengeId'));
        const challengesAlreadyAnswered = challengeIdsAlreadyAnswered.map(challengeId => _getChallengeById(challenges, challengeId));

        userCompetences = _addCourseIdAndPixToCompetence(userCompetences, coursesFromAdaptativeCourses, userLastAssessments);

        userCompetences.forEach((userCompetence) => {
          userCompetence.skills.forEach((skill) => {
            const challengesToValidateCurrentSkill = _findChallengeBySkill(challenges, skill);
            const challengesLeftToAnswer = _.difference(challengesToValidateCurrentSkill, challengesAlreadyAnswered);

            const challenge = (_.isEmpty(challengesLeftToAnswer)) ? _.first(challengesToValidateCurrentSkill) : _.first(challengesLeftToAnswer);

            challenge.testedSkill = skill.name;

            userCompetence.addChallenge(challenge);
          });
        });

        return userCompetences;
      });
  }
};