* A method to change the max mark for one question_attempt in the usage
* A method to replace one question in a usage with another, moving the
old question_attempt to the end.
* Methods to set and get metadata (string name value pairs) for each
question_attempt in the usage. This gets stored in the first step in a
way that should not interfere with anything else.
Variant has two purposes. First to determine which version of the
question the student will see. Hence it is used to set up the state
of the quetsion when the question is started. Then the internal state of
the question is saved in the first step.
Once that has been done, the variant number is purely informative, and
just used to break down the statistics.
In some cases (the one I have in mind is qtype_opaque) then there is a
complex randomisation process, which may lead to several inital variant
numbers acutally giving the same version of the question. In this case
it is nice if the question can update the stored variant number, to make
the statistics more meaningful.
This parallels question_attempt->minfraction, which allows the
fractional mark to go below zere.
This is needed to allow the certainty-base marking behaviours to work
better.
When doing Each attempt builds on last, we need to copy any response
files into a draft file area, and then re-save them.
While writing the unit test for this, I had to deal with a todo in the
question engine so that questions with files in the response could be
unit-tested.
I also found an fixed a bug with qtype_essay_question::is_same_response
and fixed some notices in the existing essay/manual graded unit tests.
This only matters in an obscure edge case, but it is an edge case we hit
with one of the OU question types.
This load data code is processing the results of a LEFT JOIN, so is_null
is the correct logic.
The problem was mostly that, in the past, we did not worry if
question_attempt_step.id changed during regrade (because we deleted the
old step row and inserted a new one). However, now that steps can have
associated files, we can't be that slack, becuase the step id is used as
the file itemid.
So, now, we have to update the existing rows during a regrade. We do
this by having the question engine tell the question_engine_unit_of_work
that the step has first been deleted, and then added back. Then we make
the unit-of-work spot that delete + add = update.
This also means that during regrading, we have to pass around some extra
ids so that new steps know the id of the step they are replacing.
Naturally, this requires some quite trickly logic, so I finally got
around to writing unit tests for question_engine_unit_of_work, which is
a good thing.
Along the way I also got around to renaming
question_attempt->set_number_in_usage, which got missed out when
everthing else was renamed to slot ages ago.
Finally, while working on this code, I noticed and fixed some PHPdoc
comments.