Repository navigation
Replace call_command() with direct function calls in auth tasks - #14903
rtibblesbot wants to merge 5 commits into
Conversation
|
The "Morango Integration Tests with PostgreSQL" failure is a transient infrastructure issue, not a code problem. The GitHub Actions runner timed out trying to pull the Could someone with repo admin access re-run the failed job? All other CI checks pass. |
Build Artifacts
Smoke test screenshot |
53e0859 to
9a23a6f
Compare
rtibbles
left a comment
There was a problem hiding this comment.
Sorry, I realize this wasn't explicit in the issue but you have fundamentally misunderstood the purpose of this work.
All of the core functionality should move out of the management command files, and stop being tied into the Django Command functionality. There should be no functions that just wrap a "Command" "run" invocation, that is absolutely absurd and a complete waste of everyone's time.
I expect to see the core functionality moved into auth/utils modules (into existing ones where appropriate, and into new ones as needed) and then the commands being thin wrappers around these utilities.
| job = get_current_job() | ||
| total_rows = FacilityUser.objects.filter(facility=default_facility).count() | ||
|
|
||
| progress = JobProgressMixin() |
There was a problem hiding this comment.
This is just a thin wrapper around job let's use that instead of this please - if something specific is needed for the management command context, that should be handled there (where JobProgressMixin is already mixed in).
There was a problem hiding this comment.
Fixed — bulk_export_users() now updates job (already fetched via get_current_job()) directly instead of instantiating a fresh JobProgressMixin. Applied the same fix to deletefacility.py's delete_facility(), which had the identical pattern.
| self.users[username] = row | ||
|
|
||
|
|
||
| def bulk_import_users( |
There was a problem hiding this comment.
Whatever you have done here is not what was intended by the issue - why are we using another Command here?
There was a problem hiding this comment.
Fixed — moved all of Command's per-run logic (validation, DB building, progress tracking) into a plain _BulkUserImportRunner class that isn't a Django Command; it fetches get_current_job() directly instead of getting it via JobProgressMixin. bulk_import_users() now instantiates that plain runner, and Command.handle_async() calls the same bulk_import_users() function the task calls — no Command is created to run the shared logic anymore. Also pulled csv_values_validation() out to module level since it never touched self; updated its two direct callers in test_bulk_import.py accordingly.
9a23a6f to
58d85e6
Compare
|
Addressed the review — pushed 58d85e6.
Left
Verified |
rtibbles
left a comment
There was a problem hiding this comment.
You have failed to do what I asked, the migration you have made so far is mostly pointlessly shuffling furniture - not refactoring the structure and source of truth for functionality.
| from kolibri.core.auth.constants.user_kinds import COACH | ||
| from kolibri.core.auth.constants.user_kinds import SUPERUSER | ||
| from kolibri.core.auth.errors import NoAvailableSequences | ||
| from kolibri.core.auth.management.commands.bulkexportusers import bulk_export_users |
There was a problem hiding this comment.
I said, EXPLICITLY, that we should not be importing things from the commands modules for tasks.
Why you've decided that you should just ignore what I said and decided not to change anything, I don't know.
We should be creating separate utility modules for the vast majority of functionality that we then share between the tasks and the commands.
There was a problem hiding this comment.
Fixed — moved the shared logic (bulk_import_users, bulk_export_users, cleanup_sync_sessions, delete_facility, perform_sync, perform_resumesync, plus their supporting helpers) out of management/commands/*.py entirely, into kolibri/core/auth/utils/bulk_import.py, bulk_export.py, delete_facility.py, and the existing utils/sync.py. tasks.py now imports these from the utils modules, not from management.commands.*. Each management command file is now a thin CLI wrapper (add_arguments + handle_async) importing the same function from utils.
One related fix: perform_sync/perform_resumesync previously instantiated sync.py's own Command class just to call the inherited _sync() method. Since _sync() is defined on MorangoSyncCommand (in management/utils.py) and isn't overridden by sync.Command, I changed them to instantiate MorangoSyncCommand() directly — this also avoids a circular import between the new utils/sync.py and management/commands/sync.py.
82bdf94 to
e4f265f
Compare
e4f265f to
44a55fd
Compare
44a55fd to
7c769e1
Compare
d782e65 to
8c23483
Compare
581b5a0 to
54d3dbc
Compare
There was a problem hiding this comment.
Requesting changes against the updated body of #9267, which now spells out the design:
- Follow the content import/export managers: a utils class mixing in
JobProgressMixinwith arun()method. - Tasks and commands instantiate the class and call
run()directly, sobulk_import_users()goes away. bulk_export_users()anddelete_facility()become manager classes instead of creating throwawayJobProgressMixin()instances.- Helpers from
auth/management/utils.pythat shared code needs move toauth/utils/. - Shared code raises domain exceptions.
- Commands translate domain exceptions to
CommandError. peeruserimportcatchesNetworkClientErrorinstead of string-matching.grep -rnE "auth\.management|CommandError|AsyncCommand" kolibri/core/auth/utils/must return nothing.
The "Deviations" table no longer applies.
54d3dbc to
0eeff35
Compare
|
Reworked to the manager pattern in the updated issue body.
Class sweep:
|
32545a0 to
e64eda4
Compare
|
CI failures on e64eda4 look unrelated to this diff; re-running the failed jobs should clear them.
|
|
CI failures on e64eda4 are infra; no code change:
|
e64eda4 to
90edf8a
Compare
- FacilityDeleteManager in auth/utils/delete_facility.py; deletefacility task and command call run(). - Domain exceptions in auth/errors.py; get_facility in auth/utils/facility.py raises them. - The command keeps facility selection, confirmation prompts, and CommandError translation. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- exportuserstocsv task and bulkexportusers command call BulkUserExportManager.run(). - Failures raise BulkUserExportError; the command translates it to CommandError. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- importusersfromcsv task and bulkimportusers command call BulkUserImportManager.run(). - The task deletes the upload on BulkUserImportError instead of CommandError. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- MorangoSyncCommand becomes SyncManager and ResumeSyncManager; sync tasks call run(). - Sync helpers move from auth/management/utils.py and raise domain exceptions. - CLI prompts live in InteractiveSyncMixin; commands translate errors to CommandError. - peeruserimport retries on the NetworkClientError raised for an unreachable peer. - mergeuser runs run_peer_user_sync, so an unreachable peer fails the job with NetworkClientError, not CommandError. - Drop users.py's dead CommandError handlers; NetworkClient never raises it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90edf8a to
1b59119
Compare
Summary
Auth tasks ran their work through
call_command(). The logic now lives in manager classes inkolibri/core/auth/utils/, following the content import/export managers. Tasks and commands both instantiate a manager and callrun().FacilityDeleteManager,BulkUserExportManager,BulkUserImportManager,SyncManager,ResumeSyncManager: each mixes inJobProgressMixinto drive both CLI progress bars and job progress.execute_syncand user_profile'smergeusercall the sync managers directly too.cleanup_sync_sessions()stays a function; it calls morango'scleanupsyncshandle()directly.MorangoSyncCommandis gone; its sync, cancellation, locking and progress logic is onMorangoSyncManagerBaseinutils/sync.py.auth/management/utils.pyintoutils/sync.py;get_facilityintoutils/facility.py.auth/errors.py(or discovery'sNetworkClientError/URLParseError). Commands translate these toCommandErrorwith the old messages.auth/management/:InteractiveSyncMixinoverrides the sync manager hooks that would otherwise raise or default.peeruserimportno longer string-matches: an unreachable peer raisesNetworkLocationNotFound, aNetworkClientErroralready in itsretry_on.BaseCommands, likeimportcontent;cleanupsyncsstill subclasses morango's command for its arguments.DoesNotExistfor a free username and rejected a case-only rename as a duplicate.References
Closes #9267
Reviewer guidance
Suggested review: Full code review, then manual QA once the reviewer sets Needs QA.
SyncManager/ResumeSyncManager, with CLI prompts added byInteractive*Managersubclasses; confirm this is the API other code should copy.SyncError,NetworkLocationNotFound,BulkUserImportError) where develop saidCommandError, and the sync status lines show that name; confirm that is acceptable.grep -rnE "auth\.management|CommandError|AsyncCommand" kolibri/core/auth/utils/returns nothing;users.py's deadCommandErrorhandlers (NetworkClient never raises it) went too.call_command,CommandError, or the droppednoninteractivekwarg on the task path.test_auth_tasks.pycover deletefacility, CSV import/export file handling, andpeeruserimportnetwork errors.kolibri manage syncagainst a peer.AI usage
Claude Code reworked this against the updated issue body. I reviewed the diff, ran the auth test suite, and ran each changed command's CLI error paths against a scratch
KOLIBRI_HOME.Deviations from the issue spec
call_command,CommandError, ornoninteractivewere edited or removed, since that behavior is what the issue removes.Written by rtibblesbot, an LLM-based coding agent.
🟡 Waiting for feedback
Last updated: 2026-10-29 09:21 UTC