QuizResultAnswerEntityTrait.php in Quiz 8.6
Same filename and directory in other branches
Namespace
Drupal\quiz\EntityFile
src/Entity/QuizResultAnswerEntityTrait.phpView source
<?php
namespace Drupal\quiz\Entity;
use Drupal;
use Drupal\quiz\Entity\QuizQuestion;
use Drupal\quiz\Entity\QuizQuestionRelationship;
use Drupal\quiz\Entity\QuizResult;
use Drupal\quiz_multichoice\Plugin\quiz\QuizQuestion\MultichoiceResponse;
use Drupal\quiz_truefalse\Plugin\quiz\QuizQuestion\TrueFalseResponse;
use function _quiz_question_response_get_instance;
use function drupal_static;
use function node_load;
/**
* Each question type must store its own response data and be able to calculate
* a score for that data.
*/
trait QuizResultAnswerEntityTrait {
/**
* Get the question of this question response.
*
* @return QuizQuestion
*/
public function getQuizQuestion() {
return Drupal::entityTypeManager()
->getStorage('quiz_question')
->loadRevision($this
->get('question_vid')
->getString());
}
/**
* Get the result of this question response.
*
* @return QuizResult
*/
public function getQuizResult() {
return Drupal::entityTypeManager()
->getStorage('quiz_result')
->load($this
->get('result_id')
->getString());
}
/**
* Indicate whether the response has been evaluated (scored) yet.
*
* Questions that require human scoring (e.g. essays) may need to manually
* toggle this.
*
* @return bool
*/
public function isEvaluated() {
return (bool) $this
->get('is_evaluated')
->getString();
}
/**
* Check to see if the answer is marked as correct.
*
* This default version returns TRUE if the score is equal to the maximum
* possible score. Each question type can determine on its own if the question
* response is "correct". For example a multiple choice question with 4
* correct answers could be considered correct in different configurations.
*
* @return bool
*/
public function isCorrect() {
return $this
->getMaxScore() == $this
->getPoints();
}
/**
* Get the scaled awarded points.
*
* This is marked as final to make sure that no question overrides this and
* causes reporting issues.
*
* @return float
* The user's scaled awarded points for this question.
*/
public final function getPoints() {
return (int) $this
->get('points_awarded')
->getString();
}
/**
* Get the related question relationship from this quiz result answer.
*
* @return QuizQuestionRelationship
*/
public function getQuestionRelationship() {
$quiz_result = QuizResult::load($this
->get('result_id')
->getString());
$relationships = Drupal::entityTypeManager()
->getStorage('quiz_question_relationship')
->loadByProperties([
'quiz_id' => $quiz_result
->get('qid')
->getString(),
'quiz_vid' => $quiz_result
->get('vid')
->getString(),
'question_id' => $this
->get('question_id')
->getString(),
'question_vid' => $this
->get('question_vid')
->getString(),
]);
if ($relationships) {
return reset($relationships);
}
}
/**
* Get the weighted max score of this question response. This is the score
* that is entered on the manage questions screen. For example if a multiple
* choice question is worth 4 points, but 8 points are entered on the manage
* questions screen, 8 points is returned here.
*
* @return int
* The weighted max score of this question response.
*/
public function getMaxScore() {
$quiz = $this
->getQuizResult()
->getQuiz();
if ($quiz
->get('randomization')
->getString() == 2) {
return (int) $quiz
->get('max_score_for_random')
->getString();
}
if ($quiz
->get('randomization')
->getString() == 3) {
/* @var $terms Drupal\paragraphs\Entity\Paragraph[] */
$terms = $quiz
->get('quiz_terms')
->referencedEntities();
$total_questions = [];
foreach ($terms as $term) {
if ($term
->get('quiz_question_tid')
->getString() == $this
->get('tid')
->getString()) {
return $term
->get('quiz_question_max_score')
->getString();
}
}
}
if ($relationship = $this
->getQuestionRelationship()) {
return (int) $relationship
->get('max_score')
->getString();
}
}
/**
* Creates the report form for the admin pages.
*
* @return array|null
* An renderable FAPI array
*/
public function getReportForm() {
// Add general data, and data from the question type implementation.
$form = array();
$form['display_number'] = array(
'#type' => 'value',
'#value' => $this->display_number,
);
$form['score'] = $this
->getReportFormScore();
$form['answer_feedback'] = $this
->getReportFormAnswerFeedback();
return $form;
}
/**
* Get the response part of the report form.
*
* @return array
* Array of response data, with each item being an answer to a response. For
* an example, see MultichoiceResponse::getFeedbackValues(). The sub items
* are keyed by the feedback type. Providing a NULL option means that
* feedback will not be shown. See an example at
* LongAnswerResponse::getFeedbackValues().
*/
public function getFeedbackValues() {
$data = array();
$data[] = array(
'choice' => 'True',
'attempt' => 'Did the user choose this?',
'correct' => 'Was their answer correct?',
'score' => 'Points earned for this answer',
'answer_feedback' => 'Feedback specific to the answer',
'question_feedback' => 'General question feedback for any answer',
'solution' => 'Is this choice the correct solution?',
'quiz_feedback' => 'Quiz feedback at this time',
);
return $data;
}
/**
* Get the feedback form for the reportForm.
*
* @return array|false
* An renderable FAPI array, or FALSE if no answer form.
*/
public function getReportFormAnswerFeedback() {
$feedback = $this
->get('answer_feedback')
->getValue()[0];
return array(
'#title' => t('Enter feedback'),
'#type' => 'text_format',
'#default_value' => $feedback['value'] ?: '',
'#format' => $feedback['format'] ?: filter_default_format(),
'#attributes' => array(
'class' => array(
'quiz-report-score',
),
),
);
}
/**
* Calculate the unscaled score in points for this question response.
*
* @param array $values
* A part of form state values with the question input from the user.
*
* @return int
* The unscaled point value of the answer.
*/
public abstract function score(array $values);
/**
* Get the user's response.
*
* @return mixed
* The answer given by the user
*/
public abstract function getResponse();
/**
* Can the quiz taker view the requested review?
*
* @param string $option
* An option key.
*
* @return bool
*/
public function canReview($option) {
$can_review =& drupal_static(__METHOD__, array());
if (!isset($can_review[$option])) {
$quiz_result = $this
->getQuizResult();
$can_review[$option] = $quiz_result
->canReview($option);
}
return $can_review[$option];
}
/**
* Implementation of getReportFormScore().
*
* @see QuizQuestionResponse::getReportFormScore()
*/
public function getReportFormScore() {
$score = $this
->isEvaluated() ? $this
->getPoints() : '';
return array(
'#title' => 'Enter score',
'#type' => 'number',
'#default_value' => $score,
'#min' => 0,
'#max' => $this
->getMaxScore(),
'#attributes' => array(
'class' => array(
'quiz-report-score',
),
),
'#required' => TRUE,
'#field_suffix' => '/ ' . $this
->getMaxScore(),
);
}
/**
* Get answers for a question in a result.
*
* This static method assists in building views for the mass export of
* question answers.
*
* It is not as easy as instantiating all the question responses and returning
* the answer. To do this in views scalably we have to gather the data
* carefully.
*
* This base method provides a very poor way of gathering the data.
*
* @see views_handler_field_prerender_list for the expected return value.
*
* @see MultichoiceResponse::viewsGetAnswers() for a correct approach
* @see TrueFalseResponse::viewsGetAnswers() for a correct approach
*/
public static function viewsGetAnswers(array $result_answer_ids = array()) {
$items = array();
foreach ($result_answer_ids as $result_answer_id) {
$ra = entity_load_single('quiz_result_answer', $result_answer_id);
$question = node_load($ra->question_nid, $ra->question_vid);
/* @var $ra_i QuizQuestionResponse */
$ra_i = _quiz_question_response_get_instance($ra->result_id, $question);
$items[$ra->result_id][] = array(
'answer' => $ra_i
->getResponse(),
);
}
return $items;
}
/**
* Get the weighted score ratio.
*
* This returns the ratio of the weighted score of this question versus the
* question score. For example, if the question is worth 10 points in the
* associated quiz, but it is a 3 point multichoice question, the weighted
* ratio is 3.33.
*
* This is marked as final to make sure that no question overrides this and
* causes reporting issues.
*
* @return float
* The weight of the question
*/
public function getWeightedRatio() {
if ($this
->getMaxScore() == 0) {
return 0;
}
// getMaxScore() will get the relationship max score.
// getMaximumScore() gets the unscaled question max score.
return $this
->getMaxScore() / $this
->getQuizQuestion()
->getMaximumScore();
}
/**
* Indicate whether the response has been evaluated (scored) yet.
*
* Questions that require human scoring (e.g. essays) may need to manually
* toggle this.
*
* @return bool
*/
public function isAnswered() {
return (bool) (!$this
->get('answer_timestamp')
->isEmpty());
}
/**
* Indicate if the question was marked as skipped.
*
* @return bool
*/
public function isSkipped() {
return (bool) $this
->get('is_skipped')
->getString();
}
}
Traits
Name | Description |
---|---|
QuizResultAnswerEntityTrait | Each question type must store its own response data and be able to calculate a score for that data. |