Get pertient data from the last recorded step of a learners interactions within a question attempt.
@param PHPObj $questionAttempt
@return [String => Mixed]
185344
private function getLastState($questionAttempt) {

        // Default placeholder to -1 so that the first item we check will always be greater than the placeholder.
        $sequencenumber = -1;

        // Default state in case there are no steps.
        $state = (object)[
            "state" => "todo",
            "timestamp" => null
        ];

        // Cycle through steps to find the last one (the one with the highest sequence number).
        foreach ($questionAttempt->steps as $stepId => $step) {
            if ($step->sequencenumber > $sequencenumber) {

                // Now this step has the highest sequence number we've seen.
                $sequencenumber = $step->sequencenumber;
                $state = (object)[
                    "state" => $step->state,
                    "timestamp" => $step->timecreated,
                    "fraction" => (is_null($step->fraction) || $step->fraction == '') ? 0 : floatval($step->fraction)
                ];
            }
        }
        return $state;
    }
139007
private function serializeSolutions(BooleanQuestion $question)
    {
        return array_map(function (BooleanChoice $choice) {
            $solutionData = [
                'id' => $choice->getUuid(),
                'score' => $choice->getScore(),
            ];

            if ($choice->getFeedback()) {
                $solutionData['feedback'] = $choice->getFeedback();
            }

            return $solutionData;
        }, $question->getChoices()->toArray());
    }
89523
public function process_common($questiondata) {

        // This routine initialises the question object.
        $question = $this->defaultquestion();

        // Determine if the question is already escaped html.
        $this->ishtml = $this->getpath($questiondata,
                array('#', 'BODY', 0, '#', 'FLAGS', 0, '#', 'ISHTML', 0, '@', 'value'),
                false, false);

        // Put questiontext in question object.
        $text = $this->getpath($questiondata,
                array('#', 'BODY', 0, '#', 'TEXT', 0, '#'),
                '', true, get_string('importnotext', 'qformat_blackboard_six'));

        $questiontext = $this->cleaned_text_field($text);
        $question->questiontext = $questiontext['text'];
        $question->questiontextformat = $questiontext['format']; // Needed because add_blank_combined_feedback uses it.
        if (isset($questiontext['itemid'])) {
            $question->questiontextitemid = $questiontext['itemid'];
        }

        // Put name in question object. We must ensure it is not empty and it is less than 250 chars.
        $id = $this->getpath($questiondata, array('@', 'id'), '',  true);
        $question->name = $this->create_default_question_name($question->questiontext,
                get_string('defaultname', 'qformat_blackboard_six' , $id));

        $question->generalfeedback = '';
        $question->generalfeedbackformat = FORMAT_HTML;
        $question->generalfeedbackfiles = array();

        // TODO : read the mark from the POOL TITLE QUESTIONLIST section.
        $question->defaultmark = 1;
        return $question;
    }
742092
protected function getQuestionHelper(): QuestionHelper
    {
        if ($this->questionHelper === null) {
            $this->questionHelper = new QuestionHelper();
            $helperSet = new HelperSet([new FormatterHelper()]);
            $this->questionHelper->setHelperSet($helperSet);
        }
        return $this->questionHelper;
    }
346500
public function deserializeQuestion(array $questionData, AbstractItem $question = null, array $options = [])
    {
        return $this->getQuestionSerializer()->deserialize($questionData, $question, $options);
    }
945735
public function load_question_attempt_step($stepid) {
        $records = $this->db->get_recordset_sql("
SELECT
    quba.contextid,
    COALESCE(q.qtype, 'missingtype') AS qtype,
    qas.id AS attemptstepid,
    qas.questionattemptid,
    qas.sequencenumber,
    qas.state,
    qas.fraction,
    qas.timecreated,
    qas.userid,
    qasd.name,
    qasd.value

FROM      {question_attempt_steps}     qas
JOIN      {question_attempts}          qa   ON qa.id              = qas.questionattemptid
JOIN      {question_usages}            quba ON quba.id            = qa.questionusageid
LEFT JOIN {question}                   q    ON q.id               = qa.questionid
LEFT JOIN {question_attempt_step_data} qasd ON qasd.attemptstepid = qas.id

WHERE
    qas.id = :stepid
        ", array('stepid' => $stepid));

        if (!$records->valid()) {
            throw new coding_exception('Failed to load question_attempt_step ' . $stepid);
        }

        $step = question_attempt_step::load_from_records($records, $stepid);
        $records->close();

        return $step;
    }
641993
public function getCoursesAsync($limit = null, $starting_after = null, $ending_before = null)
    {
        return $this->getCoursesAsyncWithHttpInfo($limit, $starting_after, $ending_before)
            ->then(
                function ($response) {
                    return $response[0];
                }
            );
    }
156185
final public function get_answers() {
        global $DB;
        if ($this->answers === null) {
            $this->answers = array();
            $answers = $DB->get_records('lesson_answers', array('pageid'=>$this->properties->id, 'lessonid'=>$this->lesson->id), 'id');
            if (!$answers) {
                // It is possible that a lesson upgraded from Moodle 1.9 still
                // contains questions without any answers [MDL-25632].
                // debugging(get_string('cannotfindanswer', 'lesson'));
                return array();
            }
            foreach ($answers as $answer) {
                $this->answers[count($this->answers)] = new lesson_page_answer($answer);
            }
        }
        return $this->answers;
    }
381736
public function process_question(array $data, array $raw) {
        global $CFG;

        // Populate the list of matches first to get their ids.
        // Note that the field is re-populated on restore anyway but let us
        // do our best to produce valid backup files.
        $matchids = array();
        if (isset($data['matchs']['match'])) {
            foreach ($data['matchs']['match'] as $match) {
                $matchids[] = $match['id'];
            }
        }

        // Convert match options.
        if (isset($data['matchoptions'])) {
            $matchoptions = $data['matchoptions'][0];
        } else {
            $matchoptions = array('shuffleanswers' => 1);
        }
        $matchoptions['id'] = $this->converter->get_nextid();
        $matchoptions['subquestions'] = implode(',', $matchids);
        $this->write_xml('matchoptions', $matchoptions, array('/matchoptions/id'));

        // Convert matches.
        $this->xmlwriter->begin_tag('matches');
        if (isset($data['matchs']['match'])) {
            foreach ($data['matchs']['match'] as $match) {
                // Replay the upgrade step 2009072100.
                $match['questiontextformat'] = 0;
                if ($CFG->texteditors !== 'textarea' and $data['oldquestiontextformat'] == FORMAT_MOODLE) {
                    $match['questiontext'] = text_to_html($match['questiontext'], false, false, true);
                    $match['questiontextformat'] = FORMAT_HTML;
                } else {
                    $match['questiontextformat'] = $data['oldquestiontextformat'];
                }

                $match['questiontext'] = $this->migrate_files(
                        $match['questiontext'], 'qtype_match', 'subquestion', $match['id']);
                $this->write_xml('match', $match, array('/match/id'));
            }
        }
        $this->xmlwriter->end_tag('matches');
    }
157358
public static function get_postdata(question_attempt $qa) {
        $qaid = $qa->get_database_id();
        $qubaid = $qa->get_usage_id();
        $qid = $qa->get_question()->id;
        $slot = $qa->get_slot();
        $checksum = self::get_toggle_checksum($qubaid, $qid, $qaid, $slot);
        return "qaid={$qaid}&qubaid={$qubaid}&qid={$qid}&slot={$slot}&checksum={$checksum}&sesskey=" .
                sesskey() . '&newstate=';
    }
1270136
public static function get_last_completed($feedbackid, $courseid = 0) {
        global $PAGE;

        $params = array('feedbackid' => $feedbackid, 'courseid' => $courseid);
        $params = self::validate_parameters(self::get_last_completed_parameters(), $params);
        $warnings = array();

        list($feedback, $course, $cm, $context, $completioncourse) = self::validate_feedback($params['feedbackid'],
            $params['courseid']);
        $feedbackcompletion = new mod_feedback_completion($feedback, $cm, $completioncourse->id);

        if ($feedbackcompletion->is_anonymous()) {
             throw new moodle_exception('anonymous', 'feedback');
        }
        if ($completed = $feedbackcompletion->find_last_completed()) {
            $exporter = new feedback_completed_exporter($completed);
            return array(
                'completed' => $exporter->export($PAGE->get_renderer('core')),
                'warnings' => $warnings,
            );
        }
        throw new moodle_exception('not_completed_yet', 'feedback');
    }
1246281
public function last($cb=null)
    {
        if ($cb==null) {
            return $this->isNotEmpty()?$this->rows[$this->count()-1]:null;
        } else {
            //method returns the last element in the collection that passes a given truth test:
            $count = $this->count();
            for ($i=0;$i<$count;$i++) {
                if ($cb($this->rows[$count-$i], $count-$i)===true) {
                    return $this->rows[$count-$i];
                }
            }
        }
        return null;
    }
18256
public function getPresetTargetData()
    {
        $targetData = parent::getPresetTargetData();
        if ($this->token) {
            $targetData['track']        = $this->token->getTrackName();
            $targetData['round']        = $this->token->getRoundDescription();
            $targetData['survey']       = $this->token->getSurvey()->getName();
            $targetData['last_contact'] = $this->token->getMailSentDate();
        }
        return $targetData;
    }
598752
public function asQuestion()
    {
        $instance = $this->questionClassInstance()
            ->setMaxAttempts($this->maxAttempt);

        // Set question instance hidden if defined by the field.
        if ($this->hidden) {
            $instance->setHidden(true);
        }

        // Set validation callback if field is required.
        if ($this->isRequire()) {
            $this->setValidation(function ($answer) {
                if ($answer == ''
                    && $this->dataType() !== 'boolean') {
                    throw new \Exception('Field is required.');
                }
            });
        }

        // Set question instance validator callback.
        $instance->setValidator(function ($answer) {

            // Iterate over all field validation callbacks.
            foreach ($this->validation as $callback) {
                if (!is_callable($callback)) {
                    continue;
                }

                $callback($answer);
            }

            return $answer;
        });

        // Set question normalizer based on the field normalizer. If a default
        // normalizer is being set from the question class then setting the
        // normalizer will trump it's execution, as only one normalizer can be
        // set per question instance.
        if (isset($this->normalizer)
            && is_callable($this->normalizer)) {
            $instance->setNormalizer($this->normalizer);
        }

        return $instance;
    }
597987
public function getStatistics(Item $question, Exercise $exercise = null, $finishedPapersOnly = false)
    {
        $questionStats = [];

        // We load all the answers for the question (we need to get the entities as the response in DB are not processable as is)
        $answers = $this->answerRepository->findByQuestion($question, $exercise, $finishedPapersOnly);

        // Number of Users that have seen the question
        $questionStats['seen'] = count($answers);

        // Grab answer data to pass it decoded to the question type
        // it doesn't need to know the whole Answer object
        $answersData = [];

        // get corrected answers for the Item in order to compute question success percentage
        $correctedAnswers = [];

        // Number of Users that have answered the question (no blank answer)
        $questionStats['answered'] = 0;

        if (!empty($answers)) {
            // Let the handler of the question type parse and compile the data
            $definition = $this->itemDefinitions->get($question->getMimeType());

            for ($i = 0; $i < $questionStats['seen']; ++$i) {
                $answer = $answers[$i];
                if (!empty($answer->getData())) {
                    ++$questionStats['answered'];
                    $answersData[] = json_decode($answer->getData(), true);
                }

                // for each answer get corresponding correction
                if ($definition instanceof AnswerableItemDefinitionInterface && isset($answersData[$i])) {
                    $corrected = $definition->correctAnswer($question->getInteraction(), $answersData[$i]);
                    $correctedAnswers[] = $corrected;
                }
            }

            // Let the handler of the question type parse and compile the data
            if ($definition instanceof AnswerableItemDefinitionInterface) {
                $questionStats['solutions'] = $definition->getStatistics($question->getInteraction(), $answersData, $questionStats['seen']);
            }
        }

        // get the number of good answers among all
        $nbGoodAnswers = 0;

        foreach ($correctedAnswers as $corrected) {
            if ($corrected instanceof CorrectedAnswer && 0 === count($corrected->getMissing()) && 0 === count($corrected->getUnexpected())) {
                ++$nbGoodAnswers;
            }
        }
        // compute question success percentage
        $questionStats['successPercent'] = $questionStats['answered'] > 0 ? (100 * $nbGoodAnswers) / $questionStats['answered'] : 0;

        return $questionStats;
    }
717030
protected function compute_next_start_time($numprevattempts, $lastattempt) {
        if ($numprevattempts == 0) {
            return 0;
        }

        $lastattemptfinish = $lastattempt->timefinish;
        if ($this->quiz->timelimit > 0) {
            $lastattemptfinish = min($lastattemptfinish,
                    $lastattempt->timestart + $this->quiz->timelimit);
        }

        if ($numprevattempts == 1 && $this->quiz->delay1) {
            return $lastattemptfinish + $this->quiz->delay1;
        } else if ($numprevattempts > 1 && $this->quiz->delay2) {
            return $lastattemptfinish + $this->quiz->delay2;
        }
        return 0;
    }
1274008
public function get_last_step_with_behaviour_var($name) {
        foreach ($this->get_reverse_step_iterator() as $step) {
            if ($step->has_behaviour_var($name)) {
                return $step;
            }
        }
        return new question_attempt_step_read_only();
    }