.
This commit is contained in:
@@ -846,4 +846,10 @@ summary h5 {
|
||||
button a, button a:link, button a:visited, button a:hover {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
details.filter {
|
||||
border: 1px solid gray;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
}
|
||||
@@ -55,6 +55,15 @@ class AnatomyQuestionTable(tables.Table):
|
||||
"author",
|
||||
)
|
||||
sequence = ("view", "image", "exams")
|
||||
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(
|
||||
data.prefetch_related(
|
||||
"answers", "question_type", "body_part", "modality", "region", "examination", "exams", "author"
|
||||
),
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
class AnatomyUserAnswerTable(tables.Table):
|
||||
select = tables.CheckBoxColumn(accessor=("pk"))
|
||||
delete = tables.LinkColumn(
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{% extends 'anatomy/base.html' %}
|
||||
|
||||
{% load render_table from django_tables2 %}
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="view-filter-options">
|
||||
<h3>Filter Anatomy Questions</h3>
|
||||
<form action="" method="get">
|
||||
{{ filter.form }}
|
||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||
</form>
|
||||
</div>
|
||||
{% render_table table %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,30 +0,0 @@
|
||||
{% extends 'anatomy/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Examinations</h1>
|
||||
<div class="anatomy">
|
||||
Active exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'anatomy:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'anatomy:mark_overview' pk=exam.pk %}" class="flex-col">Mark</a><a href="{% url 'anatomy:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
Inactive exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if not exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'anatomy:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'anatomy:mark_overview' pk=exam.pk %}" class="flex-col">Mark</a><a href="{% url 'anatomy:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
+6
-1
@@ -902,10 +902,15 @@ def get_structure_id(request):
|
||||
class AnatomyQuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
||||
model = AnatomyQuestion
|
||||
table_class = AnatomyQuestionTable
|
||||
template_name = "anatomy/view.html"
|
||||
template_name = "question_table_view.html"
|
||||
|
||||
filterset_class = AnatomyQuestionFilter
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["app_name"] = "anatomy"
|
||||
return context
|
||||
|
||||
# def get_queryset(self):
|
||||
# qs = super().get_queryset()
|
||||
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ class CidUserExamTable(tables.Table):
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(
|
||||
data.prefetch_related(
|
||||
#"physics_exams", "rapid_exams", "sba_exams", "anatomy_exams", "longs_exams",
|
||||
"physics_exams", "rapid_exams", "sba_exams", "anatomy_exams", "longs_exams", "group"
|
||||
),
|
||||
*args,
|
||||
**kwargs,
|
||||
|
||||
@@ -7,11 +7,15 @@
|
||||
{% block content %}
|
||||
|
||||
<div id="view-filter-options">
|
||||
<details class="filter">
|
||||
<summary>
|
||||
<h3>Filter CID Users </h3>
|
||||
</summary>
|
||||
<form action="" method="get">
|
||||
{{ filter.form }}
|
||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||
</form>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends app_name|add:'/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<details>
|
||||
<details class="filter">
|
||||
<summary>
|
||||
Filter exam list
|
||||
</summary>
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="">
|
||||
<h1><a href="{% url app_name|add:':exam_overview' pk=exam.pk %}">Exam: {{ exam.name }} </a></h1>
|
||||
{% if exam.exam_mode %}
|
||||
{% if app_name in "rapids longs anatomy physics" %}
|
||||
{% if app_name in "rapids longs anatomy" %}
|
||||
{% if request.user.is_staff %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}">Mark answers</a>{% endif %}
|
||||
{% endif %}
|
||||
{% if request.user.is_staff %}<a href="{% url app_name|add:':exam_scores_cid' pk=exam.pk %}">Scores</a>{% endif %}
|
||||
|
||||
+5
-2
@@ -1370,7 +1370,10 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
by_question = defaultdict(dict)
|
||||
unmarked = set()
|
||||
|
||||
questions = exam.exam_questions.all().prefetch_related("answers")
|
||||
if self.app_name in ("physics", "sbas"):
|
||||
questions = exam.exam_questions.all()
|
||||
else:
|
||||
questions = exam.exam_questions.all().prefetch_related("answers")
|
||||
|
||||
# We could prefect the CidUserAnswers.answers here (if we didn't cache them)
|
||||
cid_user_answers = self.CidUserAnswer.objects.select_related("question").filter(
|
||||
@@ -1383,7 +1386,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
for cid_user_answer in cid_user_answers:
|
||||
# Convoluted (probably...)
|
||||
if cid_user_answer.user is None:
|
||||
cid = cid_user_answer.cid
|
||||
cid = f"c/{cid_user_answer.cid}"
|
||||
# cid_passcodes[cid] = cid_user_answer.passcode
|
||||
cids.add(cid)
|
||||
else:
|
||||
|
||||
@@ -150,6 +150,7 @@ class Long(models.Model):
|
||||
return authors
|
||||
|
||||
def __str__(self):
|
||||
return "{} / {}".format(self.pk, self.description)
|
||||
examinations = [series.get_examination() for series in self.series.all()]
|
||||
return "{}/{} : {}".format(self.pk, self.description, ", ".join(examinations))
|
||||
|
||||
|
||||
+21
-3
@@ -34,12 +34,12 @@ class LongImageColumn(tables.Column):
|
||||
|
||||
class LongSeriesImageColumn(tables.Column):
|
||||
def render(self, value):
|
||||
obj = value.first()
|
||||
obj = value.all()
|
||||
|
||||
if obj is None:
|
||||
if not obj:
|
||||
return "None"
|
||||
|
||||
image_object = obj.image
|
||||
image_object = obj[0].image
|
||||
try:
|
||||
thumbnailer = get_thumbnailer(image_object)
|
||||
return format_html('<img src="/media/{}" /><br/>[{}]', thumbnailer["exam-list"], value.count())
|
||||
@@ -73,6 +73,15 @@ class LongTable(tables.Table):
|
||||
fields = ("description", "history", "created_date", "author")
|
||||
sequence = ("view", "series", "exams")
|
||||
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(
|
||||
data.prefetch_related(
|
||||
"series", "author", "exams", "series__images", "series__examination", "series__plane", "series__contrast"
|
||||
),
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
class PopupLinkColumn(tables.Column):
|
||||
def render(self, value):
|
||||
return format_html("<span>test: {}<span>", value)
|
||||
@@ -111,6 +120,15 @@ class LongSeriesTable(tables.Table):
|
||||
fields = ("modality", "examination", "description", "author")
|
||||
sequence = ("view", "popup", "images", "long")
|
||||
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(
|
||||
data.prefetch_related(
|
||||
"long", "long__series", "images", "author", "examination", "plane", "contrast", "modality"
|
||||
),
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def render_popup(self, value, record):
|
||||
print(self)
|
||||
return format_html("""<a href="#" onclick="return window.create_popup_window('/longs/series/{}', 'Series')" >Popup</a>""", record.pk)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
{% extends 'longs/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Examinations</h1>
|
||||
<div class="longs">
|
||||
Active exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'longs:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'longs:mark_overview' pk=exam.pk %}" class="flex-col">Mark</a><a href="{% url 'longs:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
Inactive exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if not exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'longs:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'longs:mark_overview' pk=exam.pk %}" class="flex-col">Mark</a><a href="{% url 'longs:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -62,6 +62,15 @@ class QuestionTable(tables.Table):
|
||||
)
|
||||
sequence = ("view", "stem", "answers", "exams")
|
||||
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(
|
||||
data.prefetch_related(
|
||||
"category", "author", "exams"
|
||||
),
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def render_stem(self, value):
|
||||
return mark_safe(value)
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
{% extends 'physics/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Examinations</h1>
|
||||
<div class="physics">
|
||||
Active exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'physics:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'physics:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span>
|
||||
Publish results: <input type="checkbox" class="exam-publish-results-switch" data-posturl="{% url 'physics:exam_toggle_results_published' pk=exam.pk %}"
|
||||
{% if exam.publish_results %}checked{% endif %}>
|
||||
</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
Inactive exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if not exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'physics:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'physics:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<p>Active exams will be available to take at the below url:
|
||||
<a href="{% url 'physics:active_exams' %}">Available exams</a>
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,22 +0,0 @@
|
||||
{% extends 'physics/base.html' %}
|
||||
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="view-filter-options">
|
||||
<h3>Filter SBA Questions</h3>
|
||||
<form action="" method="get">
|
||||
|
||||
{{ filter.form }}
|
||||
|
||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||
</form>
|
||||
</div>
|
||||
{% autoescape off %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% render_table table %}
|
||||
{% endautoescape %}
|
||||
|
||||
{% endblock %}
|
||||
+7
-1
@@ -426,10 +426,16 @@ class QuestionView(
|
||||
):
|
||||
model = Question
|
||||
table_class = QuestionTable
|
||||
template_name = "physics/question_view.html"
|
||||
template_name = "question_table_view.html"
|
||||
|
||||
filterset_class = QuestionFilter
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["app_name"] = "physics"
|
||||
return context
|
||||
|
||||
|
||||
|
||||
class UserAnswerView(AuthorOrCheckerRequiredMixin, LoginRequiredMixin, DetailView):
|
||||
model = CidUserAnswer
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
import django_filters
|
||||
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
class UserListFilter(django_filters.FilterSet):
|
||||
|
||||
userprofile__grade = django_filters.CharFilter(label="Grade")
|
||||
|
||||
class Meta:
|
||||
model= User
|
||||
fields = {
|
||||
"username": ["contains"], "email": ["contains"], "userprofile__grade": ["exact"]
|
||||
}
|
||||
|
||||
#def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
|
||||
# super().__init__(data, queryset, request=request, prefix=prefix)
|
||||
# self.filters["userprofile__grade"].label == "Grade"
|
||||
@@ -53,6 +53,7 @@ urlpatterns = [
|
||||
path("sbas/", include("sbas.urls"), name="sbas"),
|
||||
path("generic/", include("generic.urls"), name="generic"),
|
||||
path("atlas/", include("atlas.urls"), name="atlas"),
|
||||
path("accounts/bulk_create/", views.accounts_bulk_create, name="accounts_bulk_create"),
|
||||
path("accounts/update/<str:slug>/", views.UpdateUserView.as_view(), name="account_update"),
|
||||
path("accounts/update_profile/<str:slug>/", views.UpdateUserProfileView.as_view(), name="account_profile_update"),
|
||||
path("accounts/", views.UserListView.as_view(), name="accounts_list"),
|
||||
|
||||
+98
-2
@@ -6,6 +6,8 @@ from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django import forms
|
||||
|
||||
from django.utils.html import format_html
|
||||
|
||||
# from django.contrib.auth.models import User
|
||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
from django.contrib.auth.models import User
|
||||
@@ -35,6 +37,7 @@ from anatomy.models import CidUserAnswer as AnatomyCidUserAnswer
|
||||
from anatomy.models import Exam as AnatomyExam
|
||||
from anatomy.models import AnatomyQuestion
|
||||
from anatomy.models import Answer as AnatomyAnswer
|
||||
from rad.filters import UserListFilter
|
||||
|
||||
from rapids.models import CidUserAnswer as RapidsCidUserAnswer
|
||||
from rapids.models import Exam as RapidsExam
|
||||
@@ -54,7 +57,9 @@ from rapids.views import GenericExamViews as RapidsExamsViews
|
||||
from longs.views import GenericExamViews as LongsExamViews
|
||||
|
||||
from generic.forms import QuestionNoteForm
|
||||
from generic.models import CidUser, QuestionNote, UserProfile
|
||||
from generic.models import CidUser, QuestionNote, UserGrades, UserProfile
|
||||
|
||||
from django_filters.views import FilterView
|
||||
|
||||
import json
|
||||
|
||||
@@ -440,10 +445,12 @@ def server_error(request):
|
||||
return response
|
||||
|
||||
|
||||
class UserListView(CidManagerRequiredMixin, ListView):
|
||||
class UserListView(CidManagerRequiredMixin, FilterView):
|
||||
model = User
|
||||
template_name = "user_list_view.html"
|
||||
|
||||
filterset_class = UserListFilter
|
||||
|
||||
|
||||
class UpdateUserView(CidManagerRequiredMixin, UpdateView):
|
||||
model = User
|
||||
@@ -500,3 +507,92 @@ class UpdateUserProfileView(CidManagerRequiredMixin, UpdateView):
|
||||
#
|
||||
# def get(self, request, *args, **kwargs):
|
||||
# return self.post(request, *args, **kwargs)
|
||||
|
||||
|
||||
@user_is_cid_user_manager
|
||||
def accounts_bulk_create(request):
|
||||
if request.method == "POST":
|
||||
context = {}
|
||||
|
||||
users = json.loads(request.POST.get("usersjson"))
|
||||
print(users)
|
||||
|
||||
created_users = []
|
||||
existing_users = []
|
||||
error_text = ""
|
||||
|
||||
try:
|
||||
users = json.loads(request.POST.get("usersjson"))
|
||||
print(users)
|
||||
|
||||
for user in users:
|
||||
if not User.objects.filter(username=user["email"]).exists():
|
||||
try:
|
||||
print(user)
|
||||
user_dict = {
|
||||
"username": user["email"],
|
||||
"first_name": user["first_name"],
|
||||
"last_name": user["last_name"],
|
||||
"email": user["email"],
|
||||
}
|
||||
new_user = User.objects.create_user(**user_dict)
|
||||
except Exception as error:
|
||||
error_text = (
|
||||
error_text
|
||||
+ f"<p>Error creating user: { user['email'] }<br/>{error}</p>"
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
user_profile = UserProfile.objects.get(user=new_user)
|
||||
user_profile.peninsula_trainee = True
|
||||
if "supervisor_email" in user:
|
||||
user_profile.supervisor_email = user["supervisor_email"]
|
||||
if "supervisor_name" in user:
|
||||
user_profile.supervisor_name = user["supervisor_name"]
|
||||
|
||||
|
||||
if "grade" in user:
|
||||
grade = UserGrades.objects.get(name=user["grade"])
|
||||
user_profile.grade = grade
|
||||
|
||||
user_profile.save()
|
||||
|
||||
created_users.append(user["email"])
|
||||
except Exception as error:
|
||||
error_text = (
|
||||
error_text
|
||||
+ f"<p>Error creating user profile: { user['email'] }<br/>{error}</p>"
|
||||
)
|
||||
else:
|
||||
existing_users.append(user["email"])
|
||||
|
||||
except Exception as error:
|
||||
error_text = error_text + f"<p>Error creating users.<br/>{error}</p>"
|
||||
|
||||
context["error"] = format_html(error_text)
|
||||
context["created_users"] = created_users
|
||||
|
||||
if existing_users:
|
||||
context["message"] = "The following users already exist: " + ", ".join(
|
||||
existing_users
|
||||
)
|
||||
# if not self.check_user_edit_access(request.user):
|
||||
# data = {"status": "error, insufficient permission"}
|
||||
# return JsonResponse(data, status=400)
|
||||
|
||||
# exam = get_object_or_404(self.Exam, pk=pk)
|
||||
|
||||
# exam.publish_results = (
|
||||
# True if request.POST.get("publish_results") == "true" else False
|
||||
# )
|
||||
# exam.save()
|
||||
# data = {
|
||||
# "status": "success",
|
||||
# "publish_results": exam.publish_results,
|
||||
# "name": exam.name,
|
||||
# "id": exam.id,
|
||||
# }
|
||||
return render(request, "accounts_bulk_create.html", context)
|
||||
else:
|
||||
return render(request, "accounts_bulk_create.html", {})
|
||||
|
||||
@@ -526,6 +526,8 @@ class RapidImage(models.Model):
|
||||
|
||||
is_dicom = models.BooleanField(default=False)
|
||||
|
||||
|
||||
|
||||
def image_tag(self):
|
||||
if self.image:
|
||||
return mark_safe(
|
||||
|
||||
+4
-4
@@ -12,12 +12,12 @@ from easy_thumbnails.exceptions import InvalidImageFormatError
|
||||
|
||||
class ImageColumn(tables.Column):
|
||||
def render(self, value):
|
||||
obj = value.first()
|
||||
obj = value.all()
|
||||
|
||||
if obj is None:
|
||||
if not obj:
|
||||
return format_html('<span>No image<span>')
|
||||
|
||||
image_object = obj.image
|
||||
image_object = obj[0].image
|
||||
try:
|
||||
thumbnailer = get_thumbnailer(image_object)
|
||||
return format_html('<img src="/media/{}" />', thumbnailer["exam-list"])
|
||||
@@ -60,7 +60,7 @@ class RapidTable(tables.Table):
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(
|
||||
data.prefetch_related(
|
||||
"abnormality", "region", "examination", "images", "exams", "author"
|
||||
"abnormality", "region", "examination", "images", "exams", "author"
|
||||
),
|
||||
*args,
|
||||
**kwargs,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
{% extends 'rapids/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Examinations</h1>
|
||||
<div class="rapids">
|
||||
Active exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'rapids:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'rapids:mark_overview' pk=exam.pk %}" class="flex-col">Mark</a><a href="{% url 'rapids:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
Inactive exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if not exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'rapids:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'rapids:mark_overview' pk=exam.pk %}" class="flex-col">Mark</a><a href="{% url 'rapids:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -8,12 +8,17 @@ APP_NAMES = ("rapids", "anatomy", "longs", "physics", "sbas")
|
||||
def create_superuser(db, django_user_model):
|
||||
return django_user_model.objects.create_superuser("admin", "ross@xkjq.uk", "adminpassword")
|
||||
|
||||
#@pytest.fixture
|
||||
#def create_exam(db):
|
||||
# return Exam.objects.create(name="test exam", exam_mode=True)
|
||||
|
||||
# Very basic tests, make sure we can hit key urls without error
|
||||
|
||||
@pytest.mark.parametrize("app_name", APP_NAMES)
|
||||
def test_index(client, create_superuser, app_name):
|
||||
client.login(username="admin", password="adminpassword")
|
||||
response = client.get(reverse(f'{app_name}:index'))
|
||||
print(reverse(f'{app_name}:index'))
|
||||
print(response.status_code)
|
||||
print(response)
|
||||
print(response.content)
|
||||
assert response.status_code == 200
|
||||
+5
-1
@@ -504,10 +504,14 @@ def get_region_id(request):
|
||||
class RapidView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
||||
model = Rapid
|
||||
table_class = RapidTable
|
||||
template_name = "rapids/view.html"
|
||||
template_name = "question_table_view.html"
|
||||
|
||||
filterset_class = RapidFilter
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["app_name"] = "rapids"
|
||||
return context
|
||||
|
||||
|
||||
@login_required
|
||||
|
||||
@@ -56,6 +56,15 @@ class QuestionTable(tables.Table):
|
||||
)
|
||||
sequence = ("view", "stem", "answers", "exams")
|
||||
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(
|
||||
data.prefetch_related(
|
||||
"category", "exams", "author"
|
||||
),
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def render_stem(self, value):
|
||||
return mark_safe(value)
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
{% extends 'sbas/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Examinations</h1>
|
||||
<div class="sbas">
|
||||
Active exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'sbas:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'sbas:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
Inactive exams:<br/>
|
||||
<ul class="exam-list">
|
||||
{% for exam in exams %}
|
||||
{% if not exam.active %}
|
||||
<li>
|
||||
<a href="{% url 'sbas:exam_overview' pk=exam.pk %}" class="flex-col">{{exam.name}}</a> <a href="{% url 'sbas:exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a> <span class="flex-col icon-container"><span class="published-icon {% if exam.publish_results %}published{% endif %}">Results Published</span></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<p>Active exams will be available to take at the below url:
|
||||
<a href="{% url 'sbas:active_exams' %}">Available exams</a>
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
+6
-1
@@ -293,10 +293,15 @@ GenericExamViews = ExamViews(
|
||||
class QuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
||||
model = Question
|
||||
table_class = QuestionTable
|
||||
template_name = "sbas/question_view.html"
|
||||
template_name = "question_table_view.html"
|
||||
|
||||
filterset_class = QuestionFilter
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["app_name"] = "anatomy"
|
||||
return context
|
||||
|
||||
|
||||
class UserAnswerView(LoginRequiredMixin, DetailView):
|
||||
model = CidUserAnswer
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
|
||||
{% extends 'generic/base.html' %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% if created_users %}
|
||||
<div class="alert alert-success">
|
||||
The follow users have been created: {{created_users}}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if error %}
|
||||
<div class="alert alert-warning">
|
||||
{{error}}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if message %}
|
||||
<div class="alert alert-info">
|
||||
{{message}}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p>
|
||||
Prepare a spreadsheet with the following format.
|
||||
|
||||
<table>
|
||||
|
||||
<tr><th>First name</th><th>Last name</th><th>Email</th><th>Grade</th><th>Supervisor</th><th>Supervisor email</th></tr>
|
||||
<tr><td>name 1</td><td>last name 1</td><td>email 1</td><td>grade 1</td><td>supervisor 1</td><td>Supervisor email 1</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
On your spreadsheet then "copy" the data (not including header), come to this page, put focus on the <textarea> box and "paste" the text in. In some instance (large data sets) this might take a couple of seconds.
|
||||
Once there simply click the button and check the users that will be created below.
|
||||
</p>
|
||||
<textarea id="csv" placeholder="Paste users content here" style="width: 300px; height: 100px;"></textarea><br/>
|
||||
<input type="button" value="Load Data" onclick="createTable()" >
|
||||
|
||||
|
||||
<div>
|
||||
<ul id="users-list">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form id="user-list-form" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="text" name="usersjson" id="user-list-json" hidden>
|
||||
<input type="submit" id="submit-users" name="submit-users" value="Create Users" style="display: none">
|
||||
</form>
|
||||
|
||||
|
||||
<script>
|
||||
function createTable() {
|
||||
|
||||
// Get the data
|
||||
var excelData = document.getElementById('csv').value.trim()+"\n";
|
||||
|
||||
// split into rows
|
||||
excelRow = excelData.split(String.fromCharCode(10));
|
||||
|
||||
// split rows into columns
|
||||
for (i=0; i<excelRow.length; i++) {
|
||||
excelRow[i] = excelRow[i].split(String.fromCharCode(9));
|
||||
|
||||
}
|
||||
|
||||
// start to create the HTML table
|
||||
var myTable = document.createElement("table");
|
||||
var myTbody = document.createElement("tbody");
|
||||
|
||||
// Loop over the rows
|
||||
|
||||
var elements = ["first_name", "last_name", "grade", "email", "supervisor_name", "supervisor_email"];
|
||||
var users = [];
|
||||
|
||||
$("#users-list").empty();
|
||||
$("#users-list").append(`The following users will be created. "undefined" fields will be left blank. Emails will be used as usernames.`);
|
||||
|
||||
for (i=0; i<excelRow.length - 1; i++) {
|
||||
//users[i] = {};
|
||||
user = {};
|
||||
|
||||
// create a row in the HTML table
|
||||
var myRow = document.createElement("tr");
|
||||
|
||||
// Loop over the columns and add TD to the TR
|
||||
for (j=0; j<excelRow[i].length; j++) {
|
||||
// Loop over the row columns
|
||||
if (excelRow[i][j].length != 0) {
|
||||
var myCell = document.createElement("td");
|
||||
myCell.innerHTML = excelRow[i][j];
|
||||
//users[i][elements[j]] = excelRow[i][j];
|
||||
user[elements[j]] = excelRow[i][j];
|
||||
}
|
||||
myRow.appendChild(myCell);
|
||||
}
|
||||
myTbody.appendChild(myRow);
|
||||
users.push(user)
|
||||
$("#users-list").append(`<li>Name: ${user.first_name} ${user.last_name}<br/>Email: ${user.email}<br/>Grade: ${user.grade}<br/>Supervisor: ${user.supervisor_name}<br/>Supervisor email: ${user.supervisor_email}</li>`)
|
||||
}
|
||||
myTable.appendChild(myTbody);
|
||||
|
||||
$("#users-list").append(`${users.length} users to create.`)
|
||||
|
||||
$("#user-list-json").val(JSON.stringify(users));
|
||||
$("#submit-users").show();
|
||||
|
||||
// If we want to show the table
|
||||
//document.body.appendChild(myTable);
|
||||
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
table{
|
||||
{% comment %} border-collapse: collapse; {% endcomment %}
|
||||
border: 1px solid silver;
|
||||
}
|
||||
tr{
|
||||
border-bottom: 1px solid silver;
|
||||
}
|
||||
td, th {
|
||||
border: 1px solid silver;
|
||||
padding: 2px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
|
||||
}
|
||||
|
||||
#users-list li {
|
||||
border: 1px solid blue;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
{% extends app_name|add:'/base.html' %}
|
||||
|
||||
{% load render_table from django_tables2 %}
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="view-filter-options">
|
||||
<details class="filter">
|
||||
<summary>
|
||||
<h3>Filter questions</h3>
|
||||
</summary>
|
||||
<form action="" method="get">
|
||||
{{ filter.form }}
|
||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||
</form>
|
||||
</details>
|
||||
</div>
|
||||
{% render_table table %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -2,14 +2,29 @@
|
||||
|
||||
{% block content %}
|
||||
<h2>Users</h2>
|
||||
<details class="filter">
|
||||
<summary>
|
||||
Filter users list
|
||||
</summary>
|
||||
<form method="get">
|
||||
{{ filter.form.as_p }}
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</details>
|
||||
<ul>
|
||||
{% for user in object_list %}
|
||||
<li>Username: <a href="{% url 'account_profile' user.username %}">{{ user.username }}</a><br/>
|
||||
Name: {{user.first_name}} {{user.last_name}}<br/>
|
||||
Registration number: {{user.registration_number}}
|
||||
<button class="small-url-button"><a href="{% url 'account_update' user.username %}">Edit user</a></button>
|
||||
<button class="small-url-button"><a href="{% url 'account_profile_update' user.username %}">Edit profile</a></button>
|
||||
{% for list_user in object_list %}
|
||||
<li>Username: <a href="{% url 'account_profile' list_user.username %}">{{ list_user.username }}</a><br/>
|
||||
Name: {{list_user.first_name}} {{list_user.last_name}}<br/>
|
||||
Registration number: {{list_user.registration_number}}
|
||||
<button class="small-url-button"><a href="{% url 'account_update' list_user.username %}">Edit user</a></button>
|
||||
<button class="small-url-button"><a href="{% url 'account_profile_update' list_user.username %}">Edit profile</a></button>
|
||||
|
||||
{% if user.is_superuser %}
|
||||
<button class="small-url-button"><a href="{% url 'admin:auth_user_change' list_user.id %}">Admin edit</a></button>
|
||||
<button class="small-url-button"><a href="{% url 'admin:auth_user_delete' list_user.id %}">Delete</a></button>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user