Enhance self-review functionality with new detail views, improved UI, and dynamic case management
This commit is contained in:
@@ -1,78 +1,139 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if cid is not None %}
|
<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-3">
|
||||||
CID: {{cid}}
|
<div>
|
||||||
{% endif %}
|
<h2 class="h4 mb-1">
|
||||||
|
{{ collection.name }}
|
||||||
<h2>Collection: {{collection.name}}
|
|
||||||
|
|
||||||
{% if collection.in_review_mode or cid_user_exam.completed %}
|
{% if collection.in_review_mode or cid_user_exam.completed %}
|
||||||
<span class="stamp-white">REVIEW</span>
|
<span class="badge bg-light text-dark ms-2">Review</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h2>
|
</h2>
|
||||||
|
<div class="text-muted small">
|
||||||
|
|
||||||
{% if not collection.review_only %}
|
|
||||||
{{answer_count}} out of {{collection_length}} cases answered. Click to go to case.
|
|
||||||
{% endif %}
|
|
||||||
<div class="sba-finish-list">
|
|
||||||
<ul>
|
|
||||||
{% for question, answer, self_review in question_answer_tuples %}
|
|
||||||
<li> <span class="{% if not collection.review_only and not answer %}unanswered{% endif %}">
|
|
||||||
<a href="
|
|
||||||
{% if cid is not None %}
|
{% if cid is not None %}
|
||||||
{% url 'atlas:collection_case_view_take' pk=collection.id case_number=forloop.counter0 cid=cid passcode=passcode %}
|
CID: <strong>{{ cid }}</strong>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% url 'atlas:collection_case_view_take_user' pk=collection.id case_number=forloop.counter0 %}
|
Your collection overview
|
||||||
{% endif %}
|
{% endif %}
|
||||||
">
|
</div>
|
||||||
Case: {{forloop.counter}}
|
</div>
|
||||||
|
<div class="card border-secondary" style="min-width: 280px;">
|
||||||
{% if answer.completed or cid_user_exam.completed %}
|
<div class="card-body py-2 px-3">
|
||||||
<span title="You have completed this case"><i class="bi bi-check-square-fill"></i>
|
<div class="small text-muted">Progress</div>
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if self_review %} <span class="self-review-complete" title="You have reviewed this case."> <i class="bi-card-checklist"></i></span>{% endif %}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<br/>
|
|
||||||
{% if not collection.review_only %}
|
{% if not collection.review_only %}
|
||||||
{% if not answer %}
|
<div class="fw-semibold">{{ answer_count }} / {{ collection_length }} answered</div>
|
||||||
No answer
|
<div class="progress mt-2" style="height: 8px;">
|
||||||
|
<div class="progress-bar" role="progressbar" style="width: {% widthratio answer_count collection_length 100 %}%"></div>
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<details><summary>Answered</summary>
|
<div class="fw-semibold">Review-only collection</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="mt-2 small">Started: {{ cid_user_exam.start_time }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if aswer.answer %}
|
<div class="list-group mb-3">
|
||||||
{{answer.answer}}
|
{% for question, answer, self_review in question_answer_tuples %}
|
||||||
|
<div class="list-group-item {% if not answer %}bg-danger-subtle border-danger-subtle{% endif %}">
|
||||||
|
<div class="d-flex flex-wrap justify-content-between align-items-start gap-2">
|
||||||
|
<div>
|
||||||
|
<div class="fw-semibold">
|
||||||
|
Case {{ forloop.counter }}{% if collection.show_title_post %}: {{ question.case.title|default:question.case.pk|truncatechars:70 }}{% endif %}
|
||||||
|
{% if answer and answer.completed or cid_user_exam.completed %}
|
||||||
|
<span class="badge bg-success-subtle text-success-emphasis ms-2">Completed</span>
|
||||||
|
{% elif answer %}
|
||||||
|
<span class="badge bg-warning-subtle text-warning-emphasis ms-2">Saved</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{answer.json_answer}}
|
<span class="badge bg-danger-subtle text-danger-emphasis ms-2">Not answered</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if self_review %}
|
||||||
|
<span class="badge bg-info-subtle text-info-emphasis ms-1">Self review added</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if collection.show_title_post %}
|
||||||
|
<div class="small text-muted">
|
||||||
|
{{ question.case }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
<a class="btn btn-sm btn-primary"
|
||||||
|
href="{% if cid is not None %}{% url 'atlas:collection_case_view_take' pk=collection.id case_number=forloop.counter0 cid=cid passcode=passcode %}{% else %}{% url 'atlas:collection_case_view_take_user' pk=collection.id case_number=forloop.counter0 %}{% endif %}">
|
||||||
|
Open Case
|
||||||
|
</a>
|
||||||
|
{% if cid is None %}
|
||||||
|
<a class="btn btn-sm btn-outline-secondary"
|
||||||
|
href="{% url 'atlas:collection_case_view_take_user_answers' pk=collection.id case_number=forloop.counter0 %}">
|
||||||
|
View Answer / Report
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-sm btn-outline-secondary"
|
||||||
|
href="{% url 'atlas:collection_case_view_take' pk=collection.id case_number=forloop.counter0 cid=cid passcode=passcode %}">
|
||||||
|
View Answer / Report
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<a class="btn btn-sm btn-outline-info"
|
||||||
|
href="{% url 'atlas:add_self_review' cid_user_exam.id question.case.id %}">
|
||||||
|
{% if self_review %}Add New Self Review{% else %}Add Self Review{% endif %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if self_review %}
|
||||||
|
<div class="mt-2 pt-2 border-top">
|
||||||
|
<div class="small fw-semibold mb-1">Your self reviews</div>
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
{% for review in self_review %}
|
||||||
|
<a class="btn btn-sm btn-outline-info"
|
||||||
|
href="{% url 'atlas:self_review_detail' cid_user_exam.id question.case.id review.id %}">
|
||||||
|
View review {{ forloop.counter }}
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-sm btn-outline-warning"
|
||||||
|
href="{% url 'atlas:self_review_update' cid_user_exam.id question.case.id review.id %}">
|
||||||
|
Edit review {{ forloop.counter }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not collection.review_only %}
|
||||||
|
{% if answer %}
|
||||||
|
<details class="mt-2">
|
||||||
|
<summary class="small text-muted">Preview saved answer/report</summary>
|
||||||
|
<div class="small mt-2 p-2 bg-body-tertiary rounded">
|
||||||
|
{% if answer.answer %}
|
||||||
|
{{ answer.answer|linebreaksbr }}
|
||||||
|
{% else %}
|
||||||
|
{{ answer.json_answer }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</details>
|
</details>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</div>
|
||||||
</li>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
Start time: {{cid_user_exam.start_time}}
|
|
||||||
|
|
||||||
|
|
||||||
{% if not collection.review_only and not cid_user_exam.completed %}
|
{% if not collection.review_only and not cid_user_exam.completed %}
|
||||||
<p>Completed: <span id="completed-state">{{cid_user_exam.completed}}</span></p>
|
<div class="card border-warning-subtle">
|
||||||
|
<div class="card-body d-flex flex-wrap justify-content-between align-items-center gap-2">
|
||||||
|
<div>
|
||||||
|
<div class="fw-semibold">Ready to submit this session?</div>
|
||||||
|
<div class="small text-muted">Completed: <span id="completed-state">{{ cid_user_exam.completed }}</span></div>
|
||||||
|
</div>
|
||||||
<form hx-post=""
|
<form hx-post=""
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
hx-delete
|
hx-delete
|
||||||
hx-confirm="Finish session?"
|
hx-confirm="Finish session?"
|
||||||
hx-target="#completed-state"
|
hx-target="#completed-state"
|
||||||
_="on htmx:afterOnLoad remove me"
|
_="on htmx:afterOnLoad remove me"
|
||||||
>
|
class="m-0">
|
||||||
<button type="submit" name="finish" value="finish">Finish</button>
|
<button type="submit" name="finish" value="finish" class="btn btn-warning">Finish Session</button>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,31 @@
|
|||||||
{% for cd in casedetails %}
|
{% for item in jump_items %}
|
||||||
{% with idx=forloop.counter0 %}
|
<div class="dropdown-item-text {% if not item.has_answer %}bg-danger-subtle border border-danger-subtle rounded{% elif item.idx == current %}bg-primary-subtle rounded{% endif %} py-2 px-2 mb-1">
|
||||||
{% if cid %}
|
<div class="d-flex justify-content-between align-items-start gap-2">
|
||||||
<a class="dropdown-item {% if idx == current %}active{% endif %}" href="{% url 'atlas:collection_case_view_take' pk=collection.pk case_number=idx cid=cid passcode=passcode %}">
|
<a class="text-decoration-none small fw-semibold"
|
||||||
{% else %}
|
href="{{ item.case_url }}">
|
||||||
<a class="dropdown-item {% if idx == current %}active{% endif %}" href="{% url 'atlas:collection_case_view_take_user' pk=collection.pk case_number=idx %}">
|
Case {{ item.idx|add:1 }}: {{ item.cd.case.title|default:item.cd.case.pk|truncatechars:45 }}
|
||||||
{% endif %}
|
|
||||||
Case {{ forloop.counter }}: {{ cd.case.title|default:cd.case.pk|truncatechars:60 }}
|
|
||||||
</a>
|
</a>
|
||||||
{% endwith %}
|
<div class="d-flex gap-1">
|
||||||
|
{% if item.has_answer %}
|
||||||
|
<span class="badge bg-success-subtle text-success-emphasis">Answered</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge bg-danger-subtle text-danger-emphasis">Not answered</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if item.has_self_review %}
|
||||||
|
<span class="badge bg-info-subtle text-info-emphasis">Self review</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-wrap gap-1 mt-2">
|
||||||
|
<a class="btn btn-outline-secondary btn-sm py-0 px-2" href="{{ item.answer_url }}">Answer</a>
|
||||||
|
{% if item.self_review_view_url %}
|
||||||
|
<a class="btn btn-outline-info btn-sm py-0 px-2" href="{{ item.self_review_view_url }}">View review</a>
|
||||||
|
<a class="btn btn-outline-warning btn-sm py-0 px-2" href="{{ item.self_review_edit_url }}">Edit</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-outline-info btn-sm py-0 px-2" href="{{ item.self_review_add_url }}">Add review</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% empty %}
|
||||||
|
<div class="dropdown-item-text text-muted small px-2 py-2">No cases available.</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -24,20 +24,30 @@
|
|||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
Updated: {{ review.review_update_date|date:"Y-m-d H:i" }}
|
Updated: {{ review.review_update_date|date:"Y-m-d H:i" }}
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="Edit self review">
|
<div class="d-flex flex-wrap gap-1" role="group" aria-label="Self review actions">
|
||||||
<a class="btn btn-outline-primary"
|
<a class="btn btn-outline-primary btn-sm"
|
||||||
|
href="{% url 'atlas:self_review_detail' cid_user_exam.id case.id review.id %}">
|
||||||
|
View
|
||||||
|
</a>
|
||||||
|
<button type="button" class="btn btn-outline-info btn-sm"
|
||||||
|
onclick="openSelfReviewPopup('{% url 'atlas:self_review_detail' cid_user_exam.id case.id review.id %}')">
|
||||||
|
View popup
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm"
|
||||||
|
onclick="openSelfReviewSidebar('{% url 'atlas:self_review_detail' cid_user_exam.id case.id review.id %}')">
|
||||||
|
View sidebar
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-outline-warning btn-sm"
|
||||||
href="{% url 'atlas:self_review_update' cid_user_exam.id case.id review.id %}">
|
href="{% url 'atlas:self_review_update' cid_user_exam.id case.id review.id %}">
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
<button type="button"
|
<button type="button" class="btn btn-outline-warning btn-sm"
|
||||||
class="btn btn-outline-info"
|
|
||||||
onclick="openSelfReviewPopup('{% url 'atlas:self_review_update' cid_user_exam.id case.id review.id %}')">
|
onclick="openSelfReviewPopup('{% url 'atlas:self_review_update' cid_user_exam.id case.id review.id %}')">
|
||||||
Popup
|
Edit popup
|
||||||
</button>
|
</button>
|
||||||
<button type="button"
|
<button type="button" class="btn btn-outline-warning btn-sm"
|
||||||
class="btn btn-outline-secondary"
|
|
||||||
onclick="openSelfReviewSidebar('{% url 'atlas:self_review_update' cid_user_exam.id case.id review.id %}')">
|
onclick="openSelfReviewSidebar('{% url 'atlas:self_review_update' cid_user_exam.id case.id review.id %}')">
|
||||||
Sidebar
|
Edit sidebar
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<div class="p-3">
|
||||||
|
<div class="card border-secondary shadow-sm mb-2">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h2 class="h6 mb-0">Self Review</h2>
|
||||||
|
<span class="badge bg-primary-subtle text-primary-emphasis">{{ user_exam.exam }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="small text-muted mb-2">Case: <strong>{{ case }}</strong></div>
|
||||||
|
|
||||||
|
<div class="row g-2 mb-3">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="p-2 border rounded">
|
||||||
|
<div class="small text-muted">Findings</div>
|
||||||
|
<div class="fw-semibold">{{ review.findings|default:"-" }}/5</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="p-2 border rounded">
|
||||||
|
<div class="small text-muted">Interpretation</div>
|
||||||
|
<div class="fw-semibold">{{ review.interpretation|default:"-" }}/5</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="small text-muted mb-1">Comments</div>
|
||||||
|
<div class="p-2 border rounded bg-body-tertiary">
|
||||||
|
{% if review.comments %}
|
||||||
|
{{ review.comments|linebreaksbr }}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">No comments added.</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small text-muted mb-3">
|
||||||
|
Updated: {{ review.review_update_date }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 flex-wrap">
|
||||||
|
<a class="btn btn-outline-warning btn-sm"
|
||||||
|
href="{% url 'atlas:self_review_update' user_exam.id case.id review.id %}?embed=1">
|
||||||
|
Edit in sidebar
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-outline-secondary btn-sm"
|
||||||
|
href="{% url 'atlas:self_review_detail' user_exam.id case.id review.id %}">
|
||||||
|
Open full page
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container px-0" style="max-width: 920px;">
|
||||||
|
<div class="card border-secondary shadow-sm mb-3">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h2 class="h5 mb-0">Self Review</h2>
|
||||||
|
<span class="badge bg-primary-subtle text-primary-emphasis">{{ user_exam.exam }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="small text-muted mb-3">Case: <strong>{{ case }}</strong></div>
|
||||||
|
<div class="row g-2 mb-3">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="p-2 border rounded">
|
||||||
|
<div class="small text-muted">Findings</div>
|
||||||
|
<div class="fw-semibold">{{ review.findings|default:"-" }}/5</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="p-2 border rounded">
|
||||||
|
<div class="small text-muted">Interpretation</div>
|
||||||
|
<div class="fw-semibold">{{ review.interpretation|default:"-" }}/5</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="small text-muted mb-1">Comments</div>
|
||||||
|
<div class="p-2 border rounded bg-body-tertiary">
|
||||||
|
{% if review.comments %}
|
||||||
|
{{ review.comments|linebreaksbr }}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">No comments added.</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted mb-3">
|
||||||
|
Created: {{ review.review_date }}<br>
|
||||||
|
Updated: {{ review.review_update_date }}
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2 flex-wrap">
|
||||||
|
<a class="btn btn-outline-warning"
|
||||||
|
href="{% url 'atlas:self_review_update' user_exam.id case.id review.id %}">
|
||||||
|
Edit Self Review
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-outline-secondary" href="{{ review.get_absolute_url }}">
|
||||||
|
Back to case
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -25,6 +25,11 @@ urlpatterns = [
|
|||||||
views.SelfReviewUpdate.as_view(),
|
views.SelfReviewUpdate.as_view(),
|
||||||
name="self_review_update",
|
name="self_review_update",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"self_review/<int:user_exam_id>/<int:case_id>/view/<int:pk>",
|
||||||
|
views.SelfReviewDetail.as_view(),
|
||||||
|
name="self_review_detail",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"self_review/<int:user_exam_id>/<int:case_id>/delete/<int:pk>",
|
"self_review/<int:user_exam_id>/<int:case_id>/delete/<int:pk>",
|
||||||
views.SelfReviewDelete.as_view(),
|
views.SelfReviewDelete.as_view(),
|
||||||
|
|||||||
+114
@@ -1884,11 +1884,94 @@ def collection_case_jump_partial(request, pk: int):
|
|||||||
cid = request.GET.get('cid')
|
cid = request.GET.get('cid')
|
||||||
passcode = request.GET.get('passcode')
|
passcode = request.GET.get('passcode')
|
||||||
|
|
||||||
|
try:
|
||||||
|
cid_value = int(cid) if cid is not None else None
|
||||||
|
except Exception:
|
||||||
|
cid_value = None
|
||||||
|
|
||||||
|
cid_user_exam = collection.get_or_create_cid_user_exam(
|
||||||
|
cid=cid_value,
|
||||||
|
user_user=request.user,
|
||||||
|
)
|
||||||
|
|
||||||
|
if cid_value is not None:
|
||||||
|
answers = CidReportAnswer.objects.filter(cid=cid_value, question__in=casedetails)
|
||||||
|
else:
|
||||||
|
answers = UserReportAnswer.objects.filter(user=request.user, question__in=casedetails)
|
||||||
|
|
||||||
|
answer_case_ids = {
|
||||||
|
ans.question.case_id
|
||||||
|
for ans in answers
|
||||||
|
if (ans.answer or ans.json_answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
latest_reviews = (
|
||||||
|
cid_user_exam.selfreview_set.filter(case__in=[cd.case for cd in casedetails])
|
||||||
|
.select_related("case")
|
||||||
|
.order_by("case_id", "-review_update_date")
|
||||||
|
)
|
||||||
|
|
||||||
|
latest_review_by_case = {}
|
||||||
|
for review in latest_reviews:
|
||||||
|
if review.case_id not in latest_review_by_case:
|
||||||
|
latest_review_by_case[review.case_id] = review
|
||||||
|
|
||||||
|
jump_items = []
|
||||||
|
for idx, cd in enumerate(casedetails):
|
||||||
|
if cid is not None:
|
||||||
|
case_url = reverse(
|
||||||
|
"atlas:collection_case_view_take",
|
||||||
|
kwargs={"pk": collection.pk, "case_number": idx, "cid": cid, "passcode": passcode},
|
||||||
|
)
|
||||||
|
answer_url = case_url
|
||||||
|
else:
|
||||||
|
case_url = reverse(
|
||||||
|
"atlas:collection_case_view_take_user",
|
||||||
|
kwargs={"pk": collection.pk, "case_number": idx},
|
||||||
|
)
|
||||||
|
answer_url = reverse(
|
||||||
|
"atlas:collection_case_view_take_user_answers",
|
||||||
|
kwargs={"pk": collection.pk, "case_number": idx},
|
||||||
|
)
|
||||||
|
|
||||||
|
add_url = reverse(
|
||||||
|
"atlas:add_self_review",
|
||||||
|
kwargs={"user_exam_id": cid_user_exam.id, "case_id": cd.case_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
review = latest_review_by_case.get(cd.case_id)
|
||||||
|
review_view_url = None
|
||||||
|
review_edit_url = None
|
||||||
|
if review is not None:
|
||||||
|
review_view_url = reverse(
|
||||||
|
"atlas:self_review_detail",
|
||||||
|
kwargs={"user_exam_id": cid_user_exam.id, "case_id": cd.case_id, "pk": review.id},
|
||||||
|
)
|
||||||
|
review_edit_url = reverse(
|
||||||
|
"atlas:self_review_update",
|
||||||
|
kwargs={"user_exam_id": cid_user_exam.id, "case_id": cd.case_id, "pk": review.id},
|
||||||
|
)
|
||||||
|
|
||||||
|
jump_items.append(
|
||||||
|
{
|
||||||
|
"idx": idx,
|
||||||
|
"cd": cd,
|
||||||
|
"case_url": case_url,
|
||||||
|
"answer_url": answer_url,
|
||||||
|
"self_review_add_url": add_url,
|
||||||
|
"self_review_view_url": review_view_url,
|
||||||
|
"self_review_edit_url": review_edit_url,
|
||||||
|
"has_answer": cd.case_id in answer_case_ids,
|
||||||
|
"has_self_review": review is not None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
'atlas/partials/_collection_case_jump.html',
|
'atlas/partials/_collection_case_jump.html',
|
||||||
{
|
{
|
||||||
'collection': collection,
|
'collection': collection,
|
||||||
'casedetails': casedetails,
|
'casedetails': casedetails,
|
||||||
|
'jump_items': jump_items,
|
||||||
'current': current,
|
'current': current,
|
||||||
'cid': cid,
|
'cid': cid,
|
||||||
'passcode': passcode,
|
'passcode': passcode,
|
||||||
@@ -5644,6 +5727,37 @@ class SelfReviewDelete(DeleteView):
|
|||||||
return self.user_exam.exam.get_case_take_url(self.case)
|
return self.user_exam.exam.get_case_take_url(self.case)
|
||||||
|
|
||||||
|
|
||||||
|
class SelfReviewDetail(DetailView):
|
||||||
|
"""Read-only self-review view for a specific user exam and case.
|
||||||
|
|
||||||
|
This is the default viewing mode for self reviews. Add ``?embed=1`` to
|
||||||
|
render the compact sidebar-friendly template.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = SelfReview
|
||||||
|
|
||||||
|
def get_template_names(self):
|
||||||
|
if self.request.GET.get("embed") == "1":
|
||||||
|
return ["atlas/partials/_selfreview_detail_embed.html"]
|
||||||
|
return ["atlas/selfreview_detail.html"]
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
return get_object_or_404(
|
||||||
|
SelfReview,
|
||||||
|
pk=self.kwargs["pk"],
|
||||||
|
user_exam_id=self.kwargs["user_exam_id"],
|
||||||
|
case_id=self.kwargs["case_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(SelfReviewDetail, self).get_context_data(**kwargs)
|
||||||
|
context["review"] = self.object
|
||||||
|
context["user_exam"] = self.object.user_exam
|
||||||
|
context["case"] = self.object.case
|
||||||
|
context["embed"] = self.request.GET.get("embed") == "1"
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class SelfReviewUpdate(UpdateView):
|
class SelfReviewUpdate(UpdateView):
|
||||||
"""Edit an existing self-review entry for a case attempt.
|
"""Edit an existing self-review entry for a case attempt.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user