.
This commit is contained in:
+98
-8
@@ -7,9 +7,11 @@ from django.forms import (
|
||||
ModelChoiceField,
|
||||
ChoiceField,
|
||||
CharField,
|
||||
ValidationError,
|
||||
modelformset_factory,
|
||||
)
|
||||
from django.forms import inlineformset_factory
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from atlas.models import (
|
||||
Case,
|
||||
@@ -52,6 +54,7 @@ class ConditionForm(ModelForm):
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class CaseCollectionForm(ModelForm):
|
||||
class Meta:
|
||||
model = CaseCollection
|
||||
@@ -73,6 +76,7 @@ class CaseCollectionForm(ModelForm):
|
||||
|
||||
super(CaseCollectionForm, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class FindingForm(ModelForm):
|
||||
class Meta:
|
||||
model = Finding
|
||||
@@ -204,7 +208,7 @@ class SeriesForm(ModelForm):
|
||||
instance.save()
|
||||
self.save_m2m()
|
||||
|
||||
#instance.anonymise_images()
|
||||
# instance.anonymise_images()
|
||||
|
||||
return instance
|
||||
|
||||
@@ -337,8 +341,6 @@ CaseDifferentialFormSet = inlineformset_factory(
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class CaseSeriesForm(ModelForm):
|
||||
def __init__(self, *args, user, **kwargs):
|
||||
super(CaseSeriesForm, self).__init__(*args, **kwargs)
|
||||
@@ -354,10 +356,11 @@ class CaseSeriesForm(ModelForm):
|
||||
# widget=Select(verbose_name="Series", is_stacked=False),
|
||||
)
|
||||
|
||||
|
||||
class CaseCollectionCaseForm(ModelForm):
|
||||
def __init__(self, *args, user, **kwargs):
|
||||
super(CaseCollectionCaseForm, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
if not user.groups.filter(name="atlas_editor").exists():
|
||||
queryset = Case.objects.filter(author__id=user.id)
|
||||
else:
|
||||
@@ -369,6 +372,7 @@ class CaseCollectionCaseForm(ModelForm):
|
||||
# widget=Select(verbose_name="Series", is_stacked=False),
|
||||
)
|
||||
|
||||
|
||||
SeriesFormSet = inlineformset_factory(
|
||||
Case,
|
||||
Series.case.through,
|
||||
@@ -399,6 +403,7 @@ SeriesImageFormSet = inlineformset_factory(
|
||||
max_num=2000,
|
||||
)
|
||||
|
||||
|
||||
class CidReportAnswerForm(ModelForm):
|
||||
class Meta:
|
||||
model = CidReportAnswer
|
||||
@@ -413,13 +418,14 @@ class CidReportAnswerForm(ModelForm):
|
||||
"answer": Textarea(attrs={"cols": 100, "rows": 5}),
|
||||
}
|
||||
|
||||
#help_texts = {
|
||||
# help_texts = {
|
||||
# "answer": "Write your answer in here."
|
||||
#}
|
||||
# }
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CidReportAnswerForm, self).__init__(*args, **kwargs)
|
||||
self.fields['answer'].required = False
|
||||
self.fields["answer"].required = False
|
||||
|
||||
|
||||
class CidReportAnswerMarkForm(ModelForm):
|
||||
class Meta:
|
||||
@@ -437,4 +443,88 @@ class CidReportAnswerMarkForm(ModelForm):
|
||||
|
||||
def __init__(self, *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
@@ -739,7 +739,7 @@ class CaseDetail(models.Model):
|
||||
case = models.ForeignKey(Case, on_delete=models.CASCADE)
|
||||
collection = models.ForeignKey(CaseCollection, on_delete=models.CASCADE)
|
||||
|
||||
sort_order = models.IntegerField()
|
||||
sort_order = models.IntegerField(default=1000)
|
||||
|
||||
class Meta:
|
||||
ordering = ("sort_order",)
|
||||
|
||||
@@ -111,6 +111,11 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<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>
|
||||
{% for p in case.casecollection_set.all %}
|
||||
<li>{{p.name}}</li>
|
||||
|
||||
@@ -119,6 +119,8 @@ urlpatterns = [
|
||||
# path("unchecked/", views.unchecked_list, name="unchecked_list"),
|
||||
# path("verified/<int:pk>/", views.verified_detail, name="verified_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("verified/", views.verified, name="verified"),
|
||||
# path("all_questions/", views.all_questions, name="all_questions"),
|
||||
|
||||
+35
-1
@@ -18,7 +18,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix
|
||||
from django.views.generic.detail import DetailView
|
||||
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.decorators.csrf import csrf_exempt
|
||||
|
||||
@@ -30,6 +30,7 @@ from django.http import HttpResponseRedirect, HttpResponse
|
||||
from generic.models import CidUser
|
||||
|
||||
from .forms import (
|
||||
AddCollectionToCaseForm,
|
||||
CaseCollectionCaseFormSet,
|
||||
CaseCollectionForm,
|
||||
CaseForm,
|
||||
@@ -714,6 +715,39 @@ class AtlasUpdate(
|
||||
else:
|
||||
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):
|
||||
"""Clones a existing atlas"""
|
||||
|
||||
+4
-3
@@ -316,9 +316,10 @@ class ExamBase(models.Model):
|
||||
exam_text.append(f"Stats\n{'-'*len('Stats')}\n{stats}")
|
||||
|
||||
|
||||
exam_text.append(
|
||||
f"Answers breakdown: {callstate}"
|
||||
)
|
||||
if self.app_name == "rapids":
|
||||
exam_text.append(
|
||||
f"Answers breakdown: {callstate}"
|
||||
)
|
||||
|
||||
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 %}"
|
||||
hx-prompt="Enter any additional emails"
|
||||
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>
|
||||
|
||||
</li>
|
||||
{% empty %}
|
||||
No emails sent.
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<button id="test">Test</button>
|
||||
<button id="resend-all">Resend all</button>
|
||||
@@ -90,8 +90,19 @@
|
||||
<details>
|
||||
<summary>Email results</summary>
|
||||
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-unsent-results-button" title="Email results to users and their supervisors">Email unsent user results</button>
|
||||
<button id="email-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"
|
||||
hx-get="{% url exam.app_name|add:':exam_report_email_status' exam_id=exam.pk %}"
|
||||
hx-target="#user-details">Check email status</button>
|
||||
@@ -100,50 +111,5 @@
|
||||
</details>
|
||||
|
||||
</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 %}
|
||||
|
||||
|
||||
+22
-7
@@ -593,7 +593,8 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
for u in users:
|
||||
user = User.objects.get(pk=u)
|
||||
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(
|
||||
request,
|
||||
@@ -620,7 +621,11 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
if not self.check_user_access(request.user, exam_id):
|
||||
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
|
||||
if additional_email:
|
||||
@@ -646,19 +651,29 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
continue
|
||||
|
||||
if additional_email:
|
||||
email_results[user.pk] = exam.email_user_results(
|
||||
email_results[user] = exam.email_user_results(
|
||||
user, additional_emails=[additional_email]
|
||||
)
|
||||
else:
|
||||
email_results[user.pk] = exam.email_user_results(user)
|
||||
email_results[user.pk].append(f"{time}")
|
||||
user_exam.results_emailed_status = json.dumps(email_results[user.pk])
|
||||
email_results[user] = exam.email_user_results(user)
|
||||
email_results[user].append(f"{time}")
|
||||
user_exam.results_emailed_status = json.dumps(email_results[user])
|
||||
|
||||
user_exam.save()
|
||||
|
||||
exam.exam_results_emailed = time
|
||||
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)
|
||||
|
||||
@method_decorator(login_required)
|
||||
@@ -1343,7 +1358,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
if not existing_answers:
|
||||
if uid:
|
||||
ans = self.CidUserAnswer(
|
||||
answer=posted_answer, uid=User.objects.get(id=uid)
|
||||
answer=posted_answer, user=User.objects.get(id=uid)
|
||||
)
|
||||
else:
|
||||
ans = self.CidUserAnswer(answer=posted_answer, cid=cid)
|
||||
|
||||
+2
-1
@@ -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/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>
|
||||
{% django_htmx_script %}
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user