Skip to content

Commit

Permalink
Merge pull request #78 from stopfstedt/72_deal_with_duplicate_user_ac…
Browse files Browse the repository at this point in the history
…counts

deduplicate ilios user records before processing their enrollment.
  • Loading branch information
jrjohnson authored Feb 10, 2025
2 parents 76fc540 + 8a8edf6 commit 50c0dc7
Show file tree
Hide file tree
Showing 3 changed files with 483 additions and 15 deletions.
79 changes: 65 additions & 14 deletions lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,22 @@ public function sync($trace, $courseid = null): int {
);
$users = $ilios->get_users(['id' => $entity->users]);
}

$trace->output(count($users) . " Ilios users found.");

// Filter out records that do not have a campus ID.
$users = array_values(array_filter($users, function ($user) use ($trace) {
$campusid = $user->campusId ?? '';
if ('' === trim($campusid)) {
$trace->output("skipping: Ilios user " . $user->id . " does not have a 'campusId' field.", 1);
return false;
}
return true;
}));

// Deduplicate Ilios user records.
$users = $this->deduplicate_ilios_users($users, $trace);

foreach ($users as $user) {
// Fetch user info if not cached in $iliosusers.
if (!isset($iliosusers[$user->id])) {
Expand All @@ -288,20 +302,11 @@ public function sync($trace, $courseid = null): int {
}

if ($iliosusers[$user->id] === null) {
if (!empty($user->campusId)) {
$trace->output(
"skipping: Cannot find campusId "
. $user->campusId
. " that matches Moodle user field 'idnumber'."
, 1);
} else {
$trace->output(
"skipping: Ilios user "
. $user->id
. " does not have a 'campusId' field."
, 1
);
}
$trace->output(
"skipping: Cannot find campusId "
. $user->campusId
. " that matches Moodle user field 'idnumber'."
, 1);
} else {
$enrolleduserids[] = $userid = $iliosusers[$user->id]['id'];

Expand Down Expand Up @@ -679,6 +684,52 @@ public function restore_user_enrolment(
$this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, ENROL_USER_SUSPENDED);
}
}

/**
* Ensures uniqueness of user accounts by campus ID.
* If there are duplicate user records, they are resolved in the following manner.
* 1. If all duplicates are active user records, then use the most recently created one.
* 2. If all duplicates are inactive user records, then use the most recently created one.
* 3. If there is a mix between active and inactive user records, then use the most recently created active one.
* @param array $users An array of Ilios user records.
* @param progress_trace $trace A logger ("trace") object.
* @return array The given array of Ilios user records, with duplicates removed.
*/
protected function deduplicate_ilios_users(array $users, progress_trace $trace): array {
// Reverse-sort users by their user ID. In other words, from most-recently created to oldest.
array_multisort(array_column($users, 'id'), SORT_DESC, SORT_NUMERIC, $users);
$cache = [];
$duplicatekeys = [];
foreach ($users as $user) {
$key = $user->campusId;
// If this is the first time we encounter this user, cache them and move on.
if (!array_key_exists($key, $cache)) {
$cache[$key] = $user;
continue;
} else {
// Track the duplicate campus id.
$duplicatekeys[] = $key;
}

// And now we're dealing with duplicates.
// If the current user record is active, and the cached duplicate is inactive,
// then replace the cached, inactive user record with the current, active one.
if (!$cache[$key]->enabled && $user->enabled) {
$cache[$key] = $user;
}
}

// Log the duplicate campus IDs.
if (!empty($duplicatekeys)) {
sort($duplicatekeys);
$trace->output(
'Duplicate Ilios user records found, with the following campus IDs: '
. implode(', ', array_unique($duplicatekeys))
);
}

return array_values($cache);
}
}

/**
Expand Down
Loading

0 comments on commit 50c0dc7

Please sign in to comment.