numerous updates

This commit is contained in:
Ross
2022-05-20 20:26:02 +01:00
parent 0373b22e78
commit 77ccdbc92a
246 changed files with 22615 additions and 2409 deletions
+13 -2
View File
@@ -9,8 +9,9 @@ from django.forms import (
)
from django.forms import inlineformset_factory
from atlas.models import CaseCollection
from django.contrib.auth.models import User
from generic.models import CidUser, CidUserGroup, Examination, QuestionNote
from generic.models import CidUser, CidUserGroup, Examination, QuestionNote, UserUserGroup
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.forms.widgets import RadioSelect, TextInput, Textarea
@@ -205,4 +206,14 @@ class CidUserForm(ModelForm):
class CidUserGroupForm(ModelForm):
class Meta:
model = CidUserGroup
fields = ["name", "archive"]
fields = ["name", "archive"]
class UserUserGroupForm(ModelForm):
users = ModelMultipleChoiceField(
required=False,
queryset=User.objects.all(),
widget=FilteredSelectMultiple(verbose_name="Users", is_stacked=False),
)
class Meta:
model = UserUserGroup
fields = ["name", "archive", "users"]
@@ -0,0 +1,24 @@
# Generated by Django 3.2.13 on 2022-05-20 16:05
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('generic', '0026_ciduser_generic_cid_cid_291418_idx'),
]
operations = [
migrations.AddField(
model_name='ciduserexam',
name='user_user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_userexam', to='generic.ciduser'),
),
migrations.AlterField(
model_name='ciduserexam',
name='cid_user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cid_userexam', to='generic.ciduser'),
),
]
@@ -0,0 +1,21 @@
# Generated by Django 3.2.13 on 2022-05-20 16:12
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('generic', '0027_auto_20220520_1705'),
]
operations = [
migrations.AlterField(
model_name='ciduserexam',
name='user_user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_userexam', to=settings.AUTH_USER_MODEL),
),
]
+24
View File
@@ -0,0 +1,24 @@
# Generated by Django 3.2.13 on 2022-05-20 17:44
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('generic', '0028_alter_ciduserexam_user_user'),
]
operations = [
migrations.CreateModel(
name='UserUserGroup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=50)),
('archive', models.BooleanField(default=False)),
('users', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)),
],
),
]
@@ -0,0 +1,20 @@
# Generated by Django 3.2.13 on 2022-05-20 18:27
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('generic', '0029_userusergroup'),
]
operations = [
migrations.AlterField(
model_name='userusergroup',
name='users',
field=models.ManyToManyField(blank=True, related_name='user_groups', to=settings.AUTH_USER_MODEL),
),
]
+25
View File
@@ -0,0 +1,25 @@
# Generated by Django 3.2.13 on 2022-05-20 19:15
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('generic', '0030_alter_userusergroup_users'),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('supervisor_email', models.EmailField(blank=True, max_length=254)),
('supervisor_name', models.CharField(blank=True, max_length=100)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
+104 -30
View File
@@ -20,6 +20,10 @@ from django.contrib.contenttypes.models import ContentType
from django.core.mail import send_mail
from django.core.mail import EmailMessage
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Plane(models.Model):
plane = models.CharField(max_length=200, unique=True)
@@ -138,13 +142,9 @@ class ExamBase(models.Model):
user_scores = models.JSONField(default=dict)
# Is this actually used?
cid_users = GenericRelation("generic.CidUserExam")
# time_limit = models.IntegerField(
# help_text="Exam time limit (in seconds). Default is 2100 secondse (35 minutes)",
# default=2100,
# )
class Meta:
abstract = True
@@ -189,49 +189,85 @@ class ExamBase(models.Model):
authors = [i for i in self.author.all()]
return authors
def check_cid_user(self, cid, passcode, request=None):
def check_cid_user(self, cid, passcode, request=None, user_id=None):
if request is not None and request.user.is_superuser:
return True
if self.valid_users.exists():
if not self.valid_cid_users.exists() and not self.valid_user_users.exists():
return True
user = self.valid_users.filter(cid=cid).first()
if user_id is not None:
if self.valid_user_users.filter(pk=user_id).exists():
return True
if not user or user.passcode != passcode:
return False
if self.valid_cid_users.exists():
return True
cid_user = self.valid_cid_users.filter(cid=cid).first()
def get_or_create_cid_user_exam(self, cid_user, start_time=None):
if cid_user and cid_user.passcode == passcode:
return True
return False
return False
def get_or_create_cid_user_exam(self, cid_user=None, user_user=None, start_time=None):
content_type = ContentType.objects.get_for_model(self)
c = CidUserExam.objects.filter(
content_type=content_type, object_id=self.pk, cid_user=cid_user
).first()
if c:
return c
if cid_user is not None:
c = CidUserExam.objects.filter(
content_type=content_type, object_id=self.pk, cid_user=cid_user
).first()
if c:
return c
if user_user is not None:
c = CidUserExam.objects.filter(
content_type=content_type, object_id=self.pk, user_user=cid_user
).first()
if c:
return c
if start_time is None:
start_time = timezone.now()
new = CidUserExam(
content_type=content_type,
object_id=self.pk,
cid_user=cid_user,
start_time=start_time,
)
if cid_user is not None:
new = CidUserExam(
content_type=content_type,
object_id=self.pk,
cid_user=cid_user,
start_time=start_time,
)
if user_user is not None:
new = CidUserExam(
content_type=content_type,
object_id=self.pk,
user_user=user_user,
start_time=start_time,
)
new.save()
return new
def get_cid_user_exams(self, cid_user=None):
def get_cid_user_exams(self, cid_user=None, user_user=None):
content_type = ContentType.objects.get_for_model(self)
if cid_user is None:
if cid_user is None and user_user is None:
return CidUserExam.objects.filter(
content_type=content_type,
object_id=self.pk,
)
else:
return CidUserExam.objects.filter(
content_type=content_type, object_id=self.pk, cid_user=cid_user
)
if cid_user is not None:
return CidUserExam.objects.filter(
content_type=content_type, object_id=self.pk, cid_user=cid_user
)
else:
return CidUserExam.objects.filter(
content_type=content_type, object_id=self.pk, user_user=user_user
)
def get_cid_user_score(self, cid_user):
c = str(cid_user)
@@ -482,7 +518,8 @@ class CidUserExam(models.Model):
start_time = models.DateTimeField(blank=True, null=True)
end_time = models.DateTimeField(blank=True, null=True)
cid_user = models.ForeignKey(CidUser, on_delete=models.CASCADE)
cid_user = models.ForeignKey(CidUser, on_delete=models.CASCADE, blank=True, null=True, related_name="cid_userexam")
user_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True, related_name="user_userexam")
manual_submission = models.BooleanField(default=False)
@@ -501,5 +538,42 @@ class CidUserGroup(models.Model):
return s
def get_absolute_url(self):
return reverse("generic:group_update", kwargs={"pk": self.pk})
return reverse("generic:cid_group_update", kwargs={"pk": self.pk})
class UserUserGroup(models.Model):
name = models.CharField(blank=True, max_length=50)
archive = models.BooleanField(default=False)
users = models.ManyToManyField(
settings.AUTH_USER_MODEL,
blank=True,
related_name="user_groups"
)
def __str__(self) -> str:
return str(self.name)
def GetGroupUsers(self):
users = self.users.all()
s = ", ".join([f"{user.id} - {user.username} ({user.email})" for user in users])
return s
def get_absolute_url(self):
return reverse("generic:user_group_update", kwargs={"pk": self.pk})
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
supervisor_email = models.EmailField(blank=True)
supervisor_name = models.CharField(max_length=100, blank=True)
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
+2 -1
View File
@@ -19,7 +19,8 @@
{% if request.user.is_authenticated %}
<a href="{% url 'generic:manage_cids' %}">Cids</a> /
<a href="{% url 'generic:manage_cid_exams' %}">Cids (exams)</a> /
<a href="{% url 'generic:group_view' %}">Groups</a> /
<a href="{% url 'generic:cid_group_view' %}">Cid Groups</a> /
<a href="{% url 'generic:user_group_view' %}">User Groups</a> /
{% endif %}
{% endblock %}
@@ -1,9 +1,9 @@
{% extends 'generic/base.html' %}
{% block content %}
<h2>Groups</h2>
<h2>Cid Groups</h2>
<p>
<a href="{% url 'generic:group_create' %}">Create new group</a>
<a href="{% url 'generic:cid_group_create' %}">Create new group</a>
</p>
{% for group in groups %}
<p>{{group.name}}
@@ -15,7 +15,7 @@
">Email candidate results</button>
<button class="url-button" data-url="{% url 'generic:group_email_results_resend' pk=group.pk %}
">Resend candidate results</button>
<button class="url-button" data-url="{% url 'generic:group_update' pk=group.pk %}
<button class="url-button" data-url="{% url 'generic:cid_group_update' pk=group.pk %}
">Edit</button>
</p>
{% endfor %}
@@ -29,8 +29,8 @@
</script>
{% if view_all %}
<a href="{% url 'generic:group_view' %}">View active</a>
<a href="{% url 'generic:cid_group_view' %}">View active</a>
{% else %}
<a href="{% url 'generic:group_view_all' %}">View all</a>
<a href="{% url 'generic:cid_group_view_all' %}">View all</a>
{% endif %}
{% endblock %}
+28 -2
View File
@@ -1,6 +1,8 @@
<div class="card text-white bg-dark">
<h3>CID candidates</h3>
{% if cid_users %}
<p>{{cid_user_count}} candidates.</p>
<p>{{cid_user_count}} CID candidates.</p>
<ol>
{% for cid in cid_users %}
@@ -16,8 +18,32 @@
{% endfor %}
</ol>
{% else %}
<p>This exam has no candidates. Add some with the below link.</p>
<p>This exam has no CID candidates. Add some with the below link.</p>
{% endif %}
<a href="{% url exam.app_name|add:':exam_cids_edit' exam.pk %}">Edit Exam CIDs</a>
</div>
<br/>
<div class="card text-white bg-dark">
<h3>User candidates</h3>
{% if user_users %}
<p>{{user_user_count}} User candidates.</p>
<ol>
{% for user in user_users %}
<li>{{user.username}}
Email: {{user.email}}
</li>
{% endfor %}
</ol>
{% else %}
<p>This exam has no User candidates. Add some with the below link.</p>
{% endif %}
<a href="{% url exam.app_name|add:':exam_users_edit' exam.pk %}">Edit Exam Users</a>
</div>
@@ -0,0 +1,72 @@
<h3>Users</h3>
{% if groups %}
Users from the following groups are available: {% for group in groups %}<a href='{{group.get_absolute_url}}'>{{group}}</a>{% endfor %} .
<ol>
{% for user in current_user_users %}
<li class="current">{{user.username}} /
Email: {{user.email}} <button class="toggle-btn" data-pk="{{user.pk}}" data-user="{{user.username}}">Toggle</button>
</li>
{% endfor %}
{% for user in available_user_users %}
<li>{{user.username}} /
Email: {{user.email}} <button class="toggle-btn" data-pk="{{user.pk}}" data-user="{{user.username}}">Toggle</button>
</li>
{% endfor %}
</ol>
{% else %}
This exam is not associated with any user groups. Edit and add a group to enable user management.
{% endif %}
<script>
$(document).ready(() => {
$(".toggle-btn").click((el) => {
parent = $(el.target).parent()
$.ajax({
url: "{% url exam.app_name|add:':exam_json_edit' exam.pk %}",
data: {
csrfmiddlewaretoken: "{{csrf_token}}",
edit_user_user: el.target.dataset.pk,
add: !parent.hasClass("current"),
},
type: "POST",
dataType: "json",
})
// $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly
.done(function (data) {
console.log(data);
if (data.status == "success") {
if (data.added) {
parent.addClass("current");
toastr.info(`User ${el.target.dataset.user} added to exam`)
} else {
parent.removeClass("current");
toastr.info(`User ${el.target.dataset.user} removed from exam`)
}
}
})
.always(function () {
})
});
})
</script>
<style>
.current {
color: lightgray;
}
.current::after {
content: "[ADDED TO EXAM]"
}
button {
padding: 0;
}
</style>
+36
View File
@@ -0,0 +1,36 @@
{% extends 'generic/base.html' %}
{% block content %}
<h2>User Groups</h2>
<p>
<a href="{% url 'generic:user_group_create' %}">Create new group</a>
</p>
{% for group in groups %}
<p>{{group.name}}
{% comment %} <button class="url-button" data-url="{% url 'generic:group_email' pk=group.pk %}
">Email candidate details</button>
<button class="url-button" data-url="{% url 'generic:group_email_resend' pk=group.pk %}
">Resend candidate details</button>
<button class="url-button" data-url="{% url 'generic:group_email_results' pk=group.pk %}
">Email candidate results</button>
<button class="url-button" data-url="{% url 'generic:group_email_results_resend' pk=group.pk %}
">Resend candidate results</button> {% endcomment %}
<button class="url-button" data-url="{% url 'generic:user_group_update' pk=group.pk %}
">Edit</button>
</p>
{% endfor %}
<script type="text/javascript">
$(".url-button").click((evt) => {
window.location = evt.currentTarget.dataset.url;
})
</script>
{% if view_all %}
<a href="{% url 'generic:user_group_view' %}">View active</a>
{% else %}
<a href="{% url 'generic:user_group_view_all' %}">View all</a>
{% endif %}
{% endblock %}
+31
View File
@@ -0,0 +1,31 @@
{% extends "generic/base.html" %}
<!-- {% load static from static %} -->
{% block css %}
{% endblock %}
{% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
{{form.media}}
<script type="text/javascript">
</script>
<!-- {{ form.media }} -->
{% endblock %}
{% block content %}
<h2>Add / Edit Group / {{userusergroup.name}}</h2>
Use this form to create / edit a CID user group.
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" class="submit-button" value="Submit" name="submit">
<h3>Users</h3>
{{userusergroup.GetGroupUsers}}
</form>
{% endblock %}
+12 -4
View File
@@ -41,8 +41,8 @@ urlpatterns = [
),
path("cids/manage/", views.CidUserView.as_view(), name="manage_cids"),
path("cids/manage/exams", views.CidUserExamView.as_view(), name="manage_cid_exams"),
path("cids/group/", views.group_view, name="group_view"),
path("cids/group/all", views.group_view_all, name="group_view_all"),
path("cids/group/", views.cid_group_view, name="cid_group_view"),
path("cids/group/all", views.cid_group_view_all, name="cid_group_view_all"),
path("cids/group/<int:pk>/email", views.group_email, name="group_email"),
path(
"cids/group/<int:pk>/email_results",
@@ -60,10 +60,18 @@ urlpatterns = [
name="group_email_resend",
),
path(
"cids/group/<int:pk>/update", views.CidUserGroupUpdate.as_view(), name="group_update"
"cids/group/<int:pk>/update", views.CidUserGroupUpdate.as_view(), name="cid_group_update"
),
path(
"cids/group/create", views.CidUserGroupCreate.as_view(), name="group_create"
"cids/group/create", views.CidUserGroupCreate.as_view(), name="cid_group_create"
),
path("user/group/", views.user_group_view, name="user_group_view"),
path("user/group/all", views.user_group_view_all, name="user_group_view_all"),
path(
"user/group/<int:pk>/update", views.UserUserGroupUpdate.as_view(), name="user_group_update"
),
path(
"user/group/create", views.UserUserGroupCreate.as_view(), name="user_group_create"
),
path("cids/create", views.manage_cid_users, name="manage_cid_users"),
]
+126 -53
View File
@@ -43,9 +43,10 @@ from .forms import (
CidUserForm,
ExaminationForm,
CidUserGroupForm,
UserUserGroupForm,
)
from .models import CidUser, CidUserGroup, Examination, QuestionNote
from .models import CidUser, CidUserGroup, Examination, QuestionNote, UserUserGroup
from rapids.models import Rapid as RapidQuestion
from rapids.models import Exam as RapidExam
@@ -623,14 +624,18 @@ class ExamViews(View, LoginRequiredMixin):
if not self.check_user_access(request.user, exam_id):
raise PermissionDenied
cid_users = exam.valid_users.all()
cid_users = exam.valid_cid_users.all()
cid_user_count = cid_users.count()
user_users = exam.valid_user_users.all()
user_user_count = user_users.count()
context = {
"exam": exam,
"cid_users": cid_users,
"cid_user_count": cid_user_count,
"user_users": user_users,
"user_user_count": user_user_count,
"app_name": self.app_name,
}
@@ -644,6 +649,41 @@ class ExamViews(View, LoginRequiredMixin):
)
def exam_users_edit(self, request, exam_id):
exam = get_object_or_404(self.Exam, pk=exam_id)
if not request.user.groups.filter(name="cid_user_manager").exists():
#raise PermissionDenied
if request.user not in exam.author.all():
if not self.check_user_access(request.user, exam_id):
raise PermissionDenied
current_user_users = exam.valid_user_users.all()
exam_groups = exam.user_user_groups.all()
available_user_users = list(User.objects.filter(user_groups__in=exam_groups).difference(current_user_users))
print(available_user_users)
#available_user_users = User.objects.all()
context = {
"exam": exam,
"groups": exam_groups,
"current_user_users": current_user_users,
"available_user_users": available_user_users,
"app_name": self.app_name,
}
if self.app_name == "atlas":
context["collection"] = exam
return render(
request,
"exam_users_edit.html",
context
)
def exam_cids_edit(self, request, exam_id):
exam = get_object_or_404(self.Exam, pk=exam_id)
@@ -653,7 +693,7 @@ class ExamViews(View, LoginRequiredMixin):
if not self.check_user_access(request.user, exam_id):
raise PermissionDenied
current_cid_users = exam.valid_users.all()
current_cid_users = exam.valid_cid_users.all()
exam_groups = exam.cid_user_groups.all()
@@ -725,6 +765,26 @@ class ExamViews(View, LoginRequiredMixin):
data = {"status": "success", "added": add}
return JsonResponse(data, status=200)
if "edit_user_user" in request.POST:
user_id = request.POST.get("edit_user_user")
add = request.POST.get("add") == "true"
user_user = User.objects.get(pk=user_id)
app_exam_map = {}
app_exam_map["rapids"] = user_user.user_rapid_exams
#app_exam_map["atlas"] = user_user.casecollection_exams
if add:
app_exam_map[self.app_name].add(pk)
else:
app_exam_map[self.app_name].remove(pk)
data = {"status": "success", "added": add}
return JsonResponse(data, status=200)
if "add_exam_questions" in request.POST:
question_ids = json.loads(request.POST.get("add_exam_questions"))
question_objects = self.Question.objects.filter(pk__in=question_ids)
@@ -932,7 +992,7 @@ class ExamViews(View, LoginRequiredMixin):
for exam in exams:
if exam.active or self.check_user_access(request.user, exam.pk):
print(exam.name, cid, passcode)
if exam.exam_mode and not exam.check_cid_user(cid, passcode):
if exam.exam_mode and not exam.check_cid_user(cid, passcode, user_id=request.user.pk):
print(exam.name, "fail")
continue
@@ -979,7 +1039,7 @@ class ExamViews(View, LoginRequiredMixin):
n = 0
for answer in json.loads(request.POST.get("answers")):
ret_status, ret = self.loadJsonAnswer(answer)
ret_status, ret = self.loadJsonAnswer(answer, user=request.user)
# try:
# ret_status, ret = self.loadJsonAnswer(answer)
# except:
@@ -996,15 +1056,18 @@ class ExamViews(View, LoginRequiredMixin):
# print(UserAnswer.objects.filter(exam__id=q["eid"]))
# print(request.urlencode())
exam_type, exam_id = request.POST.get("eid").split("/")
c = CidUser.objects.filter(cid=request.POST.get("cid")).first()
exam = self.Exam.objects.get(pk=exam_id)
t = timezone.make_aware(
datetime.fromtimestamp(float(request.POST.get("start_time")))
)
cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c, start_time=t)
if request.POST.get("cid").startswith("u-"):
cid_user_exam = exam.get_or_create_cid_user_exam(user_user=request.user, start_time=t)
else:
c = CidUser.objects.filter(cid=request.POST.get("cid")).first()
cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c, start_time=t)
cid_user_exam.end_time = timezone.now()
cid_user_exam.save()
@@ -1021,7 +1084,11 @@ class ExamViews(View, LoginRequiredMixin):
if not exam.active and not self.check_user_access(request.user, pk):
raise Http404("No available exam")
time = datetime.now()
exam_json = exam.get_exam_json(based=False)
exam_json["generated"] = time.isoformat()
exam_json["exam_json_id"] = exam.exam_json_id
# exam_json["exam_active"] = False
return JsonResponse(exam_json)
@@ -1115,10 +1182,10 @@ class ExamViews(View, LoginRequiredMixin):
if not exam.exam_mode:
raise Http404("Packet not in exam mode")
user_answers_and_marks = defaultdict(list)
#user_answers_and_marks = defaultdict(list)
user_answers_marks = defaultdict(list)
user_answers = defaultdict(list)
user_names = {}
#user_names = {}
# cid_passcodes = {}
by_question = defaultdict(dict)
@@ -1136,11 +1203,16 @@ class ExamViews(View, LoginRequiredMixin):
# Loop through all candidates
for cid_user_answer in cid_user_answers:
# Convoluted (probably...)
cid = cid_user_answer.cid
# cid_passcodes[cid] = cid_user_answer.passcode
cids.add(cid)
if cid_user_answer.user is None:
cid = cid_user_answer.cid
# cid_passcodes[cid] = cid_user_answer.passcode
cids.add(cid)
else:
cid = f"u/{cid_user_answer.user.username}"
cids.add(cid)
s = cid_user_answer
user_names[cid] = cid
#user_names[cid] = cid
q = cid_user_answer.question
@@ -1159,7 +1231,7 @@ class ExamViews(View, LoginRequiredMixin):
unmarked.add(index)
user_answers[cid].append(ans)
user_answers_marks[cid].append(answer_score)
user_answers_and_marks[cid].append((ans, answer_score))
#user_answers_and_marks[cid].append((ans, answer_score))
if self.app_name in ("rapids", "anatomy", "sbas"):
by_question[q][cid] = (ans, answer_score)
@@ -1247,13 +1319,13 @@ class ExamViews(View, LoginRequiredMixin):
"unmarked": unmarked,
"questions": questions,
"by_question": by_question,
"user_answers": dict(user_answers),
"user_answers_marks": dict(user_answers_marks),
#"user_answers": dict(user_answers),
#"user_answers_marks": dict(user_answers_marks),
"user_scores": user_scores,
"user_scores_normalised": user_scores_normalised,
"user_scores_list": user_scores_list,
"user_names": user_names,
"user_answers_and_marks": user_answers_and_marks,
#"user_scores_list": user_scores_list,
#"user_names": user_names,
#"user_answers_and_marks": user_answers_and_marks,
"max_score": max_score,
"mean": mean,
"median": median,
@@ -1480,36 +1552,43 @@ class CidUserView(CidManagerRequiredMixin, SingleTableMixin, FilterView):
return context
@login_required
@user_is_cid_user_manager
def group_view(request):
def cid_group_view(request):
groups = CidUserGroup.objects.filter(archive=False)
return render(
request,
"generic/group_view.html",
"generic/cid_group_view.html",
{"groups": groups},
)
@user_is_cid_user_manager
def group_view(request):
groups = CidUserGroup.objects.filter(archive=False)
return render(
request,
"generic/group_view.html",
{"groups": groups, "view_all": False},
)
@user_is_cid_user_manager
def group_view_all(request):
def cid_group_view_all(request):
groups = CidUserGroup.objects.filter()
return render(
request,
"generic/group_view.html",
"generic/cid_group_view.html",
{"groups": groups, "view_all": True},
)
@user_is_cid_user_manager
def user_group_view(request):
groups = UserUserGroup.objects.filter(archive=False)
return render(
request,
"generic/user_group_view.html",
{"groups": groups},
)
@user_is_cid_user_manager
def user_group_view_all(request):
groups = UserUserGroup.objects.filter()
return render(
request,
"generic/user_group_view.html",
{"groups": groups, "view_all": True},
)
@@ -1543,21 +1622,6 @@ def group_email(request, pk, resend=False):
{"users": users, "users_count": users.count(), "results": False},
)
# sent = []
# not_sent = []
# for u in users:
# success, msg = u.email_details(resend=resend)
# if success:
# sent.append(u)
# else:
# not_sent.append((u, msg))
# return render(
# request,
# "generic/group_emailed.html",
# {"sent": sent, "not_sent": not_sent, "users_count": users.count()},
# )
@user_is_cid_user_manager
def group_email_results(request, pk, resend=False):
@@ -1788,3 +1852,12 @@ class CidUserGroupCreate(RevisionMixin, CidManagerRequiredMixin, CreateView):
class CidUserGroupUpdate(RevisionMixin, CidManagerRequiredMixin, UpdateView):
model = CidUserGroup
form_class = CidUserGroupForm
class UserUserGroupCreate(RevisionMixin, CidManagerRequiredMixin, CreateView):
model = UserUserGroup
form_class = UserUserGroupForm
class UserUserGroupUpdate(RevisionMixin, CidManagerRequiredMixin, UpdateView):
model = UserUserGroup
form_class = UserUserGroupForm