Compare commits
15
Commits
d2f4c21883
...
19eb10c089
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19eb10c089 | ||
|
|
2ab5a8e3b5 | ||
|
|
c2f12f384a | ||
|
|
33f32dab8b | ||
|
|
a676d437bb | ||
|
|
ba5b11cce2 | ||
|
|
a6c5b99620 | ||
|
|
666f49f2c2 | ||
|
|
46608a0758 | ||
|
|
cfdf33b9ce | ||
|
|
bdc97922af | ||
|
|
3626f3918e | ||
|
|
49fbff76e9 | ||
|
|
7ec81133d8 | ||
|
|
c67db4a39e |
+35
-2
@@ -34,7 +34,9 @@ from dal import autocomplete
|
|||||||
from tinymce.widgets import TinyMCE
|
from tinymce.widgets import TinyMCE
|
||||||
|
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Submit
|
from crispy_forms.layout import Submit, Layout, Div
|
||||||
|
from crispy_forms.bootstrap import Accordion, AccordionGroup
|
||||||
|
from generic.widgets import ExamSearchWidget
|
||||||
|
|
||||||
class AnatomyAnswerForm(ModelForm):
|
class AnatomyAnswerForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -127,7 +129,38 @@ class AnatomyQuestionForm(ModelForm):
|
|||||||
self.fields["exams"] = ModelMultipleChoiceField(
|
self.fields["exams"] = ModelMultipleChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
queryset=exam_queryset,
|
queryset=exam_queryset,
|
||||||
widget=FilteredSelectMultiple(verbose_name="Exams", is_stacked=False),
|
widget=ExamSearchWidget(exam_model=Exam),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now that all fields have been created, set the helper layout so
|
||||||
|
# Crispy can find the referenced field names (including
|
||||||
|
# `answer_suggest_incorrect`). Placing the layout here ensures the
|
||||||
|
# collapse/accordion renders server-side.
|
||||||
|
self.helper.layout = Layout(
|
||||||
|
"question_type",
|
||||||
|
"image",
|
||||||
|
"description",
|
||||||
|
# Place modality → body_part fields in a horizontal flex row
|
||||||
|
Div(
|
||||||
|
Div("modality", css_class="flex-fill me-2"),
|
||||||
|
Div("region", css_class="flex-fill me-2"),
|
||||||
|
Div("structure", css_class="flex-fill me-2"),
|
||||||
|
Div("examination", css_class="flex-fill me-2"),
|
||||||
|
Div("body_part", css_class="flex-fill"),
|
||||||
|
css_class="d-flex flex-wrap align-items-start mb-2",
|
||||||
|
),
|
||||||
|
"answer_help",
|
||||||
|
Accordion(
|
||||||
|
AccordionGroup(
|
||||||
|
"Suggest incorrect answers",
|
||||||
|
"answer_suggest_incorrect",
|
||||||
|
active=False,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"feedback",
|
||||||
|
"open_access",
|
||||||
|
"exams",
|
||||||
|
Div(Submit("submit", "Submit"), css_class="form-group"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
|
|||||||
@@ -233,6 +233,10 @@
|
|||||||
<p>Question type can be selected from a predefined list. If you require a more please contact ross.kruger@nhs.net.</p>
|
<p>Question type can be selected from a predefined list. If you require a more please contact ross.kruger@nhs.net.</p>
|
||||||
|
|
||||||
<p>Correct and incorrect answers can be defined once the question has been created.</p>
|
<p>Correct and incorrect answers can be defined once the question has been created.</p>
|
||||||
|
|
||||||
|
<p>Images can be selected either directly via the browse button or by dragging and dropping them into the designated area below.</p>
|
||||||
|
|
||||||
|
<p>Supported formats include JPEG, PNG, and GIF. It is also possible to use DICOM images.</p>
|
||||||
{% endhelp %}
|
{% endhelp %}
|
||||||
<form action="" method="post" enctype="multipart/form-data" id="anatomyquestion-form">
|
<form action="" method="post" enctype="multipart/form-data" id="anatomyquestion-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@@ -241,10 +245,9 @@
|
|||||||
<a href="/anatomy/body_part/create" id="add_body_part" class="add-popup"
|
<a href="/anatomy/body_part/create" id="add_body_part" class="add-popup"
|
||||||
onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a>
|
onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a>
|
||||||
|
|
||||||
{{ form|crispy }}
|
{% crispy form %}
|
||||||
<div id="drop-container" class="drop-target">Drop image here
|
<div id="drop-container" class="drop-target">Drop image here
|
||||||
<div id="drop-filenames"></div>
|
<div id="drop-filenames"></div>
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" class="submit-button" value="Submit" name="submit">
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -10,10 +10,10 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="anatomy-dicom-image" class="dicom-image-legacy w-100" data-url="{{ question.get_image_url }}" data-annotations='{{question.image_annotations}}' data-edit_annotation=true style="min-height:300px">
|
<div id="anatomy-dicom-image" class="dicom-image-legacy w-100" data-url="{{ question.get_image_url }}" data-annotations='{{question.image_annotations}}' data-edit_annotation=true style="min-height:300px">
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3 d-flex justify-content-between align-items-center">
|
<div class="mt-3 d-flex justify-content-between align-items-start">
|
||||||
<details class="mb-0">
|
<details class="mb-0 flex-grow-1 me-2">
|
||||||
<summary class="small text-muted"><i class="bi bi-info-circle"></i> Image help</summary>
|
<summary class="small text-muted"><i class="bi bi-info-circle"></i> Image annotation help</summary>
|
||||||
<div class="small text-muted mt-2">Annotate the image using the right mouse button to click and drag an arrow. To remove an arrow drag it out of the image boundaries. Click <strong>Save Annotations</strong> to persist.</div>
|
<div class="small text-muted mt-2">Annotate the image using the right mouse button to click and drag an arrow. To remove an arrow drag it out of the image boundaries. Click <strong>Save Annotations</strong> to persist. If required use the middle button to zoom and the left button to pan.</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<button id="save-annotations" class="btn btn-sm btn-primary">Save Annotations</button>
|
<button id="save-annotations" class="btn btn-sm btn-primary">Save Annotations</button>
|
||||||
|
|||||||
@@ -3005,6 +3005,21 @@ class StructureAutocomplete(autocomplete.Select2QuerySetView):
|
|||||||
return Structure.objects.none()
|
return Structure.objects.none()
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
def has_add_permission(self, request):
|
||||||
|
"""Allow any authenticated user to create new Structure entries.
|
||||||
|
|
||||||
|
By default DAL requires the user to have the model add permission.
|
||||||
|
For Anatomy question creation we want any registered user to be
|
||||||
|
able to create structures, so relax that check to only require
|
||||||
|
authentication.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
auth = request.user.is_authenticated
|
||||||
|
except Exception:
|
||||||
|
auth = False
|
||||||
|
|
||||||
|
return bool(auth)
|
||||||
|
|
||||||
|
|
||||||
class SubspecialtyAutocomplete(autocomplete.Select2QuerySetView):
|
class SubspecialtyAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
|
|||||||
@@ -131,7 +131,7 @@
|
|||||||
<div class="card-body p-2">
|
<div class="card-body p-2">
|
||||||
<div class="collapse" id="open-access-bulk">
|
<div class="collapse" id="open-access-bulk">
|
||||||
<form hx-post="{% url 'generic:generic_exam_set_open_access' %}" hx-target="#action-result" hx-swap="innerHTML" class="row g-2 align-items-center">
|
<form hx-post="{% url 'generic:generic_exam_set_open_access' %}" hx-target="#action-result" hx-swap="innerHTML" class="row g-2 align-items-center">
|
||||||
<input type="hidden" name="type" value="sbas" />
|
<input type="hidden" name="type" value="{{exam.app_name}}" />
|
||||||
<input type="hidden" name="exam_id" value="{{ exam.pk }}" />
|
<input type="hidden" name="exam_id" value="{{ exam.pk }}" />
|
||||||
<div class="col-12 d-flex flex-column flex-md-row gap-2 justify-content-md-end">
|
<div class="col-12 d-flex flex-column flex-md-row gap-2 justify-content-md-end">
|
||||||
<button class="btn btn-sm btn-outline-success" type="submit" name="set_open_access" value="true" hx-include="input[name='selection']:checked">Set Open Access = True</button>
|
<button class="btn btn-sm btn-outline-success" type="submit" name="set_open_access" value="true" hx-include="input[name='selection']:checked">Set Open Access = True</button>
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
{% extends app_name|add:'/base.html' %}
|
{% extends app_name|add:'/base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<h3>Questions </h3>
|
||||||
|
<a href="{% url app_name|add:':question_view' %}?author={{ request.user.id }}" class="btn btn-sm btn-outline-secondary">My questions</a>
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<a href="{% url app_name|add:':question_create' %}" class="btn btn-sm btn-primary">Create question</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Exams</h3>
|
||||||
<div id="exam-list-wrapper">
|
<div id="exam-list-wrapper">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<div>
|
<div>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
{% load static %}
|
||||||
|
{% if user_obj %}
|
||||||
|
<div class="card p-2">
|
||||||
|
<h5>Content by {{ user_obj.get_full_name }} ({{ user_obj.username }})</h5>
|
||||||
|
|
||||||
|
{% for app_key, info in content.items %}
|
||||||
|
<div class="mb-2">
|
||||||
|
<strong>{{ info.label }}</strong>
|
||||||
|
<div class="small">
|
||||||
|
Questions: {{ info.questions|length }} Exams: {{ info.exams|length }}
|
||||||
|
</div>
|
||||||
|
<div class="mt-1">
|
||||||
|
{% if info.questions %}
|
||||||
|
<ul class="list-unstyled ms-3">
|
||||||
|
{% for q in info.questions %}
|
||||||
|
<li>
|
||||||
|
{% comment %} Try to link to app question detail if available {% endcomment %}
|
||||||
|
<a href="{% url app_key|add:':question_detail' q.pk %}" target="_blank">{{ q }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if info.exams %}
|
||||||
|
<ul class="list-unstyled ms-3">
|
||||||
|
{% for ex in info.exams %}
|
||||||
|
<li>{{ ex }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<strong>Atlas Cases</strong>
|
||||||
|
<div class="small">Cases: {{ atlas_cases|length }}</div>
|
||||||
|
{% if atlas_cases %}
|
||||||
|
<ul class="list-unstyled ms-3">
|
||||||
|
{% for case in atlas_cases %}
|
||||||
|
<li><a href="{% url 'atlas:case_detail' case.pk %}" target="_blank">{{ case }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<strong>Atlas Collections</strong>
|
||||||
|
<div class="small">Collections: {{ atlas_collections|length }}</div>
|
||||||
|
{% if atlas_collections %}
|
||||||
|
<ul class="list-unstyled ms-3">
|
||||||
|
{% for coll in atlas_collections %}
|
||||||
|
<li><a href="{% url 'atlas:collection_detail' coll.pk %}" target="_blank">{{ coll }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div>No user found</div>
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{% extends 'generic/base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>User content</h2>
|
||||||
|
|
||||||
|
<p>Click "Show content" to load items created by a user.</p>
|
||||||
|
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Content count</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for user in users %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ user.first_name }} {{ user.last_name }}</td>
|
||||||
|
<td><a href="{% url 'account_profile' user.username %}">{{ user.username }}</a></td>
|
||||||
|
<td>{{ user.content_count }}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-sm btn-outline-primary"
|
||||||
|
hx-get="{% url 'generic:user_content_review' user.pk %}"
|
||||||
|
hx-target="closest tr"
|
||||||
|
hx-swap="afterend">Show content</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -8,6 +8,9 @@ from generic.views import ExamViews as GenericExamViews, GenericViewBase
|
|||||||
app_name = "generic"
|
app_name = "generic"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
# User content review views
|
||||||
|
path("user-content/", views.user_content_list, name="user_content_list"),
|
||||||
|
path("user-content/<int:user_id>/", views.user_content_review, name="user_content_review"),
|
||||||
path("examination/", views.ExaminationView.as_view(), name="examination_view"),
|
path("examination/", views.ExaminationView.as_view(), name="examination_view"),
|
||||||
path("examination/<int:pk>", views.examination_detail, name="examination_detail"),
|
path("examination/<int:pk>", views.examination_detail, name="examination_detail"),
|
||||||
path(
|
path(
|
||||||
|
|||||||
+85
-1
@@ -2989,7 +2989,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"generic/exam_index.html",
|
"generic/frcr_index.html",
|
||||||
{"filter": filter, "app_name": self.app_name},
|
{"filter": filter, "app_name": self.app_name},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -6877,3 +6877,87 @@ def supervisor_search(request):
|
|||||||
|
|
||||||
return render(request, "generic/partials/supervisor_search_results.html", {"results": results, "row": row})
|
return render(request, "generic/partials/supervisor_search_results.html", {"results": results, "row": row})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(lambda u: u.is_superuser or u.groups.filter(name__in=["content_moderator", "atlas_editor"]).exists())
|
||||||
|
def user_content_list(request):
|
||||||
|
"""
|
||||||
|
Shows a list of all users with the ability to load the content they have created via htmx requests
|
||||||
|
"""
|
||||||
|
users = User.objects.annotate(
|
||||||
|
content_count=Count("user_anatomy_exams", filter=Q(user_anatomy_exams__archive=False))
|
||||||
|
+ Count("user_longs_exams", filter=Q(user_longs_exams__archive=False))
|
||||||
|
+ Count("user_physics_exams", filter=Q(user_physics_exams__archive=False))
|
||||||
|
+ Count("user_rapid_exams", filter=Q(user_rapid_exams__archive=False))
|
||||||
|
+ Count("user_sba_exams", filter=Q(user_sba_exams__archive=False))
|
||||||
|
+ Count("user_shorts_exams", filter=Q(user_shorts_exams__archive=False))
|
||||||
|
).filter(content_count__gt=0)
|
||||||
|
|
||||||
|
return render(request, "generic/user_content_list.html", {"users": users})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(lambda u: u.is_superuser or u.groups.filter(name__in=["content_moderator", "atlas_editor"]).exists())
|
||||||
|
def user_content_review(request, user_id):
|
||||||
|
"""
|
||||||
|
HTMX partial to show content authored by a given user.
|
||||||
|
Includes authored questions and authored exams for each app,
|
||||||
|
plus atlas cases and case collections.
|
||||||
|
"""
|
||||||
|
user_obj = get_object_or_404(User, pk=user_id)
|
||||||
|
|
||||||
|
# Mapping of app -> (question_related_name, exam_author_related_name, display_label)
|
||||||
|
APP_MAP = {
|
||||||
|
"anatomy": ("anatomy_authored_questions", "anatomy_exam_author", "Anatomy"),
|
||||||
|
"physics": ("physics_authored_questions", "physics_exam_author", "Physics"),
|
||||||
|
"rapids": ("rapid_authored_questions", "rapid_exam_author", "Rapids"),
|
||||||
|
"shorts": ("shorts_authored_questions", "shorts_exam_author", "Shorts"),
|
||||||
|
"longs": ("long_authored_questions", "long_exam_author", "Longs"),
|
||||||
|
"sbas": ("sbas_authored_questions", "sba_exam_author", "SBAs"),
|
||||||
|
}
|
||||||
|
|
||||||
|
content = {}
|
||||||
|
|
||||||
|
for app_key, (q_rel, e_rel, label) in APP_MAP.items():
|
||||||
|
questions = []
|
||||||
|
exams = []
|
||||||
|
try:
|
||||||
|
questions = list(getattr(user_obj, q_rel).all().order_by("-pk")[:50])
|
||||||
|
except Exception:
|
||||||
|
questions = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
exams = list(getattr(user_obj, e_rel).all().filter(archive=False).order_by("-pk")[:50])
|
||||||
|
except Exception:
|
||||||
|
exams = []
|
||||||
|
|
||||||
|
content[app_key] = {
|
||||||
|
"label": label,
|
||||||
|
"questions": questions,
|
||||||
|
"exams": exams,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Atlas authored content
|
||||||
|
atlas_cases = []
|
||||||
|
atlas_collections = []
|
||||||
|
try:
|
||||||
|
atlas_cases = list(user_obj.atlas_authored_cases.all().order_by("-pk")[:50])
|
||||||
|
except Exception:
|
||||||
|
atlas_cases = []
|
||||||
|
try:
|
||||||
|
atlas_collections = list(user_obj.casecollection_authored_cases.all().order_by("-pk")[:50])
|
||||||
|
except Exception:
|
||||||
|
atlas_collections = []
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"user_obj": user_obj,
|
||||||
|
"content": content,
|
||||||
|
"atlas_cases": atlas_cases,
|
||||||
|
"atlas_collections": atlas_collections,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Render HTMX partial (or full page if not HTMX)
|
||||||
|
template = "generic/partials/user_content_review.html"
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|||||||
+171
-119
@@ -2,128 +2,180 @@
|
|||||||
{% load auth_extras %}
|
{% load auth_extras %}
|
||||||
{% load help_tags %}
|
{% load help_tags %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="p-4 bg-dark rounded-3 border">
|
<div class="p-4 bg-dark rounded-3 border">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<h1 class="mb-0">PENRA Courses</h1>
|
<h1 class="mb-0">PENRA Courses</h1>
|
||||||
{% if not request.user.is_authenticated %}
|
{% if not request.user.is_authenticated %}
|
||||||
{% comment %} <div>
|
{% comment %} <div>
|
||||||
<a class="btn btn-outline-primary me-2" href="{% url 'login' %}">Log in</a>
|
<a class="btn btn-outline-primary me-2" href="{% url 'login' %}">Log in</a>
|
||||||
<a class="btn btn-outline-secondary" href="{% url 'password_reset' %}">Reset password</a>
|
<a class="btn btn-outline-secondary" href="{% url 'password_reset' %}">Reset password</a>
|
||||||
</div> {% endcomment %}
|
</div> {% endcomment %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="btn btn-outline-light" href="{% url 'profile' %}">Your profile</a>
|
<a class="btn btn-outline-light" href="{% url 'profile' %}">Your profile</a>
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<p class="text-muted mt-2">Access exams, courses and resources for trainees and trainers.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row g-4">
|
|
||||||
<div class="col-lg-8">
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
{% if request.user.userprofile.peninsula_trainee %}
|
|
||||||
<div class="card mb-3">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">Profile</h5>
|
|
||||||
<p class="mb-1"><strong>Name:</strong> {{ request.user.first_name }} {{ request.user.last_name }}</p>
|
|
||||||
<p class="mb-1"><strong>Email:</strong> {{ request.user.email }}</p>
|
|
||||||
<p class="mb-1"><strong>Grade:</strong> {{ request.user.userprofile.grade }}</p>
|
|
||||||
<p class="mb-0"><strong>Supervisor:</strong> {{ request.user.userprofile.supervisor }}</p>
|
|
||||||
<div role="alert" class="alert alert-warning mt-3">
|
|
||||||
If any of this information is incorrect, please update it via your <a href="{% url 'profile' %}">profile page</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="card mb-3">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">Exams / Courses</h5>
|
|
||||||
<p class="card-text">View your courses and exam scores.</p>
|
|
||||||
<a class="btn btn-outline-primary" href="{% url 'user_scores' %}">View my courses</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="card mb-3">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">Peninsula Trainees</h5>
|
|
||||||
<p class="card-text">Please log in to access your exams and courses.</p>
|
|
||||||
<a class="btn btn-outline-primary me-2" href="{% url 'login' %}">Log in</a>
|
|
||||||
<a class="btn btn-outline-secondary" href="{% url 'password_reset' %}">Reset password</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="alert alert-info" role="alert">
|
|
||||||
<h5 class="alert-heading mb-1">Not a Peninsula trainee?</h5>
|
|
||||||
<p class="mb-0">If you are an external user, please <a href="{% url 'cid_selector' %}">log in via CID</a>.</p>
|
|
||||||
{% help "Help" %}
|
|
||||||
You should have received your candidate ID and login details by email or directly from your course organiser. If you have not, please contact them for assistance.
|
|
||||||
{% endhelp %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if collections %}
|
|
||||||
<div class="card mb-3">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">Exam collections</h5>
|
|
||||||
<div class="list-group list-group-flush">
|
|
||||||
{% for collection in collections %}
|
|
||||||
<a class="list-group-item list-group-item-action" href="{% url 'generic:examcollection_detail' collection.pk %}">{{ collection.name }}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if rcr_assessor %}
|
|
||||||
<div class="alert alert-info" role="alert">
|
|
||||||
<h5 class="alert-heading mb-1">RCR platform migration</h5>
|
|
||||||
<p class="mb-0"><a href="{% url 'rcr:radiology_index_view' %}">Open RCR migration tools</a></p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-lg-4">
|
|
||||||
<div class="card mb-3">
|
|
||||||
<div class="card-body">
|
|
||||||
<h6 class="card-title">Quick links</h6>
|
|
||||||
<ul class="list-unstyled mb-0">
|
|
||||||
<li><a href="http://www.penracourses.org.uk/rts" class="link-primary">RTS platform</a></li>
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
<li><a href="{% url 'user_marking' %}" class="link-primary">Marking overview</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if not request.user.is_authenticated %}<li><a href="{% url 'cid_selector' %}" class="link-primary">CID login</a></li>{% endif %}
|
|
||||||
{% if request.user.supervisor %}<li><a href='{% url "generic:supervisor_overview" request.user.supervisor.pk %}' class="link-primary">Supervisor overview</a></li>{% endif %}
|
|
||||||
{% if request.user|has_group:"cid_user_manager" %}
|
|
||||||
<li>Manage <a href="{% url 'trainees' %}">trainees</a> · <a href="{% url 'accounts_list' %}">users</a> · <a href="{% url 'generic:manage_cids' %}">candidates</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if request.user.is_superuser %}
|
|
||||||
<li><a href="{% url 'django_psutil_dash:dashboard' %}" class="link-primary">psutil</a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="text-muted mt-2">Exams, courses and resources for trainees and trainers.</p>
|
||||||
<div class="card text-muted small">
|
|
||||||
<div class="card-body">
|
|
||||||
<strong>Need help?</strong>
|
|
||||||
<p class="mb-0">See <a href="{% url 'about' %}">About</a> or contact your admin.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mt-4">
|
|
||||||
<div class="col-12 text-center">
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
{% if request.user|has_group:"cid_user_manager" %}<a class="btn btn-sm btn-outline-secondary me-2" href="{% url 'admin:index' %}">Admin</a>{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
{% if request.user.userprofile.peninsula_trainee %}
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Profile</h5>
|
||||||
|
<p class="mb-1"><strong>Name:</strong> {{ request.user.first_name }} {{ request.user.last_name
|
||||||
|
}}</p>
|
||||||
|
<p class="mb-1"><strong>Email:</strong> {{ request.user.email }}</p>
|
||||||
|
<p class="mb-1"><strong>Grade:</strong> {{ request.user.userprofile.grade }}</p>
|
||||||
|
<p class="mb-0"><strong>Supervisor:</strong> {{ request.user.userprofile.supervisor }}</p>
|
||||||
|
<div role="alert" class="alert alert-warning mt-3">
|
||||||
|
If any of this information is incorrect, please update it via your <a
|
||||||
|
href="{% url 'profile' %}">profile page</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Exams / Courses</h5>
|
||||||
|
<p class="card-text">View your courses and exam scores.</p>
|
||||||
|
<a class="btn btn-outline-primary" href="{% url 'user_scores' %}">View my courses</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">We need your help!</h5>
|
||||||
|
<p class="card-text small text-muted">Help us grow the question banks and atlas.</p>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="border rounded p-3 h-100 bg-dark-subtle">
|
||||||
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||||
|
<h6 class="mb-0">FRCR</h6>
|
||||||
|
<span class="badge text-bg-secondary">Exams</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<a class="btn btn-sm btn-outline-primary"
|
||||||
|
href="{% url 'anatomy:question_create' %}">Add Anatomy question</a>
|
||||||
|
<a class="btn btn-sm btn-outline-primary"
|
||||||
|
href="{% url 'sbas:question_create' %}">Add SBA question</a>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary"
|
||||||
|
href="{% url 'sbas:question_review_start' %}">Review SBA question</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="border rounded p-3 h-100 bg-dark-subtle">
|
||||||
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||||
|
<h6 class="mb-0">Atlas</h6>
|
||||||
|
<span class="badge text-bg-secondary">Cases</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<a class="btn btn-sm btn-outline-primary"
|
||||||
|
href="{% url 'atlas:case_create' %}">Add case to Atlas</a>
|
||||||
|
<a class="btn btn-sm btn-outline-primary"
|
||||||
|
href="{% url 'atlas:exam_create' %}">Create collection</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Peninsula Trainees</h5>
|
||||||
|
<p class="card-text">Please log in to access your exams and courses.</p>
|
||||||
|
<a class="btn btn-outline-primary me-2" href="{% url 'login' %}">Log in</a>
|
||||||
|
<a class="btn btn-outline-secondary" href="{% url 'password_reset' %}">Reset password</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<h5 class="alert-heading mb-1">Not a Peninsula trainee?</h5>
|
||||||
|
<p class="mb-0">If you are an external user, please <a href="{% url 'cid_selector' %}">log in via
|
||||||
|
CID</a>.</p>
|
||||||
|
{% help "Help" %}
|
||||||
|
You should have received your candidate ID and login details by email or directly from your course
|
||||||
|
organiser. If you have not, please contact them for assistance.
|
||||||
|
{% endhelp %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if collections %}
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Exam collections</h5>
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
{% for collection in collections %}
|
||||||
|
<a class="list-group-item list-group-item-action"
|
||||||
|
href="{% url 'generic:examcollection_detail' collection.pk %}">{{ collection.name }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if rcr_assessor %}
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<h5 class="alert-heading mb-1">RCR platform migration</h5>
|
||||||
|
<p class="mb-0"><a href="{% url 'rcr:radiology_index_view' %}">Open RCR migration tools</a></p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-title">Quick links</h6>
|
||||||
|
<ul class="list-unstyled mb-0">
|
||||||
|
<li><a href="http://www.penracourses.org.uk/rts" class="link-primary">RTS platform</a></li>
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<li><a href="{% url 'user_marking' %}" class="link-primary">Marking overview</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if not request.user.is_authenticated %}<li><a href="{% url 'cid_selector' %}"
|
||||||
|
class="link-primary">CID login</a></li>{% endif %}
|
||||||
|
{% if request.user.supervisor %}<li><a
|
||||||
|
href='{% url "generic:supervisor_overview" request.user.supervisor.pk %}'
|
||||||
|
class="link-primary">Supervisor overview</a></li>{% endif %}
|
||||||
|
{% if request.user|has_group:"cid_user_manager" %}
|
||||||
|
<li>Manage <a href="{% url 'trainees' %}">trainees</a> · <a
|
||||||
|
href="{% url 'accounts_list' %}">users</a> · <a
|
||||||
|
href="{% url 'generic:manage_cids' %}">candidates</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if request.user.is_superuser %}
|
||||||
|
<li><a href="{% url 'django_psutil_dash:dashboard' %}" class="link-primary">psutil</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card text-muted small">
|
||||||
|
<div class="card-body">
|
||||||
|
<strong>Need help?</strong>
|
||||||
|
<p class="mb-0">See <a href="{% url 'about' %}">About</a> or contact your admin.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
{% if request.user|has_group:"cid_user_manager" %}<a class="btn btn-sm btn-outline-secondary me-2"
|
||||||
|
href="{% url 'admin:index' %}">Admin</a>{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user