[verified] add user subset reset scope
(cherry picked from commit e1e3ec96a2d314d39e35ce2c34f6f67df1c2f182)
This commit is contained in:
parent
7819e6e346
commit
7f7e843563
@ -5,6 +5,7 @@ Clears:
|
||||
- Neo4j: drops ALL databases except system, neo4j (including gaisdata, cc.users.*, cc.institutes.*)
|
||||
- Supabase: deletes ALL data tables except gais_local_authorities and gais_schools
|
||||
- Supabase: deletes all auth users except kcar, then re-seeds kcar profile state
|
||||
- Granular scopes can clear exam corpus, timetable data, or --user-subset seed copies
|
||||
|
||||
Safe invariants (never touched):
|
||||
- kcar auth account
|
||||
@ -261,10 +262,40 @@ def _clear_exam_storage() -> Dict[str, Any]:
|
||||
return {"removed": removed, "buckets": list(by_bucket)}
|
||||
|
||||
|
||||
def _clear_user_subset_files() -> Dict[str, Any]:
|
||||
"""Remove files rows and cc.users storage objects created by --user-subset seeding.
|
||||
|
||||
Reuses the seed/unseed implementation so reset(scope="user-subset") has the
|
||||
same storage-before-row deletion order and idempotency guarantees as
|
||||
seed_exam_corpus.py --unseed. The helper only targets rows marked by the seeder:
|
||||
bucket='cc.users', source='exam-corpus-seed', path LIKE 'exam-marker/%'.
|
||||
"""
|
||||
try:
|
||||
from modules.database.supabase.utils.client import SupabaseServiceRoleClient
|
||||
from modules.database.supabase.utils.storage import StorageAdmin
|
||||
from run.initialization.seed_exam_corpus import LoadReport, _delete_user_subset_files
|
||||
except Exception as exc:
|
||||
logger.warning(f" user-subset clear skipped (import): {exc}")
|
||||
return {"files_rows_deleted": 0, "storage_objects_removed": 0, "errors": [str(exc)]}
|
||||
|
||||
rep = LoadReport()
|
||||
_delete_user_subset_files(
|
||||
SupabaseServiceRoleClient(),
|
||||
StorageAdmin(),
|
||||
exam_codes=None,
|
||||
rep=rep,
|
||||
)
|
||||
return {
|
||||
"files_rows_deleted": rep.unseed_user_files,
|
||||
"storage_objects_removed": rep.unseed_objects,
|
||||
"errors": rep.errors,
|
||||
}
|
||||
|
||||
|
||||
# ─── Main reset ───────────────────────────────────────────────────────────────
|
||||
|
||||
def reset(scope: str = "all") -> Dict[str, Any]:
|
||||
"""Destructive reset. scope ∈ {all, exam-corpus, timetable}.
|
||||
"""Destructive reset. scope ∈ {all, exam-corpus, timetable, user-subset}.
|
||||
|
||||
- all : full wipe (Neo4j + Supabase data + auth users) AND the entire
|
||||
exam-marker subsystem listed below.
|
||||
@ -273,10 +304,12 @@ def reset(scope: str = "all") -> Dict[str, Any]:
|
||||
templates, template layouts, questions, boundaries, response
|
||||
areas, marking batches, student submissions, and mark entries.
|
||||
- timetable : ONLY timetable/calendar materialization tables.
|
||||
- user-subset : ONLY files rows and cc.users storage objects created by
|
||||
seed_exam_corpus.py --user-subset.
|
||||
"""
|
||||
scope = (scope or "all").lower()
|
||||
if scope not in ("all", "exam-corpus", "timetable"):
|
||||
raise ValueError(f"invalid scope {scope!r} (want all|exam-corpus|timetable)")
|
||||
if scope not in ("all", "exam-corpus", "timetable", "user-subset"):
|
||||
raise ValueError(f"invalid scope {scope!r} (want all|exam-corpus|timetable|user-subset)")
|
||||
url, headers = _sb_headers()
|
||||
_assert_reset_allowed(url, scope)
|
||||
|
||||
@ -291,6 +324,11 @@ def reset(scope: str = "all") -> Dict[str, Any]:
|
||||
cleared, failed = _clear_tables(url, headers, TIMETABLE_TABLES)
|
||||
return {"scope": scope, "tables_cleared": cleared, "tables_failed": failed}
|
||||
|
||||
if scope == "user-subset":
|
||||
logger.info("RESET (scope=user-subset) — --user-subset cc.users storage objects and files rows")
|
||||
user_subset = _clear_user_subset_files()
|
||||
return {"scope": scope, "user_subset": user_subset}
|
||||
|
||||
logger.info("=" * 60)
|
||||
logger.info("RESET ENVIRONMENT — full destructive wipe starting")
|
||||
logger.info("=" * 60)
|
||||
|
||||
51
tests/test_reset_environment_user_subset.py
Normal file
51
tests/test_reset_environment_user_subset.py
Normal file
@ -0,0 +1,51 @@
|
||||
from run.initialization import reset_environment
|
||||
|
||||
|
||||
def test_reset_user_subset_scope_only_runs_user_subset_cleanup(monkeypatch):
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(
|
||||
reset_environment,
|
||||
"_sb_headers",
|
||||
lambda: ("http://192.168.0.94:8000", {"Authorization": "Bearer redacted"}),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
reset_environment,
|
||||
"_assert_reset_allowed",
|
||||
lambda url, scope: calls.append(("guard", url, scope)),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
reset_environment,
|
||||
"_clear_user_subset_files",
|
||||
lambda: {"files_rows_deleted": 2, "storage_objects_removed": 2, "errors": []},
|
||||
)
|
||||
|
||||
def fail_if_called(*_args, **_kwargs):
|
||||
raise AssertionError("reset(scope='user-subset') must not clear unrelated tables or databases")
|
||||
|
||||
monkeypatch.setattr(reset_environment, "_clear_tables", fail_if_called)
|
||||
monkeypatch.setattr(reset_environment, "_neo4j_drop_all_non_system", fail_if_called)
|
||||
monkeypatch.setattr(reset_environment, "_clear_exam_storage", fail_if_called)
|
||||
|
||||
result = reset_environment.reset(scope="user-subset")
|
||||
|
||||
assert calls == [("guard", "http://192.168.0.94:8000", "user-subset")]
|
||||
assert result == {
|
||||
"scope": "user-subset",
|
||||
"user_subset": {"files_rows_deleted": 2, "storage_objects_removed": 2, "errors": []},
|
||||
}
|
||||
|
||||
|
||||
def test_reset_accepts_case_insensitive_user_subset_scope(monkeypatch):
|
||||
monkeypatch.setattr(reset_environment, "_sb_headers", lambda: ("http://192.168.0.94:8000", {}))
|
||||
monkeypatch.setattr(reset_environment, "_assert_reset_allowed", lambda *_args, **_kwargs: None)
|
||||
monkeypatch.setattr(
|
||||
reset_environment,
|
||||
"_clear_user_subset_files",
|
||||
lambda: {"files_rows_deleted": 0, "storage_objects_removed": 0, "errors": []},
|
||||
)
|
||||
|
||||
assert reset_environment.reset(scope="USER-SUBSET") == {
|
||||
"scope": "user-subset",
|
||||
"user_subset": {"files_rows_deleted": 0, "storage_objects_removed": 0, "errors": []},
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user