Add sync functionality for prerequisite users in collection detail
This commit is contained in:
@@ -4,30 +4,30 @@
|
||||
|
||||
{% partialdef casedetails-management-links %}
|
||||
|
||||
(<a href="{% url 'atlas:collection_case_displaysetup' casedetail.collection.pk casedetail.case.pk %}"><i class="bi bi-display" title="Setup default display"></i></a>
|
||||
(<a href="{% url 'atlas:collection_case_displaysetup' casedetail.collection.pk casedetail.case.pk %}"><i class="bi bi-display" title="Setup default display"></i></a>
|
||||
|
||||
{% if casedetail.default_viewerstate %}
|
||||
<i class="bi bi-check text-success" title="This case has a default viewerstate defined"></i>
|
||||
{% endif %}
|
||||
)
|
||||
{% if casedetail.default_viewerstate %}
|
||||
<i class="bi bi-check text-success" title="This case has a default viewerstate defined"></i>
|
||||
{% endif %}
|
||||
)
|
||||
|
||||
(<a href="{% url 'atlas:collection_case_details' casedetail.collection.pk casedetail.case.pk %}"><i class="bi bi-info-square" title="Case details"></i></a>)
|
||||
(<a href="{% url 'atlas:collection_case_details' casedetail.collection.pk casedetail.case.pk %}"><i class="bi bi-info-square" title="Case details"></i></a>)
|
||||
|
||||
{% if collection.collection_type == "QUE" %}
|
||||
(<a href='{% url "atlas:collection_case_questions" casedetail.collection.pk casedetail.case.pk %}'>
|
||||
{% if casedetail.question_schema %}
|
||||
<i class="bi bi-question-square text-success" title="This case has questions defined."></i>
|
||||
{% else %}
|
||||
<i class="bi bi-question-square text-danger" title="This case has no questions defined."></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
)
|
||||
{% endif %}
|
||||
{% if collection.collection_type == "QUE" %}
|
||||
(<a href='{% url "atlas:collection_case_questions" casedetail.collection.pk casedetail.case.pk %}'>
|
||||
{% if casedetail.question_schema %}
|
||||
<i class="bi bi-question-square text-success" title="This case has questions defined."></i>
|
||||
{% else %}
|
||||
<i class="bi bi-question-square text-danger" title="This case has no questions defined."></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
)
|
||||
{% endif %}
|
||||
|
||||
{% if casedetail.case.previous_case %}
|
||||
(<a href='{% url "atlas:collection_case_priors" casedetail.collection.pk casedetail.case.pk %}'>
|
||||
<i class="bi bi-link-45deg" title="Manage priors"></i></a>)
|
||||
{% endif %}
|
||||
{% if casedetail.case.previous_case %}
|
||||
(<a href='{% url "atlas:collection_case_priors" casedetail.collection.pk casedetail.case.pk %}'>
|
||||
<i class="bi bi-link-45deg" title="Manage priors"></i></a>)
|
||||
{% endif %}
|
||||
{% endpartialdef %}
|
||||
|
||||
{% block content %}
|
||||
@@ -42,6 +42,17 @@
|
||||
Collection Type: {{collection.get_collection_type_display}}<br />
|
||||
Self review: {{collection.self_review}}<br />
|
||||
Open access: {{collection.open_access}}<br />
|
||||
{% if collection.prerequisites.exists %}
|
||||
Prerequisite collections:
|
||||
<ul>
|
||||
{% for prereq in collection.prerequisites.all %}
|
||||
<li>
|
||||
<a href="{% url 'atlas:collection_detail' prereq.pk %}">{{ prereq.name }}</a>
|
||||
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -58,14 +69,17 @@
|
||||
<p>Review collection <a href='{% url "atlas:collection_viva" collection.pk %}'>here</a>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Cases</h3>
|
||||
<ol id="full-question-list" class="sortable">
|
||||
{% for casedetail in casesdetails %}
|
||||
<li data-question_pk={{casedetail.case.pk}}>
|
||||
|
||||
|
||||
|
||||
<a href="{% url 'atlas:collection_case_view' pk=collection.pk case_number=forloop.counter0 %}">Case {{forloop.counter}}</a>
|
||||
:
|
||||
:
|
||||
{% if casedetail.case.series.count == 0 %}
|
||||
<i class="bi bi-exclamation-circle text-warning" title="No series attached to this case"></i>
|
||||
{% endif %}
|
||||
@@ -114,6 +128,14 @@
|
||||
<div id="user-list"></div>
|
||||
|
||||
</details>
|
||||
<p>
|
||||
<button
|
||||
title="Sync users from all prerequisite collections into this collection"
|
||||
hx-post="{% url 'atlas:collection_sync_prerequisite_users' collection.pk %}"
|
||||
hx-swap="outerHTML"
|
||||
class="btn btn-sm btn-secondary"
|
||||
>Sync prerequisite users</button>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% include 'exam_overview_js.html' %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
{% if request.user.is_authenticated and valid_user %}
|
||||
User: {{request.user}}<br/>
|
||||
|
||||
|
||||
{% if cid_exam %}
|
||||
Started: {{cid_exam.start_time}} <br/>
|
||||
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
<p>{{ message }}</p>
|
||||
{% if prereq %}
|
||||
<p>
|
||||
You must complete the collection: <a href="{% url 'atlas:collection_take_start' prereq.pk %}">{{ prereq.name }}</a>
|
||||
You must complete the collection:
|
||||
{% if cid and passcode %}
|
||||
<a href="{% url 'atlas:collection_take_start' prereq.pk %}?cid={{ cid }}&passcode={{ passcode }}">{{ prereq.name }}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'atlas:collection_take_start' prereq.pk %}">{{ prereq.name }}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
|
||||
@@ -80,6 +80,11 @@ urlpatterns = [
|
||||
views.collection_take_start,
|
||||
name="collection_take_start",
|
||||
),
|
||||
path(
|
||||
"collection/<int:pk>/sync_prerequisite_users",
|
||||
views.collection_sync_prerequisite_users,
|
||||
name="collection_sync_prerequisite_users",
|
||||
),
|
||||
path("collection/<int:pk>/authors", views.CaseCollectionAuthorUpdate.as_view(), name="collection_authors"),
|
||||
path(
|
||||
"collection/<int:pk>/toggle_results_published",
|
||||
|
||||
+62
-6
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user