Skip to content

Commit

Permalink
Merge pull request #47 from maths/ignore-cat
Browse files Browse the repository at this point in the history
Ignore category
  • Loading branch information
EJMFarrow authored May 27, 2024
2 parents 312b84d + c06a2e1 commit 4c4e5bd
Show file tree
Hide file tree
Showing 26 changed files with 605 additions and 25 deletions.
11 changes: 11 additions & 0 deletions classes/cli_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@ public function validate_and_clean_args(): void {
static::call_exit();
}
}
if (isset($cliargs['ignorecat'])) {
if (@preg_match($cliargs['ignorecat'], 'zzzzzzzz') === false) {
echo "\nThere is a problem with your regular expression for ignoring categories:\n";
echo error_get_last()["message"] . "\n";
static::call_exit();
}
}
if (isset($cliargs['contextlevel'])) {
switch ($cliargs['contextlevel']) {
case 'system':
Expand Down Expand Up @@ -454,6 +461,7 @@ public static function create_manifest_file(object $manifestcontents, string $te
$manifestcontents->context->instanceid = $questioninfo->instanceid;
$manifestcontents->context->defaultsubcategoryid = $subcategoryid;
$manifestcontents->context->defaultsubdirectory = $subdirectory;
$manifestcontents->context->defaultignorecat = $questioninfo->ignorecat;
$manifestcontents->context->moodleurl = $moodleurl;
}
}
Expand Down Expand Up @@ -720,6 +728,9 @@ public function check_context(object $activity, bool $defaultwarning=false, bool
if ($moodlequestionlist->contextinfo->modulename) {
echo "Quiz: {$moodlequestionlist->contextinfo->modulename}\n";
}
if (isset($activity->ignorecat)) {
echo "Ignoring categories (and their descendants) in form: {$activity->ignorecat}\n";
}
if (isset($activity->subdirectory)) {
echo "Question subdirectory: {$activity->subdirectory}\n";
if ($defaultwarning) {
Expand Down
8 changes: 8 additions & 0 deletions classes/create_repo.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ class create_repo {
* @var int|null
*/
public ?int $qcategoryid = null;
/**
* Regex of categories to ignore.
*
* @var string|null
*/
public ?string $ignorecat;
/**
* Path to actual manifest file.
*
Expand Down Expand Up @@ -132,6 +138,7 @@ public function __construct(cli_helper $clihelper, array $moodleinstances) {
$coursecategory = $arguments['coursecategory'];
$qcategoryid = $arguments['qcategoryid'];
$instanceid = $arguments['instanceid'];
$this->ignorecat = $arguments['ignorecat'];

$this->moodleurl = $moodleinstances[$moodleinstance];

Expand Down Expand Up @@ -161,6 +168,7 @@ public function __construct(cli_helper $clihelper, array $moodleinstances) {
'instanceid' => $instanceid,
'contextonly' => 0,
'qbankentryids[]' => null,
'ignorecat' => $this->ignorecat,
];
$this->listcurlrequest->set_option(CURLOPT_RETURNTRANSFER, true);
$this->listcurlrequest->set_option(CURLOPT_POST, 1);
Expand Down
12 changes: 12 additions & 0 deletions classes/export_repo.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ class export_repo {
* @var string
*/
public string $subcategory;
/**
* Regex of categories to ignore.
*
* @var string|null
*/
public ?string $ignorecat;

/**
* Constructor
Expand Down Expand Up @@ -129,6 +135,11 @@ public function __construct(cli_helper $clihelper, array $moodleinstances) {
// Subcategory will be properly set later.
$this->subcategory = 'top';
}
if ($arguments['ignorecat']) {
$this->ignorecat = $arguments['ignorecat'];
} else {
$this->ignorecat = $this->manifestcontents->context->defaultignorecat ?? null;
}

$this->tempfilepath = str_replace(cli_helper::MANIFEST_FILE,
'_export' . cli_helper::TEMP_MANIFEST_FILE,
Expand Down Expand Up @@ -160,6 +171,7 @@ public function __construct(cli_helper $clihelper, array $moodleinstances) {
'instanceid' => $this->manifestcontents->context->instanceid,
'contextonly' => 0,
'qbankentryids[]' => null,
'ignorecat' => $this->ignorecat,
];
$this->listcurlrequest->set_option(CURLOPT_RETURNTRANSFER, true);
$this->listcurlrequest->set_option(CURLOPT_POST, 1);
Expand Down
1 change: 1 addition & 0 deletions classes/export_trait.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ public function export_to_repo_main_process(object $moodlequestionlist):void {
'coursecategory' => $this->listpostsettings['coursecategory'],
'instanceid' => $this->listpostsettings['instanceid'],
'format' => 'xml',
'ignorecat' => $this->ignorecat,
];
fwrite($tempfile, json_encode($fileoutput) . "\n");
}
Expand Down
19 changes: 14 additions & 5 deletions classes/external/get_question_list.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ public static function execute_parameters() {
'instanceid' => new external_value(PARAM_SEQUENCE, 'Course, module or coursecategory id'),
'contextonly' => new external_value(PARAM_BOOL, 'Only return context info?'),
'qbankentryids' => new external_multiple_structure(
new external_value(PARAM_SEQUENCE, 'QUestion bank entry id')
new external_value(PARAM_SEQUENCE, 'Question bank entry id')
),
'ignorecat' => new external_value(PARAM_TEXT, 'Regex of categories to ignore'),
]);
}

Expand All @@ -75,6 +76,7 @@ public static function execute_returns():external_single_structure {
'instanceid' => new external_value(PARAM_SEQUENCE, 'id of course category, course or module'),
'qcategoryname' => new external_value(PARAM_TEXT, 'name of question category'),
'qcategoryid' => new external_value(PARAM_SEQUENCE, 'id of question category'),
'ignorecat' => new external_value(PARAM_TEXT, 'regex of categories ignored'),
]),
'questions' => new external_multiple_structure(
new external_single_structure([
Expand All @@ -100,13 +102,14 @@ public static function execute_returns():external_single_structure {
* for course level) to search for questions (supercedes $coursename, $modulename & $coursecategory)
* @param bool $contextonly Only return info on context and not questions
* @param array|null $qbankentryids Array of qbankentryids to check
* @param string|null $ignorecat Regex of categories to ignore (along with their descendants)
* @return object containing context info and an array of question data
*/
public static function execute(?string $qcategoryname,
int $contextlevel, ?string $coursename = null, ?string $modulename = null,
?string $coursecategory = null, ?string $qcategoryid = null,
?string $instanceid = null, bool $contextonly = false,
?array $qbankentryids = ['']):object {
?array $qbankentryids = [''], ?string $ignorecat = null):object {
global $CFG, $DB;
$params = self::validate_parameters(self::execute_parameters(), [
'qcategoryname' => $qcategoryname,
Expand All @@ -118,6 +121,7 @@ public static function execute(?string $qcategoryname,
'instanceid' => $instanceid,
'contextonly' => $contextonly,
'qbankentryids' => $qbankentryids,
'ignorecat' => $ignorecat,
]);
$contextinfo = get_context($params['contextlevel'], $params['coursecategory'],
$params['coursename'], $params['modulename'],
Expand All @@ -137,6 +141,7 @@ public static function execute(?string $qcategoryname,
$response->questions = [];
$response->contextinfo->qcategoryname = '';
$response->contextinfo->qcategoryid = null;
$response->contextinfo->ignorecat = $ignorecat;

if (count($qbankentryids) === 1 && $qbankentryids[0] === '') {
if (is_null($qcategoryid) || $qcategoryid === '') {
Expand Down Expand Up @@ -176,7 +181,7 @@ public static function execute(?string $qcategoryname,
return $response;
}

$categoriestosearch = array_merge([$category], self::get_category_descendants($category->id));
$categoriestosearch = array_merge([$category], self::get_category_descendants($category->id, $params['ignorecat']));

$categoryids = array_map(fn($catinfo) => $catinfo->id, $categoriestosearch);

Expand Down Expand Up @@ -217,15 +222,19 @@ public static function execute(?string $qcategoryname,
* Recursive function to return the ids of all the question categories below a given category.
*
* @param int $parentid ID of the category to search below
* @param string|null $ignorecat Regex of categories to ignore (along with their descendants)
* @return array of question categories
*/
public static function get_category_descendants(int $parentid):array {
public static function get_category_descendants(int $parentid, ?string $ignorecat):array {
global $DB;
$children = $DB->get_records('question_categories', ['parent' => $parentid], null, 'id, parent, name');
if ($ignorecat) {
$children = array_filter($children, fn($ch) => !preg_match($ignorecat, $ch->name));
}
// Copy array.
$descendants = array_merge([], $children);
foreach ($children as $child) {
$childdescendants = self::get_category_descendants($child->id);
$childdescendants = self::get_category_descendants($child->id, $ignorecat);
$descendants = array_merge($descendants, $childdescendants);
}
return $descendants;
Expand Down
5 changes: 1 addition & 4 deletions classes/external/import_question.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,6 @@ public static function execute_returns() {
/**
* Import a question from XML file
*
* Initially just create a new one in Moodle DB. Will need to expand to
* use importasversion if question already exists.
*
* @param string|null $questionbankentryid questionbankentry id
* @param string|null $importedversion last exported version of question
* @param string|null $exportedversion last imported version of question
Expand Down Expand Up @@ -157,7 +154,7 @@ public static function execute(?string $questionbankentryid, ?string $importedve

$iscategory = false;
if ($params['questionbankentryid']) {
$question = question_bank::load_question_data($questiondata->questionid);
$question = question_bank::load_question($questiondata->questionid);
} else if (isset($params['qcategoryid']) && $params['qcategoryid'] !== '') {
$category = $DB->get_record('question_categories', ['id' => $qcategoryid]);
$qformat->setCategory($category);
Expand Down
42 changes: 39 additions & 3 deletions classes/import_repo.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ class import_repo {
* @var string
*/
public string $subdirectory;
/**
* Regex of categories to ignore.
*
* @var string|null
*/
public ?string $ignorecat;
/**
* Are we using git?.
* Set in config. Adds commit hash to manifest.
Expand Down Expand Up @@ -184,6 +190,7 @@ public function __construct(cli_helper $clihelper, array $moodleinstances) {
$modulename = $arguments['modulename'];
$coursecategory = $arguments['coursecategory'];
$instanceid = $arguments['instanceid'];
$this->ignorecat = $arguments['ignorecat'];
$this->usegit = $arguments['usegit'];

$this->moodleurl = $moodleinstances[$moodleinstance];
Expand Down Expand Up @@ -237,6 +244,7 @@ public function __construct(cli_helper $clihelper, array $moodleinstances) {
'instanceid' => $instanceid,
'contextonly' => 0,
'qbankentryids[]' => null,
'ignorecat' => $this->ignorecat,
];
$this->listcurlrequest->set_option(CURLOPT_RETURNTRANSFER, true);
$this->listcurlrequest->set_option(CURLOPT_POST, 1);
Expand All @@ -257,7 +265,7 @@ public function __construct(cli_helper $clihelper, array $moodleinstances) {
$this->listpostsettings['instanceid'] = $instanceinfo->contextinfo->instanceid;
$this->listpostsettings['coursename'] = $instanceinfo->contextinfo->coursename;
$this->listpostsettings['modulename'] = $instanceinfo->contextinfo->modulename;
$this->listpostsettings['coursecategory'] = $instanceinfo->contextinfo->categoryname;
$this->listpostsettings['ignorecat'] = $this->ignorecat;
}
$this->tempfilepath = str_replace(cli_helper::MANIFEST_FILE,
'_import' . cli_helper::TEMP_MANIFEST_FILE,
Expand Down Expand Up @@ -300,6 +308,9 @@ public function __construct(cli_helper $clihelper, array $moodleinstances) {
$this->listpostsettings['coursename'] = $this->manifestcontents->context->coursename;
$this->listpostsettings['modulename'] = $this->manifestcontents->context->modulename;
$this->listpostsettings['coursecategory'] = $this->manifestcontents->context->coursecategory;
$this->ignorecat = isset($arguments['ignorecat']) ?
$arguments['ignorecat'] : $this->manifestcontents->context->defaultignorecat ?? null;
$this->listpostsettings['ignorecat'] = $this->ignorecat;
$this->listcurlrequest->set_option(CURLOPT_POSTFIELDS, $this->listpostsettings);
if ($arguments['subdirectory']) {
$this->subdirectory = $arguments['subdirectory'];
Expand Down Expand Up @@ -381,6 +392,22 @@ public function import_categories():void {
if (pathinfo($repoitem, PATHINFO_EXTENSION) === 'xml'
&& pathinfo($repoitem, PATHINFO_FILENAME) === cli_helper::CATEGORY_FILE) {
$this->postsettings['qcategoryname'] = '';
if ($this->ignorecat) {
$qcategoryname = cli_helper::get_question_category_from_file($repoitem);
if ($qcategoryname) {
$catparts = explode('/', $qcategoryname);
foreach ($catparts as $catpart) {
if (preg_match($this->ignorecat, $catpart)) {
continue 2;
}
}
} else {
echo "\n{$repoitem->getPathname()} not imported?\n";
echo "Stopping before trying to import questions.\n";
$this->call_exit();
}
}

$this->upload_file($repoitem);
$this->curlrequest->set_option(CURLOPT_POSTFIELDS, $this->postsettings);
$response = $this->curlrequest->execute();
Expand All @@ -390,15 +417,15 @@ public function import_categories():void {
echo $response . "\n";
echo "{$repoitem->getPathname()} not imported?\n";
echo "Stopping before trying to import questions.\n";
$this->call_exit();;
$this->call_exit();
} else if (property_exists($responsejson, 'exception')) {
echo "{$responsejson->message}\n";
if (property_exists($responsejson, 'debuginfo')) {
echo "{$responsejson->debuginfo}\n";
}
echo "{$repoitem->getPathname()} not imported.\n";
echo "Stopping before trying to import questions.\n";
$this->call_exit();;
$this->call_exit();
}
}
}
Expand Down Expand Up @@ -479,6 +506,14 @@ public function import_questions() {
$this->postsettings['qcategoryname'] = $qcategoryname;
// Path of file (without filename) relative to base $directory.
if ($qcategoryname) {
if ($this->ignorecat) {
$catparts = explode('/', $qcategoryname);
foreach ($catparts as $catpart) {
if (preg_match($this->ignorecat, $catpart)) {
continue 2;
}
}
}
$relpath = str_replace(dirname($this->manifestpath), '', $repoitem->getPathname());
$relpath = str_replace( '\\', '/', $relpath);
$existingentry = $existingentries[$relpath] ?? false;
Expand Down Expand Up @@ -526,6 +561,7 @@ public function import_questions() {
'coursecategory' => $this->postsettings['coursecategory'],
'instanceid' => $this->postsettings['instanceid'],
'format' => 'xml',
'ignorecat' => $this->ignorecat,
];
if ($existingentry && isset($existingentry->currentcommit)) {
$fileoutput['moodlecommit'] = $existingentry->currentcommit;
Expand Down
9 changes: 9 additions & 0 deletions cli/config_sample.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,12 @@ $manifestpath = null;

// Are you using Git and wanting repository checks performed automatically?
$usegit = true;

// Category regex to ignore.
// Set this to ignore categories matching the regexp and the descendants of those categories.
// Example: '/^.*DO_NOT_SHARE$/' will ignore 'DO_NOT_SHARE', 'really DO_NOT_SHARE' but
// not 'DO_NOT_SHARE really', etc.
// Alternatively, use -x CLI paramater.
// If this setting is null and -x is not used, the setting
// saved in the manifest file during repo creation will be used on import/export.
$ignorecat = null;
8 changes: 8 additions & 0 deletions cli/createrepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@
'variable' => 'usegit',
'valuerequired' => false,
],
[
'longopt' => 'ignorecat',
'shortopt' => 'x',
'description' => 'Regex of categories to ignore',
'default' => $ignorecat,
'variable' => 'ignorecat',
'valuerequired' => true,
],
];

if (!function_exists('simplexml_load_file')) {
Expand Down
8 changes: 8 additions & 0 deletions cli/deletefrommoodle.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@
'variable' => 'usegit',
'valuerequired' => false,
],
[
'longopt' => 'ignorecat',
'shortopt' => 'x',
'description' => 'Regex of categories to ignore',
'default' => $ignorecat,
'variable' => 'ignorecat',
'valuerequired' => true,
],
];

$clihelper = new cli_helper($options);
Expand Down
8 changes: 8 additions & 0 deletions cli/exportrepofrommoodle.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@
'variable' => 'usegit',
'valuerequired' => false,
],
[
'longopt' => 'ignorecat',
'shortopt' => 'x',
'description' => 'Regex of categories to ignore',
'default' => $ignorecat,
'variable' => 'ignorecat',
'valuerequired' => true,
],
];

if (!function_exists('simplexml_load_file')) {
Expand Down
8 changes: 8 additions & 0 deletions cli/importrepotomoodle.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@
'variable' => 'usegit',
'valuerequired' => false,
],
[
'longopt' => 'ignorecat',
'shortopt' => 'x',
'description' => 'Regex of categories to ignore',
'default' => $ignorecat,
'variable' => 'ignorecat',
'valuerequired' => true,
],
];

if (!function_exists('simplexml_load_file')) {
Expand Down
1 change: 1 addition & 0 deletions doc/createrepo.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
|n|instanceid|Numerical id of the course, module of course category.
|t|token|Security token for webservice.
|h|help|
|x|ignorecat|Regex of categories to ignore

### Example 1:

Expand Down
Loading

0 comments on commit 4c4e5bd

Please sign in to comment.