This commit is contained in:
Ross
2022-07-05 22:29:44 +01:00
parent 41991f1ebd
commit 74e99d54b1
12 changed files with 210 additions and 70 deletions
+98 -8
View File
@@ -7,9 +7,11 @@ from django.forms import (
ModelChoiceField, ModelChoiceField,
ChoiceField, ChoiceField,
CharField, CharField,
ValidationError,
modelformset_factory, modelformset_factory,
) )
from django.forms import inlineformset_factory from django.forms import inlineformset_factory
from django.shortcuts import get_object_or_404
from atlas.models import ( from atlas.models import (
Case, Case,
@@ -52,6 +54,7 @@ class ConditionForm(ModelForm):
), ),
} }
class CaseCollectionForm(ModelForm): class CaseCollectionForm(ModelForm):
class Meta: class Meta:
model = CaseCollection model = CaseCollection
@@ -73,6 +76,7 @@ class CaseCollectionForm(ModelForm):
super(CaseCollectionForm, self).__init__(*args, **kwargs) super(CaseCollectionForm, self).__init__(*args, **kwargs)
class FindingForm(ModelForm): class FindingForm(ModelForm):
class Meta: class Meta:
model = Finding model = Finding
@@ -204,7 +208,7 @@ class SeriesForm(ModelForm):
instance.save() instance.save()
self.save_m2m() self.save_m2m()
#instance.anonymise_images() # instance.anonymise_images()
return instance return instance
@@ -337,8 +341,6 @@ CaseDifferentialFormSet = inlineformset_factory(
) )
class CaseSeriesForm(ModelForm): class CaseSeriesForm(ModelForm):
def __init__(self, *args, user, **kwargs): def __init__(self, *args, user, **kwargs):
super(CaseSeriesForm, self).__init__(*args, **kwargs) super(CaseSeriesForm, self).__init__(*args, **kwargs)
@@ -354,10 +356,11 @@ class CaseSeriesForm(ModelForm):
# widget=Select(verbose_name="Series", is_stacked=False), # widget=Select(verbose_name="Series", is_stacked=False),
) )
class CaseCollectionCaseForm(ModelForm): class CaseCollectionCaseForm(ModelForm):
def __init__(self, *args, user, **kwargs): def __init__(self, *args, user, **kwargs):
super(CaseCollectionCaseForm, self).__init__(*args, **kwargs) super(CaseCollectionCaseForm, self).__init__(*args, **kwargs)
if not user.groups.filter(name="atlas_editor").exists(): if not user.groups.filter(name="atlas_editor").exists():
queryset = Case.objects.filter(author__id=user.id) queryset = Case.objects.filter(author__id=user.id)
else: else:
@@ -369,6 +372,7 @@ class CaseCollectionCaseForm(ModelForm):
# widget=Select(verbose_name="Series", is_stacked=False), # widget=Select(verbose_name="Series", is_stacked=False),
) )
SeriesFormSet = inlineformset_factory( SeriesFormSet = inlineformset_factory(
Case, Case,
Series.case.through, Series.case.through,
@@ -399,6 +403,7 @@ SeriesImageFormSet = inlineformset_factory(
max_num=2000, max_num=2000,
) )
class CidReportAnswerForm(ModelForm): class CidReportAnswerForm(ModelForm):
class Meta: class Meta:
model = CidReportAnswer model = CidReportAnswer
@@ -413,13 +418,14 @@ class CidReportAnswerForm(ModelForm):
"answer": Textarea(attrs={"cols": 100, "rows": 5}), "answer": Textarea(attrs={"cols": 100, "rows": 5}),
} }
#help_texts = { # help_texts = {
# "answer": "Write your answer in here." # "answer": "Write your answer in here."
#} # }
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(CidReportAnswerForm, self).__init__(*args, **kwargs) super(CidReportAnswerForm, self).__init__(*args, **kwargs)
self.fields['answer'].required = False self.fields["answer"].required = False
class CidReportAnswerMarkForm(ModelForm): class CidReportAnswerMarkForm(ModelForm):
class Meta: class Meta:
@@ -437,4 +443,88 @@ class CidReportAnswerMarkForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(CidReportAnswerMarkForm, self).__init__(*args, **kwargs) super(CidReportAnswerMarkForm, self).__init__(*args, **kwargs)
self.fields['score'].required = False self.fields["score"].required = False
#class AddCollectionToCaseForm(Form):
#
# def __init__(self, *args, user, **kwargs):
# super(AddCollectionToCaseForm, self).__init__(*args, **kwargs)
# print("INIT", args, kwargs)
#
# if not user.groups.filter(name="atlas_editor").exists():
# queryset = CaseCollection.objects.filter(author__id=user.id)
# else:
# queryset = CaseCollection.objects.all()
#
# self.valid_collections = queryset
# self.case = get_object_or_404(Case, pk=pk)
#
# self.fields["casecollection"] = ModelMultipleChoiceField(
# required=False,
# queryset=queryset,
# # widget=Select(verbose_name="Series", is_stacked=False),
# )
#
# def save(self, *args, **kwargs):
# print("SAVE", args, kwargs)
# instance = super(AddCollectionToCaseForm, self).save(*args, **kwargs)
#
# # Here we save the modified project selection back into the database
# instance.casecollection_set.set(self.cleaned_data['casecollection'])
#
# print(f"{instance=}")
#
# return instance
#
# def clean_casecollection(self):
# collection_set = self.cleaned_data["casecollection"]
#
# print(f"{collection_set=}")
#
# for col in collection_set:
# if col not in self.valid_collections:
# raise ValidationError("Invalid collection")
#
# return collection_set
class AddCollectionToCaseForm(Form):
def __init__(self, *args, user, **kwargs):
super(AddCollectionToCaseForm, self).__init__(*args, **kwargs)
print("INIT", args, kwargs)
if not user.groups.filter(name="atlas_editor").exists():
queryset = CaseCollection.objects.filter(author__id=user.id)
else:
queryset = CaseCollection.objects.all()
self.valid_collections = queryset
self.fields["casecollection"] = ModelMultipleChoiceField(
required=False,
queryset=queryset,
# widget=Select(verbose_name="Series", is_stacked=False),
)
def save(self, *args, **kwargs):
print("SAVE", args, kwargs)
instance = super(AddCollectionToCaseForm, self).save(*args, **kwargs)
# Here we save the modified project selection back into the database
instance.casecollection_set.set(self.cleaned_data['casecollection'])
print(f"{instance=}")
return instance
def clean_casecollection(self):
collection_set = self.cleaned_data["casecollection"]
print(f"{collection_set=}")
for col in collection_set:
if col not in self.valid_collections:
raise ValidationError("Invalid collection")
return collection_set
@@ -0,0 +1,18 @@
# Generated by Django 3.2.13 on 2022-07-05 20:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('atlas', '0045_rename_valid_users_casecollection_valid_cid_users'),
]
operations = [
migrations.AlterField(
model_name='casedetail',
name='sort_order',
field=models.IntegerField(default=1000),
),
]
+1 -1
View File
@@ -739,7 +739,7 @@ class CaseDetail(models.Model):
case = models.ForeignKey(Case, on_delete=models.CASCADE) case = models.ForeignKey(Case, on_delete=models.CASCADE)
collection = models.ForeignKey(CaseCollection, on_delete=models.CASCADE) collection = models.ForeignKey(CaseCollection, on_delete=models.CASCADE)
sort_order = models.IntegerField() sort_order = models.IntegerField(default=1000)
class Meta: class Meta:
ordering = ("sort_order",) ordering = ("sort_order",)
@@ -111,6 +111,11 @@
{% endfor %} {% endfor %}
</ul> </ul>
<b>Collections:</b> <b>Collections:</b>
<button
hx-get="{% url 'atlas:case_collection_form' case.pk %}"
hx-target="#collection-form">
Add collection</button>
<div id="collection-form"></div>
<ul> <ul>
{% for p in case.casecollection_set.all %} {% for p in case.casecollection_set.all %}
<li>{{p.name}}</li> <li>{{p.name}}</li>
+2
View File
@@ -119,6 +119,8 @@ urlpatterns = [
# path("unchecked/", views.unchecked_list, name="unchecked_list"), # path("unchecked/", views.unchecked_list, name="unchecked_list"),
# path("verified/<int:pk>/", views.verified_detail, name="verified_detail"), # path("verified/<int:pk>/", views.verified_detail, name="verified_detail"),
path("case/<int:pk>/", views.case_detail, name="case_detail"), path("case/<int:pk>/", views.case_detail, name="case_detail"),
#path("case/<int:pk>/collection-form", views.AddCollectionToCaseView.as_view(), name="case_collection_form"),
path("case/<int:case_id>/collection-form", views.add_collection_to_case_form, name="case_collection_form"),
path("case/<int:pk>/clone", views.AtlasClone.as_view(), name="case_clone"), path("case/<int:pk>/clone", views.AtlasClone.as_view(), name="case_clone"),
# path("verified/", views.verified, name="verified"), # path("verified/", views.verified, name="verified"),
# path("all_questions/", views.all_questions, name="all_questions"), # path("all_questions/", views.all_questions, name="all_questions"),
+35 -1
View File
@@ -18,7 +18,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from generic.mixins import SuperuserRequiredMixin from generic.mixins import SuperuserRequiredMixin
from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
from django.views.generic import ListView from django.views.generic import ListView
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
@@ -30,6 +30,7 @@ from django.http import HttpResponseRedirect, HttpResponse
from generic.models import CidUser from generic.models import CidUser
from .forms import ( from .forms import (
AddCollectionToCaseForm,
CaseCollectionCaseFormSet, CaseCollectionCaseFormSet,
CaseCollectionForm, CaseCollectionForm,
CaseForm, CaseForm,
@@ -714,6 +715,39 @@ class AtlasUpdate(
else: else:
return super().form_invalid(form) return super().form_invalid(form)
def add_collection_to_case_form(request, case_id):
if request.method == 'POST':
form = AddCollectionToCaseForm(request.POST, user=request.user)
if form.is_valid():
case = get_object_or_404(Case, pk=case_id)
case.casecollection_set.add(*form.cleaned_data["casecollection"])
#do_something_with_form_data(form.cleaned_data)
return HttpResponse("Done")
return render(request, 'web/htmx_contact_form_confirm.html')
else:
form = AddCollectionToCaseForm(user=request.user)
return render(request, 'generic_form.html', {
'form': form,
'target_url': reverse("atlas:case_collection_form", args=(case_id,))
})
#class AddCollectionToCaseView(FormView):
# form_class = AddCollectionToCaseForm
# template_name = "generic_form.html"
#
# def form_valid(self, form):
# form.save()
# return super(AddCollectionToCaseView, self).form_valid(form)
#
# def get_form_kwargs(self):
# kwargs = super(AddCollectionToCaseView, self).get_form_kwargs()
# kwargs.update({"user": self.request.user})
# return kwargs
#
# def get_success_url(self) -> str:
# pk = self.kwargs["pk"]
# return reverse("atlas:case_detail", kwargs={"pk": pk})
class AtlasClone(AtlasCreateBase, CreateView): class AtlasClone(AtlasCreateBase, CreateView):
"""Clones a existing atlas""" """Clones a existing atlas"""
+4 -3
View File
@@ -316,9 +316,10 @@ class ExamBase(models.Model):
exam_text.append(f"Stats\n{'-'*len('Stats')}\n{stats}") exam_text.append(f"Stats\n{'-'*len('Stats')}\n{stats}")
exam_text.append( if self.app_name == "rapids":
f"Answers breakdown: {callstate}" exam_text.append(
) f"Answers breakdown: {callstate}"
)
msg = "\n\n".join(exam_text) msg = "\n\n".join(exam_text)
@@ -5,10 +5,12 @@
<button hx-get="{% url exam.app_name|add:':exam_user_report_email' exam_id=exam.pk user_id=user.pk %}" <button hx-get="{% url exam.app_name|add:':exam_user_report_email' exam_id=exam.pk user_id=user.pk %}"
hx-prompt="Enter any additional emails" hx-prompt="Enter any additional emails"
hx-confirm="Are you sure you wish to delete your account?" hx-confirm="Are you sure you wish to delete your account?"
hx-trigger="click, click from:#test" hx-trigger="click, click from:#resend-all"
>Resend</button> >Resend</button>
</li> </li>
{% empty %}
No emails sent.
{% endfor %} {% endfor %}
</ul> </ul>
<button id="test">Test</button> <button id="resend-all">Resend all</button>
+13 -47
View File
@@ -90,8 +90,19 @@
<details> <details>
<summary>Email results</summary> <summary>Email results</summary>
User results emailed: {{exam.exam_results_emailed|default:"Never"}}<br/> User results emailed: {{exam.exam_results_emailed|default:"Never"}}<br/>
<button id="email-results-button" title="Email results to users and their supervisors">Email user results</button> <button id="email-results-button"
<button id="email-unsent-results-button" title="Email results to users and their supervisors">Email unsent user results</button> title="Email results to users and their supervisors"
hx-get="{% url exam.app_name|add:':exam_report_email' exam_id=exam.pk %}"
hx-target="#user-details"
hx-confirm="This will email results, please make sure scores have been refreshed before continuing"
hx-prompt="Please enter an additional email to send to (user and supervisor will automatically be used if available) if required"
>Email user results</button>
<button id="email-unsent-results-button" title="Email results to users and their supervisors"
hx-get="{% url exam.app_name|add:':exam_report_email_unsent' exam_id=exam.pk %}"
hx-target="#user-details"
hx-confirm="This will email results, please make sure scores have been refreshed before continuing"
hx-prompt="Please enter an additional email to send to (user and supervisor will automatically be used if available) if required"
>Email unsent user results</button>
<button id="email-results-check-button" title="Check the status of emailed results" <button id="email-results-check-button" title="Check the status of emailed results"
hx-get="{% url exam.app_name|add:':exam_report_email_status' exam_id=exam.pk %}" hx-get="{% url exam.app_name|add:':exam_report_email_status' exam_id=exam.pk %}"
hx-target="#user-details">Check email status</button> hx-target="#user-details">Check email status</button>
@@ -100,50 +111,5 @@
</details> </details>
</div> </div>
<script>
$(document).ready(() => {
$("#email-results-button").click((evt) => {
if (confirm("This will email results, please make sure scores have been refreshed before continuing")) {
let additional_email = prompt("Please enter an additional email to send to (user and supervisor will automatically be used if available) if required");
let url = "{% url exam.app_name|add:':exam_report_email' exam_id=exam.pk %}";
if ( additional_email != null ) {
url = url + "?additional_email="+encodeURI(additional_email)
}
window.location = url;
}
evt.preventDefault();
})
$("#email-unsent-results-button").click((evt) => {
if (confirm("This will email results, please make sure scores have been refreshed before continuing")) {
let additional_email = prompt("Please enter an additional email to send to (user and supervisor will automatically be used if available) if required");
let url = "{% url exam.app_name|add:':exam_report_email_unsent' exam_id=exam.pk %}";
if ( additional_email != null ) {
url = url + "?additional_email="+encodeURI(additional_email)
}
window.location = url;
}
evt.preventDefault();
})
//$("#email-results-check-button").click((evt) => {
// window.location = "{% url exam.app_name|add:':exam_report_email_status' exam_id=exam.pk %}";
//})
})
</script>
{% endblock %} {% endblock %}
+22 -7
View File
@@ -593,7 +593,8 @@ class ExamViews(View, LoginRequiredMixin):
for u in users: for u in users:
user = User.objects.get(pk=u) user = User.objects.get(pk=u)
user_exam = exam.get_or_create_cid_user_exam(user_user=user) user_exam = exam.get_or_create_cid_user_exam(user_user=user)
status[user] = json.loads(user_exam.results_emailed_status) if user_exam.results_emailed_status:
status[user] = json.loads(user_exam.results_emailed_status)
return render( return render(
request, request,
@@ -620,7 +621,11 @@ class ExamViews(View, LoginRequiredMixin):
if not self.check_user_access(request.user, exam_id): if not self.check_user_access(request.user, exam_id):
raise PermissionDenied raise PermissionDenied
additional_email = request.GET.get("additional_email", "") additional_email = False
if request.htmx.prompt is not None:
additional_email = request.htmx.prompt
print(f"{additional_email=}")
# check addition email is valid # check addition email is valid
if additional_email: if additional_email:
@@ -646,19 +651,29 @@ class ExamViews(View, LoginRequiredMixin):
continue continue
if additional_email: if additional_email:
email_results[user.pk] = exam.email_user_results( email_results[user] = exam.email_user_results(
user, additional_emails=[additional_email] user, additional_emails=[additional_email]
) )
else: else:
email_results[user.pk] = exam.email_user_results(user) email_results[user] = exam.email_user_results(user)
email_results[user.pk].append(f"{time}") email_results[user].append(f"{time}")
user_exam.results_emailed_status = json.dumps(email_results[user.pk]) user_exam.results_emailed_status = json.dumps(email_results[user])
user_exam.save() user_exam.save()
exam.exam_results_emailed = time exam.exam_results_emailed = time
exam.save() exam.save()
return render(
request,
"generic/exam_report_email_status.html",
{
"exam": exam,
"status": email_results,
"app_name": self.app_name,
},
)
return JsonResponse(email_results) return JsonResponse(email_results)
@method_decorator(login_required) @method_decorator(login_required)
@@ -1343,7 +1358,7 @@ class ExamViews(View, LoginRequiredMixin):
if not existing_answers: if not existing_answers:
if uid: if uid:
ans = self.CidUserAnswer( ans = self.CidUserAnswer(
answer=posted_answer, uid=User.objects.get(id=uid) answer=posted_answer, user=User.objects.get(id=uid)
) )
else: else:
ans = self.CidUserAnswer(answer=posted_answer, cid=cid) ans = self.CidUserAnswer(answer=posted_answer, cid=cid)
+2 -1
View File
@@ -25,7 +25,8 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/core.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/core.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/md5.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/md5.js"></script>
<script src="https://unpkg.com/alpinejs" defer></script> {% comment %} <script src="https://unpkg.com/alpinejs" defer></script> {% endcomment %}
<script src="https://unpkg.com/hyperscript.org@0.9.5"></script>
<script src="https://unpkg.com/htmx.org@1.7.0/dist/htmx.min.js" defer></script> <script src="https://unpkg.com/htmx.org@1.7.0/dist/htmx.min.js" defer></script>
{% django_htmx_script %} {% django_htmx_script %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
+6
View File
@@ -0,0 +1,6 @@
<form class="form" hx-post="{{ target_url }}">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
<button _="on click remove <.form/>">Cancel</button>
</form>