QuestionOptionsEvaluationPlugin.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use Chamilo\CoreBundle\Entity\TrackEAttempt;
  4. /**
  5. * Class QuestionOptionsEvaluationPlugin.
  6. */
  7. class QuestionOptionsEvaluationPlugin extends Plugin
  8. {
  9. const SETTING_ENABLE = 'enable';
  10. const SETTING_MAX_SCORE = 'exercise_max_score';
  11. const EXTRAFIELD_FORMULA = 'quiz_evaluation_formula';
  12. /**
  13. * QuestionValuationPlugin constructor.
  14. */
  15. protected function __construct()
  16. {
  17. $version = '1.0';
  18. $author = 'Angel Fernando Quiroz Campos';
  19. parent::__construct(
  20. $version,
  21. $author,
  22. [
  23. self::SETTING_ENABLE => 'boolean',
  24. self::SETTING_MAX_SCORE => 'text',
  25. ]
  26. );
  27. }
  28. /**
  29. * @return QuestionOptionsEvaluationPlugin|null
  30. */
  31. public static function create()
  32. {
  33. static $result = null;
  34. return $result ? $result : $result = new self();
  35. }
  36. /**
  37. * @param int $exerciseId
  38. * @param int $iconSize
  39. *
  40. * @return string
  41. */
  42. public static function filterModify($exerciseId, $iconSize = ICON_SIZE_SMALL)
  43. {
  44. $directory = basename(__DIR__);
  45. $title = get_plugin_lang('plugin_title', self::class);
  46. $enabled = api_get_plugin_setting('questionoptionsevaluation', 'enable');
  47. if ('true' !== $enabled) {
  48. return '';
  49. }
  50. return Display::url(
  51. Display::return_icon('options_evaluation.png', $title, [], $iconSize),
  52. api_get_path(WEB_PATH)."plugin/$directory/evaluation.php?exercise=$exerciseId",
  53. [
  54. 'class' => 'ajax',
  55. 'data-size' => 'md',
  56. 'data-title' => get_plugin_lang('plugin_title', self::class),
  57. ]
  58. );
  59. }
  60. public function install()
  61. {
  62. $this->createExtraField();
  63. }
  64. public function uninstall()
  65. {
  66. $this->removeExtraField();
  67. }
  68. /**
  69. * @return Plugin
  70. */
  71. public function performActionsAfterConfigure()
  72. {
  73. return $this;
  74. }
  75. /**
  76. * @param int $formula
  77. * @param Exercise $exercise
  78. */
  79. public function saveFormulaForExercise($formula, Exercise $exercise)
  80. {
  81. $this->recalculateQuestionScore($formula, $exercise);
  82. $extraFieldValue = new ExtraFieldValue('quiz');
  83. $extraFieldValue->save(
  84. [
  85. 'item_id' => $exercise->iId,
  86. 'variable' => self::EXTRAFIELD_FORMULA,
  87. 'value' => $formula,
  88. ]
  89. );
  90. }
  91. /**
  92. * @param int $exerciseId
  93. *
  94. * @return int
  95. */
  96. public function getFormulaForExercise($exerciseId)
  97. {
  98. $extraFieldValue = new ExtraFieldValue('quiz');
  99. $value = $extraFieldValue->get_values_by_handler_and_field_variable(
  100. $exerciseId,
  101. self::EXTRAFIELD_FORMULA
  102. );
  103. if (empty($value)) {
  104. return 0;
  105. }
  106. return (int) $value['value'];
  107. }
  108. /**
  109. * @return int
  110. */
  111. public function getMaxScore()
  112. {
  113. $max = $this->get(self::SETTING_MAX_SCORE);
  114. if (!empty($max)) {
  115. return (int) $max;
  116. }
  117. return 10;
  118. }
  119. /**
  120. * @param int $trackId
  121. * @param int $formula
  122. *
  123. * @throws \Doctrine\ORM\ORMException
  124. * @throws \Doctrine\ORM\OptimisticLockException
  125. * @throws \Doctrine\ORM\TransactionRequiredException
  126. *
  127. * @return float|int
  128. */
  129. public function getResultWithFormula($trackId, $formula)
  130. {
  131. $em = Database::getManager();
  132. $eTrack = $em->find('ChamiloCoreBundle:TrackEExercises', $trackId);
  133. $qTracks = $em
  134. ->createQuery(
  135. 'SELECT a FROM ChamiloCoreBundle:TrackEAttempt a
  136. WHERE a.exeId = :id AND a.userId = :user AND a.cId = :course AND a.sessionId = :session'
  137. )
  138. ->setParameters(
  139. [
  140. 'id' => $eTrack->getExeId(),
  141. 'course' => $eTrack->getCId(),
  142. 'session' => $eTrack->getSessionId(),
  143. 'user' => $eTrack->getExeUserId(),
  144. ]
  145. )
  146. ->getResult();
  147. $counts = ['correct' => 0, 'incorrect' => 0];
  148. /** @var TrackEAttempt $qTrack */
  149. foreach ($qTracks as $qTrack) {
  150. if ($qTrack->getMarks() > 0) {
  151. $counts['correct']++;
  152. } elseif ($qTrack->getMarks() < 0) {
  153. $counts['incorrect']++;
  154. }
  155. }
  156. switch ($formula) {
  157. case 1:
  158. $result = $counts['correct'] - $counts['incorrect'];
  159. break;
  160. case 2:
  161. $result = $counts['correct'] - $counts['incorrect'] / 2;
  162. break;
  163. case 3:
  164. $result = $counts['correct'] - $counts['incorrect'] / 3;
  165. break;
  166. }
  167. $score = ($result / count($qTracks)) * $this->getMaxScore();
  168. return $score >= 0 ? $score : 0;
  169. }
  170. /**
  171. * @param int $formula
  172. * @param Exercise $exercise
  173. */
  174. private function recalculateQuestionScore($formula, Exercise $exercise)
  175. {
  176. $tblQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION);
  177. $tblAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
  178. foreach ($exercise->questionList as $questionId) {
  179. $question = Question::read($questionId, $exercise->course, false);
  180. if (!in_array($question->selectType(), [UNIQUE_ANSWER, MULTIPLE_ANSWER])) {
  181. continue;
  182. }
  183. $questionAnswers = new Answer($questionId, $exercise->course_id, $exercise);
  184. $counts = array_count_values($questionAnswers->correct);
  185. $questionPonderation = 0;
  186. foreach ($questionAnswers->correct as $i => $isCorrect) {
  187. if (!isset($questionAnswers->iid[$i])) {
  188. continue;
  189. }
  190. $iid = $questionAnswers->iid[$i];
  191. if ($question->selectType() == MULTIPLE_ANSWER || 0 === $formula) {
  192. $ponderation = 1 == $isCorrect ? 1 / $counts[1] : -1 / $counts[0];
  193. } else {
  194. $ponderation = 1 == $isCorrect ? 1 : -1 / $formula;
  195. }
  196. if ($ponderation > 0) {
  197. $questionPonderation += $ponderation;
  198. }
  199. //error_log("question: $questionId -- i: $i -- w: $ponderation");
  200. Database::query("UPDATE $tblAnswer SET ponderation = $ponderation WHERE iid = $iid");
  201. }
  202. Database::query("UPDATE $tblQuestion SET ponderation = $questionPonderation WHERE iid = {$question->iid}");
  203. }
  204. }
  205. /**
  206. * Creates an extrafield.
  207. */
  208. private function createExtraField()
  209. {
  210. $extraField = new ExtraField('quiz');
  211. if (false === $extraField->get_handler_field_info_by_field_variable(self::EXTRAFIELD_FORMULA)) {
  212. $extraField
  213. ->save(
  214. [
  215. 'variable' => self::EXTRAFIELD_FORMULA,
  216. 'field_type' => ExtraField::FIELD_TYPE_TEXT,
  217. 'display_text' => $this->get_lang('EvaluationFormula'),
  218. 'visible_to_self' => false,
  219. 'changeable' => false,
  220. ]
  221. );
  222. }
  223. }
  224. /**
  225. * Removes the extrafield .
  226. */
  227. private function removeExtraField()
  228. {
  229. $extraField = new ExtraField('quiz');
  230. $value = $extraField->get_handler_field_info_by_field_variable(self::EXTRAFIELD_FORMULA);
  231. if (false !== $value) {
  232. $extraField->delete($value['id']);
  233. }
  234. }
  235. }