Refactor case handling: update URLs and views to support case_number and case_id interchangeably, enhancing backward compatibility and maintainability

This commit is contained in:
Ross
2025-11-17 11:05:13 +00:00
parent 81cd109449
commit 6e658aca93
8 changed files with 270 additions and 38 deletions
+178 -15
View File
@@ -2755,10 +2755,101 @@ def collection_take_start(request, pk, cid=None, passcode=None):
)
def _case_number_for_collection_and_caseid(collection_pk, case_id):
"""Helper: given a collection PK and a case PK, return the case index (case_number).
Raises Http404 if the case is not found in the collection.
"""
collection = get_object_or_404(CaseCollection, pk=collection_pk)
try:
# attempt to resolve the case object (may raise Http404)
case_obj = get_case_for_case_detail(case_id)
except Http404:
raise
try:
return collection.get_index_of_case(case_obj)
except ValueError:
raise Http404(f"Case {case_id} not in collection {collection_pk}")
@login_required
def redirect_collection_case_details_by_id(request, exam_id, case_id):
case_number = _case_number_for_collection_and_caseid(exam_id, case_id)
return redirect("atlas:collection_case_details", exam_id=exam_id, case_number=case_number)
@login_required
def redirect_collection_case_questions_by_id(request, exam_id, case_id):
case_number = _case_number_for_collection_and_caseid(exam_id, case_id)
return redirect("atlas:collection_case_questions", exam_id=exam_id, case_number=case_number)
@login_required
def redirect_collection_case_priors_by_id(request, exam_id, case_id):
case_number = _case_number_for_collection_and_caseid(exam_id, case_id)
return redirect("atlas:collection_case_priors", exam_id=exam_id, case_number=case_number)
@login_required
def redirect_collection_case_dicom_json_by_id(request, exam_id, case_id):
case_number = _case_number_for_collection_and_caseid(exam_id, case_id)
return redirect("atlas:collection_case_dicom_json", exam_id=exam_id, case_number=case_number)
@login_required
def redirect_collection_case_dicom_json_review_by_id(request, exam_id, case_id):
case_number = _case_number_for_collection_and_caseid(exam_id, case_id)
return redirect("atlas:collection_case_dicom_json_review", exam_id=exam_id, case_number=case_number)
@login_required
def redirect_collection_case_view_by_id(request, pk, case_id):
case_number = _case_number_for_collection_and_caseid(pk, case_id)
return redirect("atlas:collection_case_view", pk=pk, case_number=case_number)
@login_required
def redirect_collection_case_displaysetup_by_id(request, collection_id, case_id):
case_number = _case_number_for_collection_and_caseid(collection_id, case_id)
return redirect("atlas:collection_case_displaysetup", collection_id=collection_id, case_number=case_number)
@login_required
def redirect_collection_case_view_take_by_id(request, pk, case_id, cid, passcode):
case_number = _case_number_for_collection_and_caseid(pk, case_id)
return redirect("atlas:collection_case_view_take", pk=pk, case_number=case_number, cid=cid, passcode=passcode)
@login_required
def redirect_collection_case_view_take_user_by_id(request, pk, case_id):
case_number = _case_number_for_collection_and_caseid(pk, case_id)
return redirect("atlas:collection_case_view_take_user", pk=pk, case_number=case_number)
@login_required
def redirect_collection_case_view_take_user_answers_by_id(request, pk, case_id):
case_number = _case_number_for_collection_and_caseid(pk, case_id)
return redirect("atlas:collection_case_view_take_user_answers", pk=pk, case_number=case_number)
@user_is_collection_author_or_atlas_editor
def collection_case_priors(request, exam_id, case_id):
casedetail = CaseDetail.objects.get(case=case_id, collection=exam_id)
collection = casedetail.collection
# Support either case_number (index) or case_id (case PK) in the URL/kwargs.
# Backwards compatibility: callers may pass case_id; prefer case_number when present.
casedetail = None
collection = None
# Try treating the parameter as a case PK first, falling back to case index.
try:
casedetail = CaseDetail.objects.get(case=case_id, collection=exam_id)
collection = casedetail.collection
except Exception:
collection = get_object_or_404(CaseCollection, pk=exam_id)
try:
case_obj = collection.get_case_by_index(int(case_id))
casedetail = CaseDetail.objects.get(case=case_obj, collection=collection)
except Exception:
raise Http404("Case not found in collection")
if request.htmx:
# Ensure we can render the updated single-card partial and return it so HTMX
@@ -2892,9 +2983,17 @@ def collection_case_priors(request, exam_id, case_id):
@user_is_collection_author_or_atlas_editor
def collection_case_questions(request, exam_id, case_id):
casedetail = CaseDetail.objects.get(case=case_id, collection=exam_id)
collection = casedetail.collection
# Support either case_number (index) or case_id (case PK)
try:
casedetail = CaseDetail.objects.get(case=case_id, collection=exam_id)
collection = casedetail.collection
except Exception:
collection = get_object_or_404(CaseCollection, pk=exam_id)
try:
case_obj = collection.get_case_by_index(int(case_id))
casedetail = CaseDetail.objects.get(case=case_obj, collection=collection)
except Exception:
raise Http404("Case not found in collection")
if not collection.collection_type == "QUE":
raise Http404("Collection not in question mode")
@@ -3005,9 +3104,19 @@ def collection_case_questions(request, exam_id, case_id):
@user_is_collection_author_or_atlas_editor
def collection_case_details(request, exam_id, case_id):
casedetail = CaseDetail.objects.get(case=case_id, collection=exam_id)
collection = casedetail.collection
# Accept either case_number (index into collection) or case_id (case PK).
casedetail = None
collection = None
try:
casedetail = CaseDetail.objects.get(case=case_id, collection=exam_id)
collection = casedetail.collection
except Exception:
collection = get_object_or_404(CaseCollection, pk=exam_id)
try:
case_obj = collection.get_case_by_index(int(case_id))
casedetail = CaseDetail.objects.get(case=case_obj, collection=collection)
except Exception:
raise Http404("Case not found in collection")
if request.method == "POST":
form = CaseDetailForm(request.POST, instance=casedetail)
@@ -3303,12 +3412,14 @@ def collection_case_view_take_answers(
)
def collection_case_view_take_user(request, pk: int, case_number: int):
return collection_case_view_take(request, pk, case_number)
def collection_case_view_take_user(request, pk, case_number=None, case_id=None):
# Accept either case_number (index) or case_id (case PK) and forward both to
# the handler. This allows URLs that use either name to keep working.
return collection_case_view_take(request, pk, case_number=case_number, cid=None, passcode=None, case_id=case_id)
def collection_case_view_take(
request, pk: int, case_number: int, cid=None, passcode=None
request, pk, case_number=None, cid=None, passcode=None, case_id=None
):
"""
Displays the page from which the user takes a case in the collection
@@ -3328,6 +3439,21 @@ def collection_case_view_take(
"""
collection = get_object_or_404(CaseCollection, pk=pk)
# If caller provided a case_id (case PK) instead of a case_number (index),
# translate it into the index used by collection.get_case_by_index().
if case_id is not None and case_number is None:
try:
case_obj = get_case_for_case_detail(case_id)
except Http404:
raise
try:
case_number = collection.get_index_of_case(case_obj)
except ValueError:
raise Http404(f"Case {case_id} not in collection {pk}")
# Ensure we have a case_number to work with
if case_number is None:
raise Http404("No case specified")
form = None
answer: None | CidReportAnswer | UserReportAnswer = None
@@ -3567,7 +3693,7 @@ def collection_case_view_take(
@user_is_collection_author_or_atlas_editor
def collection_case_view(request, pk, case_number):
def collection_case_view(request, pk, case_number=None, case_id=None):
"""Edit/management overview of the case (as part of the collection)
Args:
@@ -3583,6 +3709,19 @@ def collection_case_view(request, pk, case_number):
"""
collection = get_object_or_404(CaseCollection, pk=pk)
# Support either case_number (index) or case_id (case PK) in the URL/kwargs.
if case_id is not None and case_number is None:
try:
case_obj = get_case_for_case_detail(case_id)
except Http404:
raise
try:
case_number = collection.get_index_of_case(case_obj)
except ValueError:
raise Http404(f"Case {case_id} not in collection {pk}")
if case_number is None:
raise Http404("No case specified")
form = None
answer = None
@@ -3635,8 +3774,18 @@ def use_dates_as_descriptions(request, pk):
def collection_case_dicom_json_review(request, exam_id, case_id):
return collection_case_dicom_json(request, exam_id, case_id, review=True)
def collection_case_dicom_json(request, exam_id, case_id, review=False):
casedetail = CaseDetail.objects.get(case=case_id, collection=exam_id)
# Support either case_number (index) or case_id (case PK)
try:
casedetail = CaseDetail.objects.get(case=case_id, collection=exam_id)
except Exception:
collection = get_object_or_404(CaseCollection, pk=exam_id)
try:
case_obj = collection.get_case_by_index(int(case_id))
casedetail = CaseDetail.objects.get(case=case_obj, collection=collection)
except Exception:
raise Http404("Case not found in collection")
if review:
priors = casedetail.caseprior_set.exclude(prior_visibility="NO")
@@ -4517,12 +4666,26 @@ def series_bulk_delete(request):
@user_is_collection_author_or_atlas_editor
@csrf_exempt # Only if you have CSRF issues; otherwise, keep CSRF protection
def collection_case_displaysetup(request, collection_id, case_id):
def collection_case_displaysetup(request, collection_id, case_number=None, case_id=None):
"""
View to set up the display options for a specific case in a collection.
This can include setting which series to display, annotations, etc.
"""
casedetail = get_object_or_404(CaseDetail, case__pk=case_id, collection__pk=collection_id)
# Accept either case_number (index into collection) or case_id (case PK).
casedetail = None
try:
# If a case_id was passed explicitly, try that first
if case_id is not None:
casedetail = get_object_or_404(CaseDetail, case__pk=case_id, collection__pk=collection_id)
else:
# Otherwise the route most likely supplied case_number; try resolving by index
collection = get_object_or_404(CaseCollection, pk=collection_id)
case_obj = collection.get_case_by_index(int(case_number))
casedetail = CaseDetail.objects.get(case=case_obj, collection=collection)
except Http404:
raise
except Exception:
raise Http404("Case not found in collection")
if request.method == "POST":
data = json.loads(request.body.decode("utf-8"))