-
+
Case {{forloop.counter}}
- :
+ :
{% if casedetail.case.series.count == 0 %}
{% endif %}
@@ -114,6 +128,14 @@
+
+
+
{% endif %}
{% include 'exam_overview_js.html' %}
{% endblock %}
diff --git a/atlas/templates/atlas/collection_take_start.html b/atlas/templates/atlas/collection_take_start.html
index c09f4b46..90ca5c4d 100644
--- a/atlas/templates/atlas/collection_take_start.html
+++ b/atlas/templates/atlas/collection_take_start.html
@@ -6,6 +6,7 @@
{% if request.user.is_authenticated and valid_user %}
User: {{request.user}}
+
{% if cid_exam %}
Started: {{cid_exam.start_time}}
diff --git a/atlas/templates/atlas/prerequisite_required.html b/atlas/templates/atlas/prerequisite_required.html
index cc13e0a9..a03cfc86 100644
--- a/atlas/templates/atlas/prerequisite_required.html
+++ b/atlas/templates/atlas/prerequisite_required.html
@@ -5,7 +5,12 @@
{{ message }}
{% if prereq %}
- You must complete the collection: {{ prereq.name }}
+ You must complete the collection:
+ {% if cid and passcode %}
+ {{ prereq.name }}
+ {% else %}
+ {{ prereq.name }}
+ {% endif %}
{% endif %}
diff --git a/atlas/urls.py b/atlas/urls.py
index 13e136cc..e61c4adf 100755
--- a/atlas/urls.py
+++ b/atlas/urls.py
@@ -80,6 +80,11 @@ urlpatterns = [
views.collection_take_start,
name="collection_take_start",
),
+ path(
+ "collection//sync_prerequisite_users",
+ views.collection_sync_prerequisite_users,
+ name="collection_sync_prerequisite_users",
+ ),
path("collection//authors", views.CaseCollectionAuthorUpdate.as_view(), name="collection_authors"),
path(
"collection//toggle_results_published",
diff --git a/atlas/views.py b/atlas/views.py
index 3f109380..552aba99 100755
--- a/atlas/views.py
+++ b/atlas/views.py
@@ -2189,9 +2189,7 @@ def collection_take_start(request, pk, cid=None, passcode=None):
Returns:
_type_: _description_
"""
- print(f"Collection take start: {pk}, cid: {cid}, passcode: {passcode}")
collection = get_object_or_404(CaseCollection, pk=pk)
- print(f"Collection: {collection}")
try:
collection.check_user_can_take(cid, passcode, request.user)
@@ -2204,7 +2202,7 @@ def collection_take_start(request, pk, cid=None, passcode=None):
return render(
request,
'atlas/prerequisite_required.html',
- {'message': str(e), 'prereq': prereq},
+ {'message': str(e), 'prereq': prereq, 'cid': cid, 'passcode': passcode},
)
@@ -2626,7 +2624,7 @@ def collection_take_overview(
collection.check_user_can_take(cid, passcode, request.user)
except PrerequisiteRequired as e:
prereq = getattr(e, 'prereq', None)
- return render(request, 'atlas/prerequisite_required.html', {'message': str(e), 'prereq': prereq})
+ return render(request, 'atlas/prerequisite_required.html', {'message': str(e), 'prereq': prereq, 'cid': cid, 'passcode': passcode})
cid_user_exam = collection.get_or_create_cid_user_exam(
cid=cid, user_user=request.user
@@ -2703,7 +2701,7 @@ def collection_case_view_take_answers(
collection.check_user_can_take(cid, passcode, request.user)
except PrerequisiteRequired as e:
prereq = getattr(e, 'prereq', None)
- return render(request, 'atlas/prerequisite_required.html', {'message': str(e), 'prereq': prereq})
+ return render(request, 'atlas/prerequisite_required.html', {'message': str(e), 'prereq': prereq, 'cid': cid, 'passcode': passcode})
case, case_count = collection.get_case_by_index(case_number, case_count=True)
@@ -2756,7 +2754,7 @@ def collection_case_view_take(
collection.check_user_can_take(cid, passcode, request.user)
except PrerequisiteRequired as e:
prereq = getattr(e, 'prereq', None)
- return render(request, 'atlas/prerequisite_required.html', {'message': str(e), 'prereq': prereq})
+ return render(request, 'atlas/prerequisite_required.html', {'message': str(e), 'prereq': prereq, 'cid': cid, 'passcode': passcode})
case, case_count = collection.get_case_by_index(case_number, case_count=True)
@@ -3563,6 +3561,64 @@ def collection_question_schemas(request, exam_id: int):
)
+@user_is_collection_author_or_atlas_editor
+def collection_sync_prerequisite_users(request, pk: int):
+ """Copy valid cid_users and user_users from all prerequisite collections into this collection.
+
+ This endpoint is intended to be called via HTMX from the collection detail page.
+ """
+ if not request.htmx:
+ raise Http404("Invalid request")
+
+ collection = get_object_or_404(CaseCollection, pk=pk)
+
+ # Collect the closure of collections: the target collection and all prerequisites (recursively)
+ to_visit = [collection]
+ visited = {}
+ while to_visit:
+ col = to_visit.pop()
+ if col.pk in visited:
+ continue
+ visited[col.pk] = col
+ for p in col.prerequisites.all():
+ if p.pk not in visited:
+ to_visit.append(p)
+
+ collections = list(visited.values())
+
+ # Compute the union of all cid users and user users across the set
+ union_cid_users = set()
+ union_user_users = set()
+ for col in collections:
+ union_cid_users.update(list(col.valid_cid_users.all()))
+ union_user_users.update(list(col.valid_user_users.all()))
+
+ # Apply the union to each collection and count additions
+ total_added_cid = 0
+ total_added_user = 0
+ per_collection_added = []
+ for col in collections:
+ added_cid = 0
+ added_user = 0
+ for cid_user in union_cid_users:
+ if not col.valid_cid_users.filter(pk=cid_user.pk).exists():
+ col.valid_cid_users.add(cid_user)
+ added_cid += 1
+ for user in union_user_users:
+ if not col.valid_user_users.filter(pk=user.pk).exists():
+ col.valid_user_users.add(user)
+ added_user += 1
+ if added_cid or added_user:
+ col.save()
+ total_added_cid += added_cid
+ total_added_user += added_user
+ per_collection_added.append((col.pk, added_cid, added_user))
+
+ return HttpResponse(
+ f"Two-way sync complete — total added: {total_added_cid} cid(s), {total_added_user} user(s)."
+ )
+
+
def collection_reset_answers_user_list(request, exam_id: int):
if request.htmx:
collection = get_object_or_404(CaseCollection, pk=exam_id)