You are here

public function OpignoAnswerForm::save in Opigno module 8

Same name and namespace in other branches
  1. 3.x src/Form/OpignoAnswerForm.php \Drupal\opigno_module\Form\OpignoAnswerForm::save()

Form submission handler for the 'save' action.

Normally this method should be overridden to provide specific messages to the user and redirect the form after the entity has been saved.

Parameters

array $form: An associative array containing the structure of the form.

\Drupal\Core\Form\FormStateInterface $form_state: The current state of the form.

Return value

int Either SAVED_NEW or SAVED_UPDATED, depending on the operation performed.

Overrides EntityForm::save

File

src/Form/OpignoAnswerForm.php, line 102

Class

OpignoAnswerForm
Form controller for Answer edit forms.

Namespace

Drupal\opigno_module\Form

Code

public function save(array $form, FormStateInterface $form_state) {
  $entity =& $this->entity;
  $activity = $entity
    ->getActivity();
  $module = $entity
    ->getModule();
  $attempt = $module
    ->getModuleActiveAttempt($this
    ->currentUser());
  $moduleHandler = \Drupal::service('module_handler');
  $activities = $module
    ->getModuleActivities();
  if ($attempt !== NULL) {
    $attempt
      ->setLastActivity($activity);
    $entity
      ->setUserModuleStatus($attempt);

    // Check if answer should be evaluated or not.
    // Make it possible to modify answer object before save.

    /* @var $answer_service \Drupal\opigno_module\ActivityAnswerManager */
    $answer_service = \Drupal::service('plugin.manager.activity_answer');
    $answer_activity_type = $activity
      ->getType();
    if ($answer_service
      ->hasDefinition($answer_activity_type)) {
      $answer_instance = $answer_service
        ->createInstance($answer_activity_type);

      // Evaluation status.
      $evaluated_status = $answer_instance
        ->evaluatedOnSave($activity) ? 1 : 0;

      // Answer score.
      if ($activity
        ->hasField('opigno_evaluation_method') && $activity
        ->get('opigno_evaluation_method')->value) {
        $score = 0;
      }
      else {
        $score = $answer_instance
          ->getScore($entity);
      }

      // Calculate score for skills system if activity not included in the current module.
      // Activity type H5P.
      if ($moduleHandler
        ->moduleExists('opigno_skills_system') && $module
        ->getSkillsActive() == 1 && $activity
        ->getType() == 'opigno_h5p') {
        $h5p_score = $form_state
          ->getValue('score');
        $percent_score = $h5p_score / 1.234 - 32.17;
        $score = round($percent_score * 10);
        if ($score < 0) {
          $score = 0;
        }
      }
      $entity
        ->setScore(round($score));
    }

    // Set evaluation status.
    if (isset($evaluated_status)) {
      $entity
        ->setEvaluated($evaluated_status);
    }

    // $entity->save();
    $attempt
      ->save();
  }
  if ($moduleHandler
    ->moduleExists('opigno_skills_system')) {

    // Set skill ID.
    $skill_id = $activity
      ->getSkillId();
    if (!empty($skill_id)) {
      $entity
        ->setSkillId($skill_id);
    }

    // Set skill level.
    $skill_level = $activity
      ->getSkillLevel();
    if (!empty($skill_level)) {
      $entity
        ->setSkillLevel($skill_level);
    }
  }
  $status = parent::save($form, $form_state);
  switch ($status) {
    case SAVED_NEW:
    case SAVED_UPDATED:
      break;
    default:
      \Drupal::messenger()
        ->addMessage($this
        ->t('Saved the %label Answer.', [
        '%label' => $entity
          ->id(),
      ]));
  }
  if ($activity
    ->getType() == 'opigno_scorm') {
    $form_state
      ->set('scorm_answer', $entity);
  }
  $args = [
    'opigno_module' => $module
      ->id(),
  ];
  $current_group = \Drupal::routeMatch()
    ->getParameter('group');
  if ($current_group) {
    $args['group'] = $current_group
      ->id();
  }
  else {
    $args['group'] = OpignoGroupContext::getCurrentGroupId();
  }

  // Query param is used to detect if we go to take page
  // from submitted answer.
  $form_state
    ->setRedirect('opigno_module.take_module', $args, [
    'query' => [
      'continue' => TRUE,
    ],
  ]);

  // Calculate skills statistic.
  if ($moduleHandler
    ->moduleExists('opigno_skills_system') && !empty($skill_level) && !empty($skill_id)) {
    $db_connection = \Drupal::service('database');
    $uid = $this
      ->currentUser()
      ->id();
    $term_storage = \Drupal::entityTypeManager()
      ->getStorage('taxonomy_term');
    $skill_entity = $term_storage
      ->load($skill_id);
    if ($skill_entity != NULL) {
      $minimum_score = $skill_entity
        ->get('field_minimum_score')
        ->getValue()[0]['value'];
      $minimum_answers = $skill_entity
        ->get('field_minimum_count_of_answers')
        ->getValue()[0]['value'];
      $skill_level = $activity
        ->getSkillLevel();

      // Get initial level. This is equal to the count of levels.
      $initial_level = count($skill_entity
        ->get('field_level_names')
        ->getValue());
      $initial_level = $initial_level === 0 ? 1 : $initial_level;

      // Get current user's skills.
      $query = $db_connection
        ->select('opigno_skills_statistic', 'o_s_s');
      $query
        ->fields('o_s_s', [
        'tid',
        'score',
        'progress',
        'stage',
      ]);
      $query
        ->condition('o_s_s.uid', $uid);
      $query
        ->condition('o_s_s.tid', $skill_id);
      $user_skills = $query
        ->execute()
        ->fetchAllAssoc('tid');

      // Get current level of user's skill.
      if (isset($user_skills[$skill_id]) && $user_skills[$skill_id]->stage == $skill_level) {
        $current_stage = $user_skills[$skill_id]->stage;
      }
      elseif (isset($skill_level) || !$module
        ->getSkillsActive()) {
        $current_stage = $skill_level;
      }
      else {
        $current_stage = $initial_level;
      }
      $stage = $current_stage;

      // Get last user's answers on questions for current skill.
      $query = $db_connection
        ->select('opigno_answer_field_data', 'o_a_f_d');
      $query
        ->addExpression('MAX(o_a_f_d.score)', 'score');
      $query
        ->addExpression('MAX(o_a_f_d.changed)', 'changed');
      $query
        ->addExpression('MAX(o_a_f_d.skill_level)', 'skill_level');
      $query
        ->addField('o_a_f_d', 'activity');
      $query
        ->condition('o_a_f_d.user_id', $uid)
        ->condition('o_a_f_d.skills_mode', $skill_id);
      $all_answers = $query
        ->groupBy('o_a_f_d.activity')
        ->orderBy('changed', 'DESC')
        ->execute()
        ->fetchAllAssoc('activity');
      $activity_ids = array_keys($all_answers);

      // Get max score of questions for skill.
      $query = $db_connection
        ->select('opigno_module_relationship', 'o_m_r');
      $query
        ->fields('o_m_r', [
        'child_id',
        'max_score',
      ]);
      $query
        ->condition('o_m_r.parent_id', $module
        ->id());
      $query
        ->condition('o_m_r.parent_vid', $module
        ->getRevisionId());
      $query
        ->condition('o_m_r.child_id', $activity_ids, 'IN');
      $max_scores = $query
        ->execute()
        ->fetchAllAssoc('child_id');

      // Get last user's answers for each level of skill.
      $answers = [];
      $answer_count_for_levels = [];

      // Check the level of skill.
      while ($current_stage > 0) {
        $count_answers_for_stage = 0;
        $avg_score = 0;
        foreach ($all_answers as $key => $answer) {
          if ($answer->skill_level == $current_stage) {
            $answers[$answer->activity] = $answer;
            $count_answers_for_stage++;
            if (!isset($max_scores[$key]->max_score)) {
              $max_scores[$key]->max_score = 10;
            }
            $avg_score += $answer->score / $max_scores[$key]->max_score;
            if ($count_answers_for_stage >= $minimum_answers) {
              $answer_count_for_levels[$current_stage]['access'] = TRUE;
              $avg_score = round($avg_score / $minimum_answers * 100);
              $answer_count_for_levels[$current_stage]['avg_score'] = $avg_score;
              if ($current_stage > $stage) {
                $stage = $current_stage;
              }
              break;
            }
          }
        }
        $current_stage--;
      }

      // Get average score and current progress of skill.
      $avg_score = 0;
      $current_progress = 0;
      $count_of_levels = $initial_level;

      //      $current_progress = round (count($answers) / $minimum_answers / $count_of_levels * 100);
      foreach ($answers as $key => $answer) {
        if (!isset($max_scores[$key]->max_score)) {
          $max_scores[$key]->max_score = 10;
        }
        $avg_score += $answer->score / $max_scores[$key]->max_score;
      }
      $avg_score = round($avg_score / count($answers) * 100);

      // Update user's skill statistic.
      $keys = [
        'tid' => $skill_id,
        'uid' => $uid,
      ];

      // Check if the user is ready to level-up the skill.
      if (!empty($user_skills) || $minimum_answers == 1) {
        if (!empty($answer_count_for_levels[$stage]) && $answer_count_for_levels[$stage]['access'] == TRUE && $answer_count_for_levels[$stage]['avg_score'] >= $minimum_score) {
          $stage--;
        }
        elseif (isset($user_skills[$skill_id])) {
          $stage = $user_skills[$skill_id]->stage;
        }
      }
      else {
        $stage = $initial_level;
      }

      // Set current progress
      $current_progress = 100 - round($stage / $initial_level * 100);
      $fields = [
        'score' => $avg_score,
        'progress' => $current_progress,
        'stage' => $stage,
      ];
      $query = \Drupal::database()
        ->merge('opigno_skills_statistic')
        ->keys($keys)
        ->fields($fields)
        ->execute();

      // Check if user successfully finished skills tree.
      $target_skill = $module
        ->getTargetSkill();
      $term_storage = \Drupal::entityTypeManager()
        ->getStorage('taxonomy_term');
      $skills_tree = array_reverse($term_storage
        ->loadTree('skills', $target_skill));

      // Get current user's skills.
      $uid = $this
        ->currentUser()
        ->id();
      $query = $db_connection
        ->select('opigno_skills_statistic', 'o_s_s');
      $query
        ->fields('o_s_s', [
        'tid',
        'score',
        'progress',
        'stage',
      ]);
      $query
        ->condition('o_s_s.uid', $uid);
      $user_skills = $query
        ->execute()
        ->fetchAllAssoc('tid');

      // Set default successfully finished this skills tree for user.
      // If the system finds any skill which is not successfully finished then change this variable to FALSE.
      $successfully_finished = TRUE;
      $sum_score = 0;
      foreach ($skills_tree as $key => $skill) {
        $term_storage = \Drupal::entityTypeManager()
          ->getStorage('taxonomy_term');
        $skill_entity = $term_storage
          ->load($skill->tid);
        $minimum_score = $skill_entity
          ->get('field_minimum_score')
          ->getValue()[0]['value'];
        if (!isset($user_skills[$skill->tid])) {
          $successfully_finished = FALSE;
        }
        else {
          $sum_score += $user_skills[$skill->tid]->score;

          // Check if the skill was successfully finished.
          if ($minimum_score > $user_skills[$skill->tid]->score || $user_skills[$skill->tid]->progress < 100) {
            $successfully_finished = FALSE;
          }
        }
      }
      $gid = OpignoGroupContext::getCurrentGroupId();
      $uid = $this
        ->currentUser()
        ->id();
      $steps = LearningPathContent::getAllStepsOnlyModules($gid, $uid);

      // Search for parent.
      $current_step = [];
      foreach ($steps as $step) {
        if ($cid = $step['cid']) {
          $current_step = $step;
          break;
        }
      }
      $activities_from_module = $module
        ->getModuleActivities();
      $count_of_activities = count($activities_from_module);
      $activity_ids = array_keys($activities_from_module);
      $query = $db_connection
        ->select('opigno_answer_field_data', 'o_a_f_d');
      $query
        ->leftJoin('opigno_module_relationship', 'o_m_r', 'o_a_f_d.activity = o_m_r.child_id');
      $query
        ->addExpression('MAX(o_a_f_d.score)', 'score');
      $query
        ->addExpression('MAX(o_a_f_d.changed)', 'changed');
      $query
        ->addExpression('MAX(o_a_f_d.skill_level)', 'skill_level');
      $query
        ->addField('o_a_f_d', 'activity');
      $query
        ->condition('o_a_f_d.user_id', $uid)
        ->condition('o_a_f_d.module', $module
        ->id());
      if (!$module
        ->getModuleSkillsGlobal()) {
        $query
          ->condition('o_a_f_d.activity', $activity_ids, 'IN');
      }
      $query
        ->condition('o_a_f_d.user_module_status', $attempt
        ->id())
        ->condition('o_m_r.max_score', '', '<>')
        ->groupBy('o_a_f_d.activity')
        ->orderBy('changed', 'DESC');
      $answers = $query
        ->execute()
        ->fetchAllAssoc('activity');
      $count_of_answers = count($answers);
      $progress = round($count_of_answers / $count_of_activities * 100);
      $activity_ids = array_keys($answers);
      $sum_score = 0;

      // Get max score of activities.
      if (!empty($activity_ids)) {
        $query = $db_connection
          ->select('opigno_module_relationship', 'o_m_r');
        $query
          ->fields('o_m_r', [
          'child_id',
          'max_score',
        ]);
        $query
          ->condition('o_m_r.parent_id', $module
          ->id());
        $query
          ->condition('o_m_r.parent_vid', $module
          ->getRevisionId());
        $query
          ->condition('o_m_r.child_id', $activity_ids, 'IN');
        $max_scores = $query
          ->execute()
          ->fetchAllAssoc('child_id');
      }
      foreach ($answers as $key => $answer) {
        if (!isset($max_scores[$key]->max_score)) {
          $max_scores[$key]->max_score = 10;
        }
        $sum_score += $answer->score / $max_scores[$key]->max_score;
      }
      $avg_score = $sum_score / $count_of_answers * 100;
      $current_step['best score'] = $avg_score;
      $current_step['current attempt score'] = $avg_score;
      $last_activity_id = end($activities)->id;
      if ($module
        ->getSkillsActive() || $activity
        ->id() == $last_activity_id) {
        if ($successfully_finished == TRUE) {
          $_SESSION['successfully_finished'] = TRUE;
        }
        $attempt
          ->setScore($avg_score);
        $attempt
          ->setMaxScore(100);
        $attempt
          ->save();
      }
    }
  }

  // Send email to the module managers.
  $this
    ->sendEmailToManager($module, $activity, $entity, $current_group);
}