Merge branch 'master' of gitssh://web.xkjq.uk:30001/ross/rad
This commit is contained in:
+17
-1
@@ -1,6 +1,22 @@
|
||||
import django_filters
|
||||
|
||||
from .models import AnatomyQuestion
|
||||
from .models import AnatomyQuestion, CidUserAnswer
|
||||
|
||||
#from generic.filters import UserAnswerFilter
|
||||
|
||||
|
||||
class AnatomyUserAnswerFilter(django_filters.FilterSet):
|
||||
class Meta:
|
||||
model= CidUserAnswer
|
||||
fields = (
|
||||
"cid",
|
||||
"question",
|
||||
"answer",
|
||||
#"answer_compare",
|
||||
"exam",
|
||||
"created",
|
||||
"updated",
|
||||
)
|
||||
|
||||
|
||||
class AnatomyQuestionFilter(django_filters.FilterSet):
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.1.3 on 2021-02-09 22:39
|
||||
|
||||
import anatomy.models
|
||||
import django.core.files.storage
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('anatomy', '0028_auto_20210206_0905'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='anatomyquestion',
|
||||
name='image',
|
||||
field=models.ImageField(storage=django.core.files.storage.FileSystemStorage(base_url='/media/anatomy/', location='/home/ross/rad/media//anatomy/'), upload_to=anatomy.models.image_directory_path),
|
||||
),
|
||||
]
|
||||
@@ -310,41 +310,6 @@ class Exam(ExamBase):
|
||||
return exam_json
|
||||
|
||||
|
||||
# class UserAnswer(models.Model):
|
||||
# question = models.ForeignKey(
|
||||
# AnatomyQuestion, related_name="user_answers", on_delete=models.CASCADE
|
||||
# )
|
||||
# answer = models.TextField(max_length=500, blank=True)
|
||||
# user = models.ForeignKey(
|
||||
# settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL
|
||||
# )
|
||||
|
||||
# cid = models.BigIntegerField(blank=True, null=True, help_text="Candidate ID (limitied by BigIntegerField size)")
|
||||
|
||||
# # Each user answer is associated with a particular exam
|
||||
# exam = models.ForeignKey(
|
||||
# Exam, related_name="exam", on_delete=models.CASCADE, null=True
|
||||
# )
|
||||
|
||||
# flagged = models.BooleanField(default=False)
|
||||
|
||||
# created = models.DateTimeField(auto_now_add=True)
|
||||
# updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
# def __str__(self):
|
||||
# try:
|
||||
# exam = self.exam
|
||||
# except (Exam.DoesNotExist, KeyError) as e:
|
||||
# exam = "None"
|
||||
# return "{}/{}/{}: {}".format(
|
||||
# exam, self.cid, self.question.GetPrimaryAnswer(), self.answer
|
||||
# )
|
||||
|
||||
# def clean(self):
|
||||
# if self.answer:
|
||||
# self.answer = self.answer.strip()
|
||||
|
||||
|
||||
class CidUserAnswer(models.Model):
|
||||
"""User answers by candidate"""
|
||||
|
||||
|
||||
+22
-1
@@ -1,7 +1,7 @@
|
||||
import django_tables2 as tables
|
||||
from django_tables2.utils import A
|
||||
|
||||
from .models import AnatomyQuestion
|
||||
from .models import AnatomyQuestion, CidUserAnswer
|
||||
|
||||
from django.utils.html import format_html
|
||||
|
||||
@@ -9,6 +9,27 @@ from easy_thumbnails.files import get_thumbnailer
|
||||
|
||||
from easy_thumbnails.exceptions import InvalidImageFormatError
|
||||
|
||||
#from generic.tables import UserAnswerTable
|
||||
|
||||
|
||||
class AnatomyUserAnswerTable(tables.Table):
|
||||
select = tables.CheckBoxColumn(accessor=("pk"))
|
||||
delete = tables.LinkColumn(
|
||||
"anatomy:anatomy_user_answer_delete", text="Delete", args=[A("pk")], orderable=False
|
||||
)
|
||||
class Meta:
|
||||
model = CidUserAnswer
|
||||
template_name = "django_tables2/bootstrap4.html"
|
||||
fields = (
|
||||
"cid",
|
||||
"question",
|
||||
"answer",
|
||||
#"answer_compare",
|
||||
"exam",
|
||||
"created",
|
||||
"updated",
|
||||
)
|
||||
|
||||
|
||||
class ImageColumn(tables.Column):
|
||||
def render(self, value):
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<form method="post">{% csrf_token %}
|
||||
<p>Are you sure you want to delete "{{ object }}"?</p>
|
||||
<input type="submit" value="Confirm">
|
||||
</form>
|
||||
@@ -51,4 +51,6 @@ urlpatterns = [
|
||||
path("examination/ajax/get_examination_id",
|
||||
views.get_examination_id,
|
||||
name="get_examination_id"),
|
||||
path("user_answers/", views.AnatomyUserAnswerView.as_view(), name="anatomy_user_answer_view"),
|
||||
path("user_answers/<int:pk>/delete", views.UserAnswerDelete.as_view(), name="anatomy_user_answer_delete"),
|
||||
]
|
||||
|
||||
+16
-3
@@ -46,8 +46,8 @@ from .models import (
|
||||
from generic.models import Examination
|
||||
from generic.views import ExamViews
|
||||
|
||||
from .tables import AnatomyQuestionTable
|
||||
from .filters import AnatomyQuestionFilter
|
||||
from .tables import AnatomyQuestionTable, AnatomyUserAnswerTable
|
||||
from .filters import AnatomyQuestionFilter, AnatomyUserAnswerFilter
|
||||
|
||||
from django_tables2 import SingleTableView, SingleTableMixin
|
||||
from django_filters.views import FilterView
|
||||
@@ -62,6 +62,8 @@ from helpers.images import image_as_base64
|
||||
|
||||
from django.template.defaulttags import register
|
||||
|
||||
from generic.mixins import SuperuserRequiredMixin
|
||||
|
||||
|
||||
@register.filter
|
||||
def get_item(dictionary, key):
|
||||
@@ -919,6 +921,13 @@ class AnatomyQuestionView(SingleTableMixin, FilterView):
|
||||
|
||||
filterset_class = AnatomyQuestionFilter
|
||||
|
||||
class AnatomyUserAnswerView(SingleTableMixin, FilterView):
|
||||
model = CidUserAnswer
|
||||
table_class = AnatomyUserAnswerTable
|
||||
template_name = "anatomy/anatomy_question_view.html"
|
||||
|
||||
filterset_class = AnatomyUserAnswerFilter
|
||||
|
||||
|
||||
def question_save_annotation(request, pk):
|
||||
if request.is_ajax() and request.method == "POST":
|
||||
@@ -935,4 +944,8 @@ def question_save_annotation(request, pk):
|
||||
return JsonResponse(data, status=400)
|
||||
|
||||
|
||||
AnatomyExamViews = ExamViews(Exam, "anatomy", "anatomy", loadJsonAnswer)
|
||||
AnatomyExamViews = ExamViews(Exam, "anatomy", "anatomy", loadJsonAnswer)
|
||||
|
||||
class UserAnswerDelete(SuperuserRequiredMixin, DeleteView):
|
||||
model = CidUserAnswer
|
||||
success_url = reverse_lazy("anatomy:anatomy_user_answer_view")
|
||||
@@ -0,0 +1,18 @@
|
||||
import django_filters
|
||||
|
||||
|
||||
|
||||
#class UserAnswerFilter(django_filters.FilterSet):
|
||||
# class Meta:
|
||||
# #model = AnatomyQuestion
|
||||
# abstract = True
|
||||
# fields = (
|
||||
# "cid",
|
||||
# "question",
|
||||
# "answer",
|
||||
# #"answer_compare",
|
||||
# "exam",
|
||||
# "created",
|
||||
# "updateded",
|
||||
# "created_date",
|
||||
# )
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||
|
||||
|
||||
class SuperuserRequiredMixin(UserPassesTestMixin):
|
||||
def test_func(self):
|
||||
return self.request.user.is_superuser
|
||||
@@ -0,0 +1,47 @@
|
||||
import django_tables2 as tables
|
||||
from django_tables2.utils import A
|
||||
|
||||
from django.utils.html import format_html
|
||||
|
||||
from easy_thumbnails.files import get_thumbnailer
|
||||
|
||||
from easy_thumbnails.exceptions import InvalidImageFormatError
|
||||
|
||||
|
||||
class ImageColumn(tables.Column):
|
||||
def render(self, value):
|
||||
try:
|
||||
thumbnailer = get_thumbnailer(value)
|
||||
return format_html('<img src="/media/{}" />', thumbnailer["exam-list"])
|
||||
except InvalidImageFormatError:
|
||||
return format_html('<span title="{}">Invalid image url<span>', value)
|
||||
|
||||
|
||||
#class UserAnswerTable(tables.Table):
|
||||
# #edit = tables.LinkColumn(
|
||||
# # "anatomy:anatomy_question_update", text="Edit", args=[A("pk")], orderable=False
|
||||
# #)
|
||||
# #view = tables.LinkColumn(
|
||||
# # "anatomy:question_detail", text="View", args=[A("pk")], orderable=False
|
||||
# #)
|
||||
# #image = ImageColumn("image", orderable=False)
|
||||
# # clone = tables.LinkColumn('anatomy:anatomy_question_clone',
|
||||
# # text='Clone',
|
||||
# # args=[A('pk')],
|
||||
# # orderable=False)
|
||||
# #exams = tables.ManyToManyColumn(verbose_name="Exams")
|
||||
#
|
||||
# class Meta:
|
||||
# #model = AnatomyQuestion
|
||||
# abstract = True
|
||||
# template_name = "django_tables2/bootstrap4.html"
|
||||
# fields = (
|
||||
# "cid",
|
||||
# "question",
|
||||
# "answer",
|
||||
# #"answer_compare",
|
||||
# "exam",
|
||||
# "created",
|
||||
# "updated",
|
||||
# )
|
||||
# #sequence = ("view")
|
||||
+1
-1
@@ -210,7 +210,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
return JsonResponse(active_exams)
|
||||
|
||||
@method_decorator(csrf_exempt)
|
||||
def postExamAnswers(request):
|
||||
def postExamAnswers(self, request):
|
||||
if request.is_ajax and request.method == "POST":
|
||||
|
||||
n = 0
|
||||
|
||||
+3
-1
@@ -21,6 +21,8 @@ def image_as_base64(image_file):
|
||||
# encoded_string = base64.b64encode(img_f.read())
|
||||
encoded_string = base64.b64encode(image_file.file.read())
|
||||
mimetype, enc = mimetypes.guess_type(image_file.path)
|
||||
if "dicom" in mimetype:
|
||||
return "data:{};base64,{}".format(mimetype, encoded_string.decode("utf-8"))
|
||||
return "data:image/{};base64,{}".format(mimetype, encoded_string.decode("utf-8"))
|
||||
|
||||
|
||||
@@ -54,4 +56,4 @@ def pil_dicom_image(source, exif_orientation=True, **options):
|
||||
|
||||
#if exif_orientation:
|
||||
# image = utils.exif_orientation(image)
|
||||
return image
|
||||
return image
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.1.3 on 2021-02-11 18:27
|
||||
|
||||
import django.core.files.storage
|
||||
from django.db import migrations, models
|
||||
import longs.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('longs', '0013_auto_20210206_2147'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='longseriesimage',
|
||||
name='image',
|
||||
field=models.FileField(storage=django.core.files.storage.FileSystemStorage(base_url='/media/longs/', location='/home/ross/rad/media/longs/'), upload_to=longs.models.image_directory_path),
|
||||
),
|
||||
]
|
||||
+61
-3
@@ -15,9 +15,13 @@ from django.urls import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.html import mark_safe
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from sortedm2m.fields import SortedManyToManyField
|
||||
|
||||
import string
|
||||
from collections import defaultdict
|
||||
from helpers.images import image_as_base64
|
||||
|
||||
from anatomy.models import Modality
|
||||
|
||||
@@ -30,7 +34,7 @@ from easy_thumbnails.exceptions import InvalidImageFormatError
|
||||
|
||||
image_storage = FileSystemStorage(
|
||||
# Physical file location ROOT
|
||||
location=u"{0}/longs/".format(settings.MEDIA_ROOT),
|
||||
location=u"{0}longs/".format(settings.MEDIA_ROOT),
|
||||
# Url for file
|
||||
base_url=u"{0}longs/".format(settings.MEDIA_URL),
|
||||
)
|
||||
@@ -139,8 +143,11 @@ class Long(models.Model):
|
||||
# return self.GetImages()
|
||||
|
||||
|
||||
def test_image_validator(file):
|
||||
pass
|
||||
|
||||
class LongSeriesImage(models.Model):
|
||||
image = models.ImageField(upload_to=image_directory_path, storage=image_storage)
|
||||
image = models.FileField(upload_to=image_directory_path, storage=image_storage)
|
||||
position = models.IntegerField(default=0)
|
||||
series = models.ForeignKey(
|
||||
"LongSeries", related_name="images", on_delete=models.SET_NULL, null=True
|
||||
@@ -200,7 +207,7 @@ class LongSeries(models.Model):
|
||||
return authors
|
||||
|
||||
def get_author_display(self):
|
||||
return ", ".join(self.get_author_objects())
|
||||
return ", ".join([i.username for i in self.get_author_objects()])
|
||||
|
||||
def get_examination(self):
|
||||
"""Returns a comma seperated text list of regions"""
|
||||
@@ -303,6 +310,57 @@ class Exam(ExamBase):
|
||||
default=4500,
|
||||
)
|
||||
|
||||
def get_exam_json(self):
|
||||
questions = self.exam_questions.all()
|
||||
|
||||
exam_questions = defaultdict(dict)
|
||||
|
||||
exam_order = []
|
||||
|
||||
for q in questions:
|
||||
exam_order.append(q.id)
|
||||
|
||||
# Loop through longimage associations
|
||||
images = []
|
||||
image_titles = []
|
||||
for series in q.series.all():
|
||||
image_array = []
|
||||
for i in series.images.all():
|
||||
image_array.append(image_as_base64(i.image))
|
||||
#image_array.append(i.image.url)
|
||||
images.append(image_array)
|
||||
image_titles.append(series.get_examination())
|
||||
|
||||
|
||||
|
||||
exam_questions[q.id] = {
|
||||
"title": q.history,
|
||||
"images": images,
|
||||
"image_titles": image_titles,
|
||||
#"feedback_image": [],
|
||||
#"annotations": [str(q.image_annotations)],
|
||||
"type": "long",
|
||||
}
|
||||
|
||||
#if feedback_images:
|
||||
# exam_questions[q.id]["feedback_image"] = feedback_images
|
||||
|
||||
|
||||
exam_json = {
|
||||
"eid": "long/{}".format(self.id),
|
||||
"cached": False,
|
||||
"exam_type": "long",
|
||||
"exam_name": self.name,
|
||||
"exam_mode": True,
|
||||
"exam_order": exam_order,
|
||||
"questions": exam_questions,
|
||||
}
|
||||
|
||||
|
||||
if self.time_limit:
|
||||
exam_json["exam_time"] = self.time_limit
|
||||
|
||||
return exam_json
|
||||
|
||||
class CidUserAnswer(models.Model):
|
||||
"""User answers by candidate"""
|
||||
|
||||
@@ -31,6 +31,9 @@ class LongSeriesImageColumn(tables.Column):
|
||||
def render(self, value):
|
||||
obj = value.first()
|
||||
|
||||
if obj is None:
|
||||
return "None"
|
||||
|
||||
image_object = obj.image
|
||||
try:
|
||||
thumbnailer = get_thumbnailer(image_object)
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ urlpatterns = [
|
||||
path("exam/submit", views.LongExamViews.postExamAnswers, name="exam_answers_submit"),
|
||||
path("exam/", views.LongExamViews.exam_list, name="exam_list"),
|
||||
path("exam/json/", views.LongExamViews.active_exams, name="active_exams"),
|
||||
path("exam/json/<int:pk>", views.exam_json, name="exam_json"),
|
||||
path("exam/json/<int:pk>", views.LongExamViews.exam_json, name="exam_json"),
|
||||
path(
|
||||
"exam/json/<int:pk>/recreate",
|
||||
views.LongExamViews.exam_json_recreate,
|
||||
|
||||
@@ -796,75 +796,6 @@ def mark(request, pk, sk):
|
||||
)
|
||||
|
||||
|
||||
|
||||
def exam_json(request, pk):
|
||||
|
||||
exam = get_object_or_404(Exam, pk=pk)
|
||||
|
||||
if not exam.active:
|
||||
raise Http404("No available exam")
|
||||
|
||||
exam_json_cache = cache.get("longs_exam_json_{}".format(pk))
|
||||
|
||||
if exam_json_cache is not None and not exam.recreate_json:
|
||||
exam_json_cache["cached"] = True
|
||||
return JsonResponse(exam_json_cache)
|
||||
|
||||
questions = exam.exam_questions.all()
|
||||
|
||||
exam_questions = defaultdict(dict)
|
||||
|
||||
exam_order = []
|
||||
|
||||
for q in questions:
|
||||
exam_order.append(q.id)
|
||||
|
||||
# Loop through longimage associations
|
||||
images = []
|
||||
feedback_images = []
|
||||
for i in q.images.all():
|
||||
if i.feedback_image == True:
|
||||
feedback_images.append(image_as_base64(i.image))
|
||||
# feedback_images.append(i.image.url)
|
||||
else:
|
||||
images.append(image_as_base64(i.image))
|
||||
#images.append(i.image.url)
|
||||
|
||||
|
||||
|
||||
exam_questions[q.id] = {
|
||||
"images": images,
|
||||
#"feedback_image": [],
|
||||
#"annotations": [str(q.image_annotations)],
|
||||
"type": "long",
|
||||
}
|
||||
|
||||
#if feedback_images:
|
||||
# exam_questions[q.id]["feedback_image"] = feedback_images
|
||||
|
||||
|
||||
exam_json = {
|
||||
"eid": "long/{}".format(exam.id),
|
||||
"cached": False,
|
||||
"exam_type": "long",
|
||||
"exam_name": exam.name,
|
||||
"exam_mode": True,
|
||||
"exam_order": exam_order,
|
||||
"questions": exam_questions,
|
||||
}
|
||||
|
||||
|
||||
if exam.time_limit:
|
||||
exam_json["exam_time"] = exam.time_limit
|
||||
|
||||
exam.recreate_json = False
|
||||
exam.save()
|
||||
|
||||
cache.set("longs_exam_json_{}".format(pk), exam_json, 3600)
|
||||
|
||||
return JsonResponse(exam_json)
|
||||
|
||||
|
||||
@login_required
|
||||
def exam_scores_cid(request, pk):
|
||||
exam = get_object_or_404(Exam, pk=pk)
|
||||
|
||||
@@ -50,6 +50,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.staticfiles',
|
||||
'debug_toolbar',
|
||||
"tagulous",
|
||||
"dbbackup",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@@ -199,8 +200,14 @@ THUMBNAIL_ALIASES = {
|
||||
}
|
||||
|
||||
THUMBNAIL_SOURCE_GENERATORS = ('easy_thumbnails.source_generators.pil_image', 'helpers.images.pil_dicom_image')
|
||||
DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
||||
DBBACKUP_STORAGE_OPTIONS = {'location': os.path.join(BASE_DIR, '../backups')}
|
||||
|
||||
try:
|
||||
from .settings_local import *
|
||||
except ImportError:
|
||||
pass
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
>>>>>>> 5763aae287cbceee318f975495f7d9429517c88f
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.1.3 on 2021-02-09 22:39
|
||||
|
||||
import django.core.files.storage
|
||||
from django.db import migrations, models
|
||||
import rapids.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rapids', '0012_auto_20210206_0905'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='rapidimage',
|
||||
name='image',
|
||||
field=models.ImageField(storage=django.core.files.storage.FileSystemStorage(base_url='/media/rapids/', location='/home/ross/rad/media//rapids/'), upload_to=rapids.models.image_directory_path),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.1.3 on 2021-02-11 20:34
|
||||
|
||||
import django.core.files.storage
|
||||
from django.db import migrations, models
|
||||
import rapids.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rapids', '0013_auto_20210209_2239'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='rapidimage',
|
||||
name='image',
|
||||
field=models.FileField(storage=django.core.files.storage.FileSystemStorage(base_url='/media/rapids/', location='/home/ross/rad/media//rapids/'), upload_to=rapids.models.image_directory_path),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.1.3 on 2021-02-11 20:35
|
||||
|
||||
import django.core.files.storage
|
||||
from django.db import migrations, models
|
||||
import rapids.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rapids', '0014_auto_20210211_2034'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='rapidimage',
|
||||
name='image',
|
||||
field=models.FileField(storage=django.core.files.storage.FileSystemStorage(base_url='/media/rapids/', location='/home/ross/rad/media/rapids/'), upload_to=rapids.models.image_directory_path),
|
||||
),
|
||||
]
|
||||
+53
-2
@@ -16,13 +16,15 @@ from django.utils.html import mark_safe
|
||||
from sortedm2m.fields import SortedManyToManyField
|
||||
|
||||
import string
|
||||
from collections import defaultdict
|
||||
from helpers.images import image_as_base64
|
||||
|
||||
|
||||
from generic.models import Site, Condition, Sign, ExamBase
|
||||
|
||||
image_storage = FileSystemStorage(
|
||||
# Physical file location ROOT
|
||||
location=u"{0}/rapids/".format(settings.MEDIA_ROOT),
|
||||
location=u"{0}rapids/".format(settings.MEDIA_ROOT),
|
||||
# Url for file
|
||||
base_url=u"{0}rapids/".format(settings.MEDIA_URL),
|
||||
)
|
||||
@@ -286,7 +288,7 @@ class RapidImage(models.Model):
|
||||
rapid = models.ForeignKey(Rapid,
|
||||
related_name="images",
|
||||
on_delete=models.CASCADE)
|
||||
image = models.ImageField(upload_to=image_directory_path, storage=image_storage)
|
||||
image = models.FileField(upload_to=image_directory_path, storage=image_storage)
|
||||
|
||||
feedback_image = models.BooleanField(default=False)
|
||||
|
||||
@@ -350,6 +352,55 @@ class Exam(ExamBase):
|
||||
help_text="Exam time limit (in seconds). Default is 2100 secondse (35 minutes)", default=2100
|
||||
)
|
||||
|
||||
def get_exam_json(self):
|
||||
questions = self.exam_questions.all()
|
||||
|
||||
exam_questions = defaultdict(dict)
|
||||
|
||||
exam_order = []
|
||||
|
||||
for q in questions:
|
||||
exam_order.append(q.id)
|
||||
|
||||
# Loop through rapidimage associations
|
||||
images = []
|
||||
feedback_images = []
|
||||
for i in q.images.all():
|
||||
if i.feedback_image == True:
|
||||
feedback_images.append(image_as_base64(i.image))
|
||||
# feedback_images.append(i.image.url)
|
||||
else:
|
||||
images.append(image_as_base64(i.image))
|
||||
#images.append(i.image.url)
|
||||
|
||||
|
||||
|
||||
exam_questions[q.id] = {
|
||||
"images": images,
|
||||
#"feedback_image": [],
|
||||
#"annotations": [str(q.image_annotations)],
|
||||
"type": "rapid",
|
||||
}
|
||||
|
||||
#if feedback_images:
|
||||
# exam_questions[q.id]["feedback_image"] = feedback_images
|
||||
|
||||
|
||||
exam_json = {
|
||||
"eid": "rapid/{}".format(self.id),
|
||||
"cached": False,
|
||||
"exam_type": "rapid",
|
||||
"exam_name": self.name,
|
||||
"exam_mode": True,
|
||||
"exam_order": exam_order,
|
||||
"questions": exam_questions,
|
||||
}
|
||||
|
||||
if self.time_limit:
|
||||
exam_json["exam_time"] = self.time_limit
|
||||
|
||||
return exam_json
|
||||
|
||||
|
||||
class CidUserAnswer(models.Model):
|
||||
"""User answers by candidate"""
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ urlpatterns = [
|
||||
path("exam/submit", views.RapidExamViews.postExamAnswers, name="exam_answers_submit"),
|
||||
path("exam/", views.RapidExamViews.exam_list, name="exam_list"),
|
||||
path("exam/json/", views.RapidExamViews.active_exams, name="active_exams"),
|
||||
path("exam/json/<int:pk>", views.exam_json, name="exam_json"),
|
||||
path("exam/json/<int:pk>", views.RapidExamViews.exam_json, name="exam_json"),
|
||||
path("exam/json/<int:pk>/recreate", views.RapidExamViews.exam_json_recreate, name="exam_json_recreate"),
|
||||
path("create/", views.RapidCreate.as_view(), name="rapid_create"),
|
||||
path("create/defaults",
|
||||
|
||||
@@ -699,73 +699,6 @@ def mark(request, pk, sk):
|
||||
)
|
||||
|
||||
|
||||
def exam_json(request, pk):
|
||||
|
||||
exam = get_object_or_404(Exam, pk=pk)
|
||||
|
||||
if not exam.active:
|
||||
raise Http404("No available exam")
|
||||
|
||||
exam_json_cache = cache.get("rapids_exam_json_{}".format(pk))
|
||||
|
||||
if exam_json_cache is not None and not exam.recreate_json:
|
||||
exam_json_cache["cached"] = True
|
||||
return JsonResponse(exam_json_cache)
|
||||
|
||||
questions = exam.exam_questions.all()
|
||||
|
||||
exam_questions = defaultdict(dict)
|
||||
|
||||
exam_order = []
|
||||
|
||||
for q in questions:
|
||||
exam_order.append(q.id)
|
||||
|
||||
# Loop through rapidimage associations
|
||||
images = []
|
||||
feedback_images = []
|
||||
for i in q.images.all():
|
||||
if i.feedback_image == True:
|
||||
feedback_images.append(image_as_base64(i.image))
|
||||
# feedback_images.append(i.image.url)
|
||||
else:
|
||||
images.append(image_as_base64(i.image))
|
||||
#images.append(i.image.url)
|
||||
|
||||
|
||||
|
||||
exam_questions[q.id] = {
|
||||
"images": images,
|
||||
#"feedback_image": [],
|
||||
#"annotations": [str(q.image_annotations)],
|
||||
"type": "rapid",
|
||||
}
|
||||
|
||||
#if feedback_images:
|
||||
# exam_questions[q.id]["feedback_image"] = feedback_images
|
||||
|
||||
|
||||
exam_json = {
|
||||
"eid": "rapid/{}".format(exam.id),
|
||||
"cached": False,
|
||||
"exam_type": "rapid",
|
||||
"exam_name": exam.name,
|
||||
"exam_mode": True,
|
||||
"exam_order": exam_order,
|
||||
"questions": exam_questions,
|
||||
}
|
||||
|
||||
if exam.time_limit:
|
||||
exam_json["exam_time"] = exam.time_limit
|
||||
|
||||
exam.recreate_json = False
|
||||
exam.save()
|
||||
|
||||
cache.set("rapids_exam_json_{}".format(pk), exam_json, 3600)
|
||||
|
||||
return JsonResponse(exam_json)
|
||||
|
||||
|
||||
@login_required
|
||||
def exam_scores_cid(request, pk):
|
||||
exam = get_object_or_404(Exam, pk=pk)
|
||||
|
||||
+2
-1
@@ -14,4 +14,5 @@ psycopg2-binary
|
||||
django-sortedm2m
|
||||
pytesseract
|
||||
opencv-python
|
||||
pydicom
|
||||
pydicom
|
||||
django-dbbackup
|
||||
|
||||
Reference in New Issue
Block a user