can a qformat import substitute existing questions?

can a qformat import substitute existing questions?

by Guido Hornig -
Number of replies: 11
Picture of Plugin developers

I am a little prod about my tiny CSV question import.

I extended the AIKEN format a little bit. The import works. Jeeeah!

Over time the question.CSV will get some updates and changes. The import needs to be redone.

But when the new CSV-file  is imported, the questions are doubled and get new ID numbers.

How can I reset the id count? Even if all questions get deleted, the next import continues counting and I can't stor the new question with their former id numbers. There is no old question left, there are no quizzes until now, but it keeps counting the id.

How to rewrite questions with a new import totally including question id starting from 0?

Or: How can I define the id of a question by my self? The id is also in the CSV-file.

Average of ratings: -
In reply to Guido Hornig

Re: can a qformat import substitute existing questions?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

(Perhaps this could be moved to the quiz forum.)

I think the answer to this is No, but ...

Conceptually, the way question import works is

  1. The import format parses the file, and creates a data structure in Memory which is the same as you gets when the user clicks save on the editing form for that question type.
  2. Then we use the standard function to save that data into the question bank database tables.

Unfortunately, the real code is old and crufty, so it does not work exactly like that. There are some slight differences in the two data-structures, and we don't entirely use the standard save_question method. Instead, question/format.php does some work of its own https://github.com/moodle/moodle/blob/a149d6a177f58aa031c60d6e1b4c0d9f8f2ced22/question/format.php#L397 and only later calls the standard save_question_options to do the rest.

If it did not work like that, then it would be as simple as setting $question->id in your import format, and then the standard code would do an update, not an insert, of the question.

Except that, while it would be simple, it would be extremely dangerous, because people could easily overwrite any question in the question bank. So, if this was made possible, you would need to do various checks:

  1. Check that the question from the import file is at least the same type as the one we are about to overwrite (and possibly some other sanity checks, e.g. in the same category.)
  2. Check that the current user has permission to edit any questions that are due to be overwritten.
  3. Have an extra are-you-sure warning step in the import process, that confirms to you which questions if any would be overwritten if you proceeded with this import.

But, if it was implemented well, I would not object to adding a change like this to Moodle core. But I doubt I will have time to do this myself. I will peer review if anyone else implements it.

Average of ratings: Useful (1)
In reply to Tim Hunt

Re: can a qformat import substitute existing questions?

by Guido Hornig -
Picture of Plugin developers

Thank you Tim for this long answer, and I am afraid you have not enough time to continue this discussion in high intensity.

Its a small installation and nobody else will have the chance to change anything. All questions are of the same type and very simple( singlechoice out of four). I tried $question->id, but it don't write the full question, as you said. I tried to check first, if the question is there, but the id comes from some where else when writing to database.

I can't find the line where the is finally formed.

I will investigate question/format.php again.

Delete via id was my second choice - but this needs change in the lib/questionlib.php because it checks for used questions, and of course - the questions are in use. (but need tiny updates from time to time from a central database outside of moodle) And then I do not get my id stored...

I should call it a sync-import and I know from the forums, that there is some interest in such a sync-question bank function.

Any comment appreciated -

Again: Thank you Tim!

In reply to Guido Hornig

Re: can a qformat import substitute existing questions?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

This is definitely a feature that people are interested in.

The link I gave to format.php on github is the bit of core code you would need to change. Somewhere around the insert into the 'question' table, put in an if statement

if (isset($question->id)) {
    // Do some sanity checks.
    $DB->update_record('question', $question);
} else {
    // Existing code that leads up to ...
    $DB->insert_record('question', $question);
}

I am afraid you will need to work out the details yourself.

Average of ratings: Useful (1)
In reply to Tim Hunt

Re: can a qformat import substitute existing questions?

by Guido Hornig -
Picture of Plugin developers

Tim, you are really helpful.

I tried this, but because of the id= autoincrement, I will never sync.

New Plan: copy the qtype/multichoice and add here the sync-id.

And I can now forbid to change this qtype to normal teachers.


In reply to Guido Hornig

Re: can a qformat import substitute existing questions?

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers
Look again.

The whole piont of using update_record, not insert_record is so that we overwrite the existing row. Hence autoincrement id does not happen.
In reply to Tim Hunt

Re: can a qformat import substitute existing questions?

by Guido Hornig -
Picture of Plugin developers

I do not get controle about  "ID".

I need a lower level of database write or update than $DB->update_record

The problem is the initial import. I always get the ID from the $DB->update_record.

I'll try

// if new
$wantedid$ = $question->id;
$forcedid = $DB->insert_record('question', $question);
$DB
->execute_sql("UPDATE {question} SET id = $wantedid WHERE id = $forcedid" );

but I use moodel 2.8 and there is no execute (for good reason I guess) :-(
In reply to Guido Hornig

Re: can a qformat import substitute existing questions? (solved)

by Guido Hornig -
Picture of Plugin developers

Thanks to the eclipse, but much more to Tim Hunt, I solved it:

1) I set  the $question->id in my qformat plugin comming from the CSV-file

2) then I hacked question/format.php #398

         /*actxc*/
            //$DB->set_debug(true);
 if ( isset($question->id) ) { // only if we got the id from import $givenid = $question->id; $questionexists = $DB->get_record('question', array('id' => $question->id)); if (!$questionexists) { echo $OUTPUT->notification('Neue Frage $givenid', 'notifysuccess'); $forcedid = $DB->insert_record('question', $question); // need to modify the id $DB->set_field_select('question', 'id', $givenid, "id = $forcedid"); } else { echo $OUTPUT->notification('Geänderte Frage $givenid', 'notifysuccess'); $DB->update_record('question', $question);
} } else { // Import without $question->id given $question->id = $DB->insert_record('question', $question); }

  //$DB->set_debug(false); /*actxc end*/
Average of ratings: Useful (1)
In reply to Guido Hornig

Re: can a qformat import substitute existing questions? (solved)

by Ray Morris -

Thank you for posting that code, Guido Hornig.  Also thank you Tim for pointing me here when I asked about the same thing in the Quiz forum https://moodle.org/mod/forum/discuss.php?d=310679 .


I think we'll be using Guido's code. A step toward being able to do this reliably in the future may be to start including question.stamp in the Moodle XML format, and any other format in which it is reasonable to do so.  At least based on memory, I think restore uses stamp for this functionality.


I notice that the checks Tim mentioned, regarding if the user has permission to overwrite a question, etc. are already done for restore using this function:

 public static function prechek_precheck_qbanks_by_level($restoreid, $courseid, $userid, $samesite, $contextlevel) {
It may be possible to re-use the existing function. Failing that, the function is slightly portly, so it may be possible to refactor it into three smaller functions and reuse those. Failing even that, the existing function provides at least a template or example of what checks needs to be done and approximately how they can be done.
In reply to Ray Morris

Re: can a qformat import substitute existing questions? (solved)

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

As I say, this would be a great feature to have, so any patches that pave they way, like "including question.stamp in the Moodle XML format" will be gratefully received.

Note, however, that if you change the format:

  • It needs to stay backwards compatible (but adding extra XML tags is fine. They will get ignored if you import into an older version of Moodle.)
  • There are unit tests for the export and import. Update them too if necessary.
In reply to Guido Hornig

Re: can a qformat import substitute existing questions? (solved)

by Ray Morris -

Guido, what are you using to import the CSV file?