Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

Code for loading and saving question attempts to and from the database. Note that many of the methods of this class should be considered private to the question engine. They should be accessed through the {@link question_engine} class. For example, you should call {@link question_engine::save_questions_usage_by_activity()} rather than {@link question_engine_data_mapper::insert_questions_usage_by_activity()}. The exception to this is some of the reporting methods, like {@link question_engine_data_mapper::load_attempts_at_question()}.

Copyright: 2009 The Open University
License: http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
File Size: 2029 lines (74 kb)
Included or required: 1 time
Referenced: 0 times
Includes or requires: 0 files

Defines 8 classes

question_engine_data_mapper:: (36 methods):
  __construct()
  insert_questions_usage_by_activity()
  insert_question_attempt()
  make_step_record()
  combine_step_data()
  prepare_step_data()
  insert_all_step_data()
  insert_question_attempt_step()
  update_question_attempt_step()
  insert_question_attempt_metadata()
  update_question_attempt_metadata()
  load_question_attempt_step()
  load_question_attempt()
  load_questions_usage_by_activity()
  load_questions_usages_by_activity()
  load_questions_usages_latest_steps()
  load_questions_usages_question_state_summary()
  load_questions_usages_where_question_in_state()
  load_average_marks()
  load_attempts_at_question()
  update_questions_usage_by_activity()
  update_question_attempt()
  delete_questions_usage_by_activities()
  delete_usage_records_for_mysql()
  delete_steps()
  delete_response_files()
  delete_previews()
  update_question_attempt_flag()
  full_states_to_summary_state_sql()
  in_summary_state_test()
  set_max_mark_in_attempts()
  sum_usage_marks_subquery()
  question_attempt_latest_state_view()
  latest_step_for_qa_subquery()
  questions_in_use()
  load_used_variants()

question_engine_unit_of_work:: (14 methods):
  __construct()
  notify_modified()
  notify_attempt_added()
  notify_attempt_modified()
  notify_attempt_moved()
  notify_step_added()
  notify_step_modified()
  notify_step_deleted()
  notify_metadata_added()
  notify_metadata_modified()
  is_step_added()
  is_step_modified()
  is_step_deleted()
  save()

question_response_files:: (1 method):
  get_files()

question_file_saver:: (5 methods):
  __construct()
  compute_value()
  __toString()
  save_files()
  get_files()

question_file_loader:: (4 methods):
  __construct()
  __toString()
  get_files()
  get_question_file_saver()

qubaid_condition:: (1 method):
  get_hash_code()

qubaid_list:: (6 methods):
  __construct()
  from_question_attempts()
  where()
  from_where_params()
  usage_id_in()
  usage_id_in_params()

qubaid_join:: (6 methods):
  __construct()
  from_question_attempts()
  where()
  from_where_params()
  usage_id_in()
  usage_id_in_params()


Class: question_engine_data_mapper  - X-Ref

This class controls the loading and saving of question engine data to and from
the database.

__construct(moodle_database $db = null)   X-Ref

param: moodle_database $db a database connectoin. Defaults to global $DB.

insert_questions_usage_by_activity(question_usage_by_activity $quba)   X-Ref
Store an entire {@link question_usage_by_activity} in the database,
including all the question_attempts that comprise it.

You should not call this method directly. You should use
param: question_usage_by_activity $quba the usage to store.

insert_question_attempt(question_attempt $qa, $context)   X-Ref
Store an entire {@link question_attempt} in the database,
including all the question_attempt_steps that comprise it.

You should not call this method directly. You should use
param: question_attempt $qa the question attempt to store.
param: context $context the context of the owning question_usage_by_activity.
return: array of question_attempt_step_data rows, that still need to be inserted.

make_step_record(question_attempt_step $step, $questionattemptid, $seq)   X-Ref
Helper method used by insert_question_attempt_step and update_question_attempt_step

param: question_attempt_step $step the step to store.
param: int $questionattemptid the question attept id this step belongs to.
param: int $seq the sequence number of this stop.
return: stdClass data to insert into the database.

combine_step_data(array $stepdata)   X-Ref
Take an array of arrays, and flatten it, even if the outer array is empty.

Only public so it can be called from the unit of work. Not part of the
public API of this class.

param: array $stepdata array of zero or more arrays.
return: array made by concatenating all the separate arrays.

prepare_step_data(question_attempt_step $step, $stepid, $context)   X-Ref
Helper method used by insert_question_attempt_step and update_question_attempt_step

param: question_attempt_step $step the step to store.
param: int $stepid the id of the step.
param: context $context the context of the owning question_usage_by_activity.
return: array of question_attempt_step_data rows, that still need to be inserted.

insert_all_step_data(array $rows)   X-Ref
Insert a lot of records into question_attempt_step_data in one go.

Private method, only for use by other parts of the question engine.

param: array $rows the rows to insert.

insert_question_attempt_step(question_attempt_step $step,$questionattemptid, $seq, $context)   X-Ref
Store a {@link question_attempt_step} in the database.

Private method, only for use by other parts of the question engine.

param: question_attempt_step $step the step to store.
param: int $questionattemptid the question attept id this step belongs to.
param: int $seq the sequence number of this stop.
param: context $context the context of the owning question_usage_by_activity.
return: array of question_attempt_step_data rows, that still need to be inserted.

update_question_attempt_step(question_attempt_step $step,$questionattemptid, $seq, $context)   X-Ref
Update a {@link question_attempt_step} in the database.

Private method, only for use by other parts of the question engine.

param: question_attempt_step $step the step to store.
param: int $questionattemptid the question attept id this step belongs to.
param: int $seq the sequence number of this stop.
param: context $context the context of the owning question_usage_by_activity.
return: array of question_attempt_step_data rows, that still need to be inserted.

insert_question_attempt_metadata(question_attempt $qa, array $names)   X-Ref
Store new metadata for an existing {@link question_attempt} in the database.

Private method, only for use by other parts of the question engine.

param: question_attempt $qa the question attempt to store meta data for.
param: array $names the names of the metadata variables to store.
return: array of question_attempt_step_data rows, that still need to be inserted.

update_question_attempt_metadata(question_attempt $qa, array $names)   X-Ref
Updates existing metadata for an existing {@link question_attempt} in the database.

Private method, only for use by other parts of the question engine.

param: question_attempt $qa the question attempt to store meta data for.
param: array $names the names of the metadata variables to store.
return: array of question_attempt_step_data rows, that still need to be inserted.

load_question_attempt_step($stepid)   X-Ref
Load a {@link question_attempt_step} from the database.

Private method, only for use by other parts of the question engine.

param: int $stepid the id of the step to load.
return: question_attempt_step the step that was loaded.

load_question_attempt($questionattemptid)   X-Ref
Load a {@link question_attempt} from the database, including all its
steps.

Normally, you should use {@link question_engine::load_questions_usage_by_activity()}
but there may be rare occasions where for performance reasons, you only
wish to load one qa, in which case you may call this method.

param: int $questionattemptid the id of the question attempt to load.
return: question_attempt the question attempt that was loaded.

load_questions_usage_by_activity($qubaid)   X-Ref
Load a {@link question_usage_by_activity} from the database, including
all its {@link question_attempt}s and all their steps.

You should call {@link question_engine::load_questions_usage_by_activity()}
rather than calling this method directly.

param: int $qubaid the id of the usage to load.
return: question_usage_by_activity the usage that was loaded.

load_questions_usages_by_activity($qubaids)   X-Ref
Load all {@link question_usage_by_activity} from the database for one qubaid_condition
Include all its {@link question_attempt}s and all their steps.

This method may be called publicly.

param: qubaid_condition $qubaids the condition that tells us which usages to load.
return: question_usage_by_activity[] the usages that were loaded.

load_questions_usages_latest_steps(qubaid_condition $qubaids, $slots = null, $fields = null)   X-Ref
Load information about the latest state of each question from the database.

This method may be called publicly.

param: qubaid_condition $qubaids used to restrict which usages are included
param: array|null       $slots   (optional) list of slots for which to return information. Default all slots.
param: string|null      $fields
return: array of records. See the SQL in this function to see the fields available.

load_questions_usages_question_state_summary(qubaid_condition $qubaids, $slots = null)   X-Ref
Load summary information about the state of each question in a group of
attempts. This is used, for example, by the quiz manual grading report,
to show how many attempts at each question need to be graded.

This method may be called publicly.

param: qubaid_condition $qubaids used to restrict which usages are included
param: array|null $slots (optional) list of slots for which to return information. Default all slots.
return: array The array keys are 'slot,questionid'. The values are objects with

load_questions_usages_where_question_in_state(qubaid_condition $qubaids, $summarystate, $slot, $questionid = null,$orderby = 'random', $params = array()   X-Ref
Get a list of usage ids where the question with slot $slot, and optionally
also with question id $questionid, is in summary state $summarystate. Also
return the total count of such states.

Only a subset of the ids can be returned by using $orderby, $limitfrom and
$limitnum. A special value 'random' can be passed as $orderby, in which case
$limitfrom is ignored.

This method may be called publicly.

param: qubaid_condition $qubaids used to restrict which usages are included
param: int $slot The slot for the questions you want to know about.
param: int $questionid (optional) Only return attempts that were of this specific question.
param: string $summarystate the summary state of interest, or 'all'.
param: string $orderby the column to order by.
param: array $params any params required by any of the SQL fragments.
param: int $limitfrom implements paging of the results.
param: int $limitnum implements paging of the results. null = all.
return: array with two elements, an array of usage ids, and a count of the total number.

load_average_marks(qubaid_condition $qubaids, $slots = null)   X-Ref
Load the average mark, and number of attempts, for each slot in a set of
question usages..

This method may be called publicly.

param: qubaid_condition $qubaids used to restrict which usages are included
param: array|null $slots if null, load info for all quesitions, otherwise only
return: array of objects with fields ->slot, ->averagefraction and ->numaveraged.

load_attempts_at_question($questionid, qubaid_condition $qubaids)   X-Ref
Load all the attempts at a given queston from a set of question_usages.
steps.

This method may be called publicly.

param: int $questionid the question to load all the attempts fors.
param: qubaid_condition $qubaids used to restrict which usages are included
return: question_attempt[] array of question_attempts that were loaded.

update_questions_usage_by_activity(question_usage_by_activity $quba)   X-Ref
Update a question_usages row to refect any changes in a usage (but not
any of its question_attempts.

You should not call this method directly. You should use
param: question_usage_by_activity $quba the usage that has changed.

update_question_attempt(question_attempt $qa)   X-Ref
Update a question_attempts row to refect any changes in a question_attempt
(but not any of its steps).

You should not call this method directly. You should use
param: question_attempt $qa the question attempt that has changed.

delete_questions_usage_by_activities(qubaid_condition $qubaids)   X-Ref
Delete a question_usage_by_activity and all its associated

You should not call this method directly. You should use
param: qubaid_condition $qubaids identifies which question useages to delete.

delete_usage_records_for_mysql(qubaid_condition $qubaids)   X-Ref
This function is a work-around for poor MySQL performance with
DELETE FROM x WHERE id IN (SELECT ...). We have to use a non-standard
syntax to get good performance. See MDL-29520.

param: qubaid_condition $qubaids identifies which question useages to delete.

delete_steps($stepids, $context)   X-Ref
Delete some steps of a question attempt.

Private method, only for use by other parts of the question engine.

param: array $stepids array of step ids to delete.
param: context $context the context that the $quba belongs to.

delete_response_files($contextid, $itemidstest, $params)   X-Ref
Delete all the files belonging to the response variables in the gives
question attempt steps.

param: int $contextid the context these attempts belong to.
param: string $itemidstest a bit of SQL that can be used in a
param: array $params any query parameters used in $itemidstest.

delete_previews($questionid)   X-Ref
Delete all the previews for a given question.

Private method, only for use by other parts of the question engine.

param: int $questionid question id.

update_question_attempt_flag($qubaid, $questionid, $qaid, $slot, $newstate)   X-Ref
Update the flagged state of a question in the database.

You should call {@link question_engine::update_flag()()}
rather than calling this method directly.

param: int $qubaid the question usage id.
param: int $questionid the question id.
param: int $qaid the question_attempt id.
param: int $slot the slot number of the question attempt to update.
param: bool $newstate the new state of the flag. true = flagged.

full_states_to_summary_state_sql()   X-Ref
Get all the WHEN 'x' THEN 'y' terms needed to convert the question_attempt_steps.state
column to a summary state. Use this like
CASE qas.state {$this->full_states_to_summary_state_sql()} END AS summarystate,

return: string SQL fragment.

in_summary_state_test($summarystate, $equal = true, $prefix = 'summarystates')   X-Ref
Get the SQL needed to test that question_attempt_steps.state is in a
state corresponding to $summarystate.

This method may be called publicly.

param: string $summarystate one of
param: bool $equal if false, do a NOT IN test. Default true.
param: string $prefix used in the call to $DB->get_in_or_equal().
return: array as returned by $DB->get_in_or_equal().

set_max_mark_in_attempts(qubaid_condition $qubaids, $slot, $newmaxmark)   X-Ref
Change the maxmark for the question_attempt with number in usage $slot
for all the specified question_attempts.

You should call {@link question_engine::set_max_mark_in_attempts()}
rather than calling this method directly.

param: qubaid_condition $qubaids Selects which usages are updated.
param: int $slot the number is usage to affect.
param: number $newmaxmark the new max mark to set.

sum_usage_marks_subquery($qubaid)   X-Ref
Return a subquery that computes the sum of the marks for all the questions
in a usage. Which useage to compute the sum for is controlled bu the $qubaid
parameter.

See {@link quiz_update_all_attempt_sumgrades()} for an example of the usage of
this method.

This method may be called publicly.

param: string $qubaid SQL fragment that controls which usage is summed.
return: string SQL code for the subquery.

question_attempt_latest_state_view($alias, qubaid_condition $qubaids)   X-Ref
Get a subquery that returns the latest step of every qa in some qubas.
Currently, this is only used by the quiz reports. See
{@link quiz_attempts_report_table::add_latest_state_join()}.

This method may be called publicly.

param: string $alias alias to use for this inline-view.
param: qubaid_condition $qubaids restriction on which question_usages we
return: array with two elements, the SQL fragment and any params requried.

latest_step_for_qa_subquery($questionattemptid = 'qa.id')   X-Ref
No description

questions_in_use(array $questionids, qubaid_condition $qubaids)   X-Ref
Are any of these questions are currently in use?

You should call {@link question_engine::questions_in_use()}
rather than calling this method directly.

param: array $questionids of question ids.
param: qubaid_condition $qubaids ids of the usages to consider.
return: bool whether any of these questions are being used by any of

load_used_variants(array $questionids, qubaid_condition $qubaids)   X-Ref
Get the number of times each variant has been used for each question in a list
in a set of usages.

param: array $questionids of question ids.
param: qubaid_condition $qubaids ids of the usages to consider.
return: array questionid => variant number => num uses.

Class: question_engine_unit_of_work  - X-Ref

Implementation of the unit of work pattern for the question engine.

See http://martinfowler.com/eaaCatalog/unitOfWork.html. This tracks all the
changes to a {@link question_usage_by_activity}, and its constituent parts,
so that the changes can be saved to the database when {@link save()} is called.

__construct(question_usage_by_activity $quba)   X-Ref
Constructor.

param: question_usage_by_activity $quba the usage to track.

notify_modified()   X-Ref
No description

notify_attempt_added(question_attempt $qa)   X-Ref
No description

notify_attempt_modified(question_attempt $qa)   X-Ref
No description

notify_attempt_moved(question_attempt $qa, $oldslot)   X-Ref
No description

notify_step_added(question_attempt_step $step, question_attempt $qa, $seq)   X-Ref
No description

notify_step_modified(question_attempt_step $step, question_attempt $qa, $seq)   X-Ref
No description

notify_step_deleted(question_attempt_step $step, question_attempt $qa)   X-Ref
No description

notify_metadata_added(question_attempt $qa, $name)   X-Ref
No description

notify_metadata_modified(question_attempt $qa, $name)   X-Ref
No description

is_step_added(question_attempt_step $step)   X-Ref
Determine if a step is new. If so get its array key.

param: question_attempt_step $step a step
return: int|false if the step is in the list of steps to be added, return

is_step_modified(question_attempt_step $step)   X-Ref
Determine if a step is modified. If so get its array key.

param: question_attempt_step $step a step
return: int|false if the step is in the list of steps to be modified, return

is_step_deleted(question_attempt_step $step)   X-Ref

param: question_attempt_step $step a step
return: bool whether the step is in the list of steps to be deleted.

save(question_engine_data_mapper $dm)   X-Ref
Write all the changes we have recorded to the database.

param: question_engine_data_mapper $dm the mapper to use to update the database.

Interface: question_response_files  - X-Ref

The interface implemented by {@link question_file_saver} and {@link question_file_loader}.

get_files()   X-Ref
Get the files that were submitted.

return: array of stored_files objects.

Class: question_file_saver  - X-Ref

This class represents the promise to save some files from a particular draft
file area into a particular file area. It is used beause the necessary
information about what to save is to hand in the
{@link question_attempt::process_response_files()} method, but we don't know
if this question attempt will actually be saved in the database until later,
when the {@link question_engine_unit_of_work} is saved, if it is.

__construct($draftitemid, $component, $filearea, $text = null)   X-Ref
Constructor.

param: int $draftitemid the draft area to save the files from.
param: string $component the component for the file area to save into.
param: string $filearea the name of the file area to save into.
param: string $text optional content containing file links.

compute_value($draftitemid, $text)   X-Ref
Compute the value that should be stored in the question_attempt_step_data table.

Contains a hash that (almost) uniquely encodes all the files.

param: int $draftitemid the draft file area itemid.
param: string $text optional content containing file links.
return: string the value.

__toString()   X-Ref
No description

save_files($itemid, $context)   X-Ref
Actually save the files.

param: integer $itemid the item id for the file area to save into.
param: context $context the context where the files should be saved.

get_files()   X-Ref
Get the files that were submitted.

return: array of stored_files objects.

Class: question_file_loader  - X-Ref

This class is the mirror image of {@link question_file_saver}. It allows
files to be accessed again later (e.g. when re-grading) using that same
API as when doing the original grading.

__construct(question_attempt_step $step, $name, $value, $contextid)   X-Ref
Constuctor.

param: question_attempt_step $step the step that these files belong to.
param: string $name string the field name for these files - which is used to construct the file area name.
param: string $value the value to stored in the question_attempt_step_data to
param: int $contextid the context id that the files belong to.

__toString()   X-Ref
No description

get_files()   X-Ref
Get the files that were submitted.

return: array of stored_files objects.

get_question_file_saver()   X-Ref
Copy these files into a draft area, and return the corresponding
{@link question_file_saver} that can save them again.

This is used by {@link question_attempt::start_based_on()}, which is used
(for example) by the quizzes 'Each attempt builds on last' feature.

return: question_file_saver that can re-save these files again.

Class: qubaid_condition  - X-Ref

This class represents a restriction on the set of question_usage ids to include
in a larger database query. Depending of the how you are going to restrict the
list of usages, construct an appropriate subclass.

If $qubaids is an instance of this class, example usage might be

SELECT qa.id, qa.maxmark
FROM $qubaids->from_question_attempts('qa')
WHERE $qubaids->where() AND qa.slot = 1

get_hash_code()   X-Ref

return: string 40-character hash code that uniquely identifies the combination of properties and class name of this qubaid

Class: qubaid_list  - X-Ref

This class represents a restriction on the set of question_usage ids to include
in a larger database query based on an explicit list of ids.

__construct(array $qubaids)   X-Ref
Constructor.

param: array $qubaids of question usage ids.

from_question_attempts($alias)   X-Ref
No description

where()   X-Ref
No description

from_where_params()   X-Ref
No description

usage_id_in()   X-Ref
No description

usage_id_in_params()   X-Ref
No description

Class: qubaid_join  - X-Ref

This class represents a restriction on the set of question_usage ids to include
in a larger database query based on JOINing to some other tables.

The general form of the query is something like

SELECT qa.id, qa.maxmark
FROM $from
JOIN {question_attempts} qa ON qa.questionusageid = $usageidcolumn
WHERE $where AND qa.slot = 1

where $from, $usageidcolumn and $where are the arguments to the constructor.

__construct($from, $usageidcolumn, $where = '', $params = array()   X-Ref
Constructor. The meaning of the arguments is explained in the class comment.

param: string $from SQL fragemnt to go in the FROM clause.
param: string $usageidcolumn the column in $from that should be
param: string $where SQL fragment to go in the where clause.
param: array $params required by the SQL. You must use named parameters.

from_question_attempts($alias)   X-Ref
No description

where()   X-Ref
No description

from_where_params()   X-Ref
No description

usage_id_in()   X-Ref
No description

usage_id_in_params()   X-Ref
No description