Show account names and roles on cards from scheduled scans

Cards from scheduled scans of editor-saved profiles showed the account's
GUID as its name and no Elev/Ansat badge: the profile editor stored bare
id strings, the scheduler passes them straight to run_scan, and run_scan
normalised them to displayName=<id>, userRole="other". Before 1.7.10 the
scan-start profile overwrite kept repairing profiles to named objects,
which is why this surfaced now.

- run_scan: _enrich_bare_users() looks bare accounts up in the directory
  (list_users, falling back to lookup_user) for name and role.
- M365Connector.build_role_sku_map(): the SKU map /api/users built inline,
  now shared so cards and the account list classify roles identically.
  lookup_user() also returns assignedLicenses.
- Profile editor saves {id, displayName, userRole} (manual accounts keep
  email/manual), like the 💾 save.
- _accountPill: an account_name equal to account_id counts as missing;
  name and role are filled from the loaded user list, so existing cards
  display correctly without a rescan.
- Excel export: the scan's account-name map no longer breaks on bare id
  strings (.get() on a str raised and dropped the whole map).

Tests: tests/test_account_names.py (10); the run_scan test fails on the
old engine with "Collecting emails — guid-henrik…".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
StyxX65andClaude Opus 5.5 committed 2026-09-28 14:40:14 +02:00
1 parent c55db48894
commit 20156ffc93
8 files changed
+283 -37

No files matched your search

+17 -7
View File
@@ -32,21 +32,31 @@ const SOURCE_BADGES = {
// email), falling back to an email-style account_id. Returns '' when there is
// neither a label nor a role to show.
function _accountPill(f) {
const roleBadge =
f.user_role === 'student' ? '<span class="role-badge">' + t('role_student', 'Elev') + '</span>' :
f.user_role === 'staff' ? '<span class="role-badge">' + t('role_staff', 'Ansat') + '</span>' : '';
let role = f.user_role;
let label = f.account_name || '';
if (!label && f.account_id) {
// Scheduled scans of editor-saved profiles stored the account id (a GUID) as
// the name, with role "other" — treat that like a missing name.
if (label && f.account_id && label === String(f.account_id)) label = '';
const needName = !label && f.account_id;
const needRole = role !== 'student' && role !== 'staff';
if (f.account_id && (needName || needRole)) {
const aid = String(f.account_id);
const u = (S._allUsers || []).find(function(u) {
return u.id === f.account_id ||
(u.email && u.email.toLowerCase() === aid.toLowerCase());
});
if (u) label = u.displayName || '';
else if (aid.includes('@')) label = aid; // an email is already human-readable
if (u) {
if (needName) label = u.displayName || '';
if (needRole) role = u.userRole;
} else if (needName && aid.includes('@')) {
label = aid; // an email is already human-readable
}
}
const roleBadge =
role === 'student' ? '<span class="role-badge">' + t('role_student', 'Elev') + '</span>' :
role === 'staff' ? '<span class="role-badge">' + t('role_staff', 'Ansat') + '</span>' : '';
if (!label && !roleBadge) return '';
const title = label || f.user_role || '';
const title = label || role || '';
return '<span class="account-pill" title="' + esc(title) + '">' + roleBadge + (label ? esc(label) : '') + '</span>';
}