clean up
This commit is contained in:
@@ -838,3 +838,12 @@ summary h5 {
|
|||||||
background-color: black;
|
background-color: black;
|
||||||
opacity: 80%;
|
opacity: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.small-url-button {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button a, button a:link, button a:visited, button a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{% extends 'anatomy/base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% for exam in exams %}
|
|
||||||
<div class="anatomy">
|
|
||||||
<h1><a href="{% url 'anatomy:exam_overview' pk=exam.pk %}">Exam: {{ exam.name }} </a></h1>
|
|
||||||
|
|
||||||
{% if exam.exam_mode %}
|
|
||||||
{% if request.user.is_staff %}<a href="{% url 'anatomy:mark' exam_pk=exam.pk sk=0 %}">Mark answers</a>{% endif %}
|
|
||||||
{% if request.user.is_staff %}<a href="{% url 'anatomy:exam_scores_cid' pk=exam.pk %}">Scores</a>{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -13,6 +13,8 @@ from .models import (
|
|||||||
NoteType,
|
NoteType,
|
||||||
CidUser,
|
CidUser,
|
||||||
CidUserExam,
|
CidUserExam,
|
||||||
|
UserGrades,
|
||||||
|
UserProfile,
|
||||||
)
|
)
|
||||||
|
|
||||||
# from .models import Examination, Sign, Site, Condition
|
# from .models import Examination, Sign, Site, Condition
|
||||||
@@ -28,3 +30,5 @@ admin.site.register(NoteType)
|
|||||||
admin.site.register(CidUser)
|
admin.site.register(CidUser)
|
||||||
admin.site.register(CidUserExam)
|
admin.site.register(CidUserExam)
|
||||||
admin.site.register(CidUserGroup)
|
admin.site.register(CidUserGroup)
|
||||||
|
admin.site.register(UserProfile)
|
||||||
|
admin.site.register(UserGrades)
|
||||||
|
|||||||
+16
-2
@@ -11,7 +11,7 @@ from django.forms import inlineformset_factory
|
|||||||
from atlas.models import CaseCollection
|
from atlas.models import CaseCollection
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from generic.models import CidUser, CidUserGroup, Examination, QuestionNote, UserUserGroup
|
from generic.models import CidUser, CidUserGroup, Examination, QuestionNote, UserProfile, UserUserGroup
|
||||||
|
|
||||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
from django.forms.widgets import RadioSelect, TextInput, Textarea
|
from django.forms.widgets import RadioSelect, TextInput, Textarea
|
||||||
@@ -236,8 +236,12 @@ class CidUserGroupForm(ModelForm):
|
|||||||
model = CidUserGroup
|
model = CidUserGroup
|
||||||
fields = ["name", "archive"]
|
fields = ["name", "archive"]
|
||||||
|
|
||||||
|
class UserUserGroupModelChoiceField(ModelMultipleChoiceField):
|
||||||
|
def label_from_instance(self, obj):
|
||||||
|
return f"{obj.username} ({obj.userprofile.grade}) [{obj.email}]"
|
||||||
|
|
||||||
class UserUserGroupForm(ModelForm):
|
class UserUserGroupForm(ModelForm):
|
||||||
users = ModelMultipleChoiceField(
|
users = UserUserGroupModelChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
queryset=User.objects.all(),
|
queryset=User.objects.all(),
|
||||||
widget=FilteredSelectMultiple(verbose_name="Users", is_stacked=False),
|
widget=FilteredSelectMultiple(verbose_name="Users", is_stacked=False),
|
||||||
@@ -245,3 +249,13 @@ class UserUserGroupForm(ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = UserUserGroup
|
model = UserUserGroup
|
||||||
fields = ["name", "archive", "users"]
|
fields = ["name", "archive", "users"]
|
||||||
|
|
||||||
|
#class UserForm(ModelForm):
|
||||||
|
# class Meta:
|
||||||
|
# model = User
|
||||||
|
# fields = ["first_name", "last_name", "email"]
|
||||||
|
#
|
||||||
|
#class UserProfileForm(ModelForm):
|
||||||
|
# class Meta:
|
||||||
|
# model = UserProfile
|
||||||
|
# fields = ["supervisor_name", "supervisor_email", "registration_number", "peninsula_trainee"]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.2.13 on 2022-05-23 17:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('generic', '0032_userprofile_registration_number'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userprofile',
|
||||||
|
name='grade',
|
||||||
|
field=models.IntegerField(blank=True, help_text='ST grade of the user', null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 3.2.13 on 2022-05-23 19:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('generic', '0033_userprofile_grade'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserGrades',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=100)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='userprofile',
|
||||||
|
name='grade',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userprofile',
|
||||||
|
name='peninsula_trainee',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 3.2.13 on 2022-05-23 19:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('generic', '0034_auto_20220523_2010'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userprofile',
|
||||||
|
name='grade',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='User grade', null=True, on_delete=django.db.models.deletion.CASCADE, to='generic.usergrades'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -563,12 +563,25 @@ class UserUserGroup(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("generic:user_group_update", kwargs={"pk": self.pk})
|
return reverse("generic:user_group_update", kwargs={"pk": self.pk})
|
||||||
|
|
||||||
|
class UserGrades(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
class UserProfile(models.Model):
|
class UserProfile(models.Model):
|
||||||
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||||
supervisor_email = models.EmailField(blank=True)
|
supervisor_email = models.EmailField(blank=True)
|
||||||
supervisor_name = models.CharField(max_length=100, blank=True)
|
supervisor_name = models.CharField(max_length=100, blank=True)
|
||||||
registration_number = models.CharField(max_length=25, blank=True)
|
registration_number = models.CharField(max_length=25, blank=True)
|
||||||
|
grade = models.ForeignKey(UserGrades, null=True, blank=True, help_text="User grade", on_delete=models.CASCADE)
|
||||||
|
peninsula_trainee = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
def getusername(self):
|
||||||
|
return self.user.username
|
||||||
|
|
||||||
|
username = property(getusername)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
<a href="{% url 'generic:manage_cid_exams' %}">Cids (exams)</a> /
|
<a href="{% url 'generic:manage_cid_exams' %}">Cids (exams)</a> /
|
||||||
<a href="{% url 'generic:cid_group_view' %}">Cid Groups</a> /
|
<a href="{% url 'generic:cid_group_view' %}">Cid Groups</a> /
|
||||||
<a href="{% url 'generic:user_group_view' %}">User Groups</a> /
|
<a href="{% url 'generic:user_group_view' %}">User Groups</a> /
|
||||||
|
<a href="{% url 'accounts_list' %}">Manage Users</a> /
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -7,22 +7,24 @@
|
|||||||
</p>
|
</p>
|
||||||
{% for group in groups %}
|
{% for group in groups %}
|
||||||
<p>{{group.name}}
|
<p>{{group.name}}
|
||||||
<button class="url-button" data-url="{% url 'generic:group_email' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:group_email' pk=group.pk %}
|
||||||
">Email candidate details</button>
|
">Email candidate details</button>
|
||||||
<button class="url-button" data-url="{% url 'generic:group_email_resend' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:group_email_resend' pk=group.pk %}
|
||||||
">Resend candidate details</button>
|
">Resend candidate details</button>
|
||||||
<button class="url-button" data-url="{% url 'generic:group_email_results' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:group_email_results' pk=group.pk %}
|
||||||
">Email candidate results</button>
|
">Email candidate results</button>
|
||||||
<button class="url-button" data-url="{% url 'generic:group_email_results_resend' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:group_email_results_resend' pk=group.pk %}
|
||||||
">Resend candidate results</button>
|
">Resend candidate results</button>
|
||||||
<button class="url-button" data-url="{% url 'generic:cid_group_update' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:cid_group_update' pk=group.pk %}
|
||||||
">Edit</button>
|
">Edit</button>
|
||||||
|
<button class="small-url-button" data-url="{% url 'generic:cid_group_delete' pk=group.pk %}
|
||||||
|
">Delete</button>
|
||||||
</p>
|
</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
$(".url-button").click((evt) => {
|
$(".small-url-button").click((evt) => {
|
||||||
window.location = evt.currentTarget.dataset.url;
|
window.location = evt.currentTarget.dataset.url;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,13 @@
|
|||||||
<!-- {{ form.media }} -->
|
<!-- {{ form.media }} -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Add / Edit Group / {{cidusergroup.name}}</h2>
|
{% if form.instance.id %}
|
||||||
Use this form to create / edit a CID user group.
|
<h2>Edit Group: {{cidusergroup.name}}</h2>
|
||||||
|
Use this form to edit a CID user group.
|
||||||
|
{% else %}
|
||||||
|
<h2>Add New Group</h2>
|
||||||
|
Use this form to create a CID user group.
|
||||||
|
{% endif %}
|
||||||
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
|
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
@@ -25,7 +30,12 @@ Use this form to create / edit a CID user group.
|
|||||||
|
|
||||||
<h3>Users</h3>
|
<h3>Users</h3>
|
||||||
|
|
||||||
|
{% if cidusergroup.GetGroupUsers %}
|
||||||
{{cidusergroup.GetGroupUsers}}
|
{{cidusergroup.GetGroupUsers}}
|
||||||
|
{% else %}
|
||||||
|
No users currently added to group.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{% extends app_name|add:'/base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
Filter exam list
|
||||||
|
</summary>
|
||||||
|
<form method="get">
|
||||||
|
{{ filter.form.as_p }}
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
</details>
|
||||||
|
<h4>{{filter.qs|length}} exams found.</h4>
|
||||||
|
{% for exam in filter.qs %}
|
||||||
|
<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 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 %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
@@ -7,22 +7,24 @@
|
|||||||
</p>
|
</p>
|
||||||
{% for group in groups %}
|
{% for group in groups %}
|
||||||
<p>{{group.name}}
|
<p>{{group.name}}
|
||||||
{% comment %} <button class="url-button" data-url="{% url 'generic:group_email' pk=group.pk %}
|
{% comment %} <button class="small-url-button" data-url="{% url 'generic:group_email' pk=group.pk %}
|
||||||
">Email candidate details</button>
|
">Email candidate details</button>
|
||||||
<button class="url-button" data-url="{% url 'generic:group_email_resend' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:group_email_resend' pk=group.pk %}
|
||||||
">Resend candidate details</button>
|
">Resend candidate details</button>
|
||||||
<button class="url-button" data-url="{% url 'generic:group_email_results' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:group_email_results' pk=group.pk %}
|
||||||
">Email candidate results</button>
|
">Email candidate results</button>
|
||||||
<button class="url-button" data-url="{% url 'generic:group_email_results_resend' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:group_email_results_resend' pk=group.pk %}
|
||||||
">Resend candidate results</button> {% endcomment %}
|
">Resend candidate results</button> {% endcomment %}
|
||||||
<button class="url-button" data-url="{% url 'generic:user_group_update' pk=group.pk %}
|
<button class="small-url-button" data-url="{% url 'generic:user_group_update' pk=group.pk %}
|
||||||
">Edit</button>
|
">Edit</button>
|
||||||
|
<button class="small-url-button" data-url="{% url 'generic:user_group_delete' pk=group.pk %}
|
||||||
|
">Delete</button>
|
||||||
</p>
|
</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
$(".url-button").click((evt) => {
|
$(".small-url-button").click((evt) => {
|
||||||
window.location = evt.currentTarget.dataset.url;
|
window.location = evt.currentTarget.dataset.url;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -4,18 +4,22 @@
|
|||||||
{% block css %}
|
{% block css %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
|
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
|
||||||
{{form.media}}
|
{{form.media}}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- {{ form.media }} -->
|
<!-- {{ form.media }} -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Add / Edit Group / {{userusergroup.name}}</h2>
|
{% if form.instance.id %}
|
||||||
Use this form to create / edit a CID user group.
|
<h2>Edit Group: {{userusergroup.name}}</h2>
|
||||||
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
|
{% else %}
|
||||||
|
<h2>Add New Group</h2>
|
||||||
|
{% endif %}
|
||||||
|
Use this form to create / edit a CID user group.
|
||||||
|
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
@@ -25,7 +29,18 @@ Use this form to create / edit a CID user group.
|
|||||||
|
|
||||||
<h3>Users</h3>
|
<h3>Users</h3>
|
||||||
|
|
||||||
{{userusergroup.GetGroupUsers}}
|
<ul>
|
||||||
|
{% for user in userusergroup.users.all %}
|
||||||
|
<li>{{user}}</li>{% empty %}
|
||||||
|
<li>Group currently has no users.</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
</form>
|
{% comment %} {% if userusergroup.users.all %}
|
||||||
|
{{userusergroup.GetGroupUsers}}
|
||||||
|
{% else %}
|
||||||
|
No users currently added to group.
|
||||||
|
{% endif %} {% endcomment %}
|
||||||
|
|
||||||
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -62,6 +62,9 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"cids/group/<int:pk>/update", views.CidUserGroupUpdate.as_view(), name="cid_group_update"
|
"cids/group/<int:pk>/update", views.CidUserGroupUpdate.as_view(), name="cid_group_update"
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"cids/group/<int:pk>/delete", views.CidUserGroupDelete.as_view(), name="cid_group_delete"
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"cids/group/create", views.CidUserGroupCreate.as_view(), name="cid_group_create"
|
"cids/group/create", views.CidUserGroupCreate.as_view(), name="cid_group_create"
|
||||||
),
|
),
|
||||||
@@ -70,6 +73,9 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"user/group/<int:pk>/update", views.UserUserGroupUpdate.as_view(), name="user_group_update"
|
"user/group/<int:pk>/update", views.UserUserGroupUpdate.as_view(), name="user_group_update"
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"user/group/<int:pk>/delete", views.UserUserGroupDelete.as_view(), name="user_group_delete"
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"user/group/create", views.UserUserGroupCreate.as_view(), name="user_group_create"
|
"user/group/create", views.UserUserGroupCreate.as_view(), name="user_group_create"
|
||||||
),
|
),
|
||||||
|
|||||||
+144
-65
@@ -8,6 +8,7 @@ from django.forms.models import model_to_dict
|
|||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
|
from django.urls import reverse_lazy
|
||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
@@ -29,8 +30,9 @@ import json
|
|||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.generic.edit import CreateView, UpdateView
|
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||||
from django_filters.views import FilterView
|
from django_filters.views import FilterView
|
||||||
|
from django_filters import FilterSet
|
||||||
from django_tables2.views import SingleTableMixin
|
from django_tables2.views import SingleTableMixin
|
||||||
from reversion.views import RevisionMixin
|
from reversion.views import RevisionMixin
|
||||||
from atlas.models import CaseCollection
|
from atlas.models import CaseCollection
|
||||||
@@ -71,6 +73,7 @@ import plotly.express as px
|
|||||||
# from rad.views import get_question_and_content_type
|
# from rad.views import get_question_and_content_type
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
|
|
||||||
|
|
||||||
class AuthorRequiredMixin(object):
|
class AuthorRequiredMixin(object):
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
obj = super().get_object(*args, **kwargs)
|
obj = super().get_object(*args, **kwargs)
|
||||||
@@ -78,6 +81,7 @@ class AuthorRequiredMixin(object):
|
|||||||
raise PermissionDenied() # or Http404
|
raise PermissionDenied() # or Http404
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class CidManagerRequiredMixin(UserPassesTestMixin):
|
class CidManagerRequiredMixin(UserPassesTestMixin):
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
return self.request.user.groups.filter(name="cid_user_manager").exists()
|
return self.request.user.groups.filter(name="cid_user_manager").exists()
|
||||||
@@ -289,7 +293,47 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
# group_map = {"rapids" : "rapid_checker", "anatomy" : "anatomy_checker", "longs":"long_checker"}
|
# group_map = {"rapids" : "rapid_checker", "anatomy" : "anatomy_checker", "longs":"long_checker"}
|
||||||
# exam_group = group_map[self.app_name]
|
# exam_group = group_map[self.app_name]
|
||||||
|
|
||||||
def check_user_access(self, user, exam_id=None):
|
class ExamFilter(FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model = exam
|
||||||
|
fields = {
|
||||||
|
"name": ["contains"],
|
||||||
|
"exam_mode": ["exact"],
|
||||||
|
"active": ["exact"],
|
||||||
|
"archive": ["exact"],
|
||||||
|
"open_access": ["exact"],
|
||||||
|
#'release_date': ['isnull'],
|
||||||
|
}
|
||||||
|
# filter_overrides = {
|
||||||
|
# models.CharField: {
|
||||||
|
# 'filter_class': django_filters.CharFilter,
|
||||||
|
# 'extra': lambda f: {
|
||||||
|
# 'lookup_expr': 'icontains',
|
||||||
|
# },
|
||||||
|
# },
|
||||||
|
# models.BooleanField: {
|
||||||
|
# 'filter_class': django_filters.BooleanFilter,
|
||||||
|
# 'extra': lambda f: {
|
||||||
|
# 'widget': forms.CheckboxInput,
|
||||||
|
# },
|
||||||
|
# },
|
||||||
|
# }
|
||||||
|
def __init__(self, data=None, *args, **kwargs):
|
||||||
|
# if filterset is bound, use initial values as defaults
|
||||||
|
if data is not None:
|
||||||
|
# get a mutable copy of the QueryDict
|
||||||
|
data = data.copy()
|
||||||
|
|
||||||
|
#for name, f in self.base_filters.items():
|
||||||
|
# filter param is either missing or empty, use initial as default
|
||||||
|
if not data.get("archive"):
|
||||||
|
data["archive"] = False
|
||||||
|
|
||||||
|
super().__init__(data, *args, **kwargs)
|
||||||
|
|
||||||
|
self.ExamFilter = ExamFilter
|
||||||
|
|
||||||
|
def check_user_access(self, user: User, exam_id: int = None):
|
||||||
"""Check if a user should be able to access a view
|
"""Check if a user should be able to access a view
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -547,9 +591,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
)
|
)
|
||||||
elif self.app_name == "rapids":
|
elif self.app_name == "rapids":
|
||||||
questions = (
|
questions = (
|
||||||
exam.exam_questions.select_related(
|
exam.exam_questions.select_related()
|
||||||
|
|
||||||
)
|
|
||||||
.all()
|
.all()
|
||||||
.prefetch_related(
|
.prefetch_related(
|
||||||
Prefetch(
|
Prefetch(
|
||||||
@@ -559,7 +601,11 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
),
|
),
|
||||||
to_attr="prefetched_primary_answer"
|
to_attr="prefetched_primary_answer"
|
||||||
# queryset=self.Answer.objects.filter(),
|
# queryset=self.Answer.objects.filter(),
|
||||||
), "images", "abnormality", "region", "examination"
|
),
|
||||||
|
"images",
|
||||||
|
"abnormality",
|
||||||
|
"region",
|
||||||
|
"examination",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# questions = (
|
# questions = (
|
||||||
@@ -620,7 +666,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
def exam_cids(self, request, exam_id):
|
def exam_cids(self, request, exam_id):
|
||||||
exam = get_object_or_404(self.Exam, pk=exam_id)
|
exam = get_object_or_404(self.Exam, pk=exam_id)
|
||||||
|
|
||||||
#if not request.user.groups.filter(name="cid_user_manager").exists():
|
# if not request.user.groups.filter(name="cid_user_manager").exists():
|
||||||
# raise PermissionDenied
|
# raise PermissionDenied
|
||||||
|
|
||||||
if request.user not in exam.author.all():
|
if request.user not in exam.author.all():
|
||||||
@@ -646,18 +692,13 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
if self.app_name == "atlas":
|
if self.app_name == "atlas":
|
||||||
context["collection"] = exam
|
context["collection"] = exam
|
||||||
|
|
||||||
return render(
|
return render(request, "exam_cids.html", context)
|
||||||
request,
|
|
||||||
"exam_cids.html",
|
|
||||||
context
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
def exam_users_edit(self, request, exam_id):
|
def exam_users_edit(self, request, exam_id):
|
||||||
exam = get_object_or_404(self.Exam, pk=exam_id)
|
exam = get_object_or_404(self.Exam, pk=exam_id)
|
||||||
|
|
||||||
if not request.user.groups.filter(name="cid_user_manager").exists():
|
if not request.user.groups.filter(name="cid_user_manager").exists():
|
||||||
#raise PermissionDenied
|
# raise PermissionDenied
|
||||||
if request.user not in exam.author.all():
|
if request.user not in exam.author.all():
|
||||||
if not self.check_user_access(request.user, exam_id):
|
if not self.check_user_access(request.user, exam_id):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
@@ -666,9 +707,13 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
exam_groups = exam.user_user_groups.all()
|
exam_groups = exam.user_user_groups.all()
|
||||||
|
|
||||||
available_user_users = list(User.objects.filter(user_groups__in=exam_groups).difference(current_user_users))
|
available_user_users = list(
|
||||||
|
User.objects.filter(user_groups__in=exam_groups).difference(
|
||||||
|
current_user_users
|
||||||
|
)
|
||||||
|
)
|
||||||
print(available_user_users)
|
print(available_user_users)
|
||||||
#available_user_users = User.objects.all()
|
# available_user_users = User.objects.all()
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"exam": exam,
|
"exam": exam,
|
||||||
@@ -681,18 +726,13 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
if self.app_name == "atlas":
|
if self.app_name == "atlas":
|
||||||
context["collection"] = exam
|
context["collection"] = exam
|
||||||
|
|
||||||
return render(
|
return render(request, "exam_users_edit.html", context)
|
||||||
request,
|
|
||||||
"exam_users_edit.html",
|
|
||||||
context
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def exam_cids_edit(self, request, exam_id):
|
def exam_cids_edit(self, request, exam_id):
|
||||||
exam = get_object_or_404(self.Exam, pk=exam_id)
|
exam = get_object_or_404(self.Exam, pk=exam_id)
|
||||||
|
|
||||||
if not request.user.groups.filter(name="cid_user_manager").exists():
|
if not request.user.groups.filter(name="cid_user_manager").exists():
|
||||||
#raise PermissionDenied
|
# raise PermissionDenied
|
||||||
if request.user not in exam.author.all():
|
if request.user not in exam.author.all():
|
||||||
if not self.check_user_access(request.user, exam_id):
|
if not self.check_user_access(request.user, exam_id):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
@@ -701,7 +741,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
exam_groups = exam.cid_user_groups.all()
|
exam_groups = exam.cid_user_groups.all()
|
||||||
|
|
||||||
available_cid_users = CidUser.objects.filter(group__in=exam_groups).difference(current_cid_users)
|
available_cid_users = CidUser.objects.filter(group__in=exam_groups).difference(
|
||||||
|
current_cid_users
|
||||||
|
)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"exam": exam,
|
"exam": exam,
|
||||||
@@ -714,11 +756,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
if self.app_name == "atlas":
|
if self.app_name == "atlas":
|
||||||
context["collection"] = exam
|
context["collection"] = exam
|
||||||
|
|
||||||
return render(
|
return render(request, "exam_cids_edit.html", context)
|
||||||
request,
|
|
||||||
"exam_cids_edit.html",
|
|
||||||
context
|
|
||||||
)
|
|
||||||
|
|
||||||
# @method_decorator(login_required)
|
# @method_decorator(login_required)
|
||||||
def exam_notes(self, request, pk):
|
def exam_notes(self, request, pk):
|
||||||
@@ -765,7 +803,6 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
else:
|
else:
|
||||||
app_exam_map[self.app_name].remove(pk)
|
app_exam_map[self.app_name].remove(pk)
|
||||||
|
|
||||||
|
|
||||||
data = {"status": "success", "added": add}
|
data = {"status": "success", "added": add}
|
||||||
return JsonResponse(data, status=200)
|
return JsonResponse(data, status=200)
|
||||||
|
|
||||||
@@ -778,14 +815,13 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
app_exam_map = {}
|
app_exam_map = {}
|
||||||
app_exam_map["rapids"] = user_user.user_rapid_exams
|
app_exam_map["rapids"] = user_user.user_rapid_exams
|
||||||
#app_exam_map["atlas"] = user_user.casecollection_exams
|
# app_exam_map["atlas"] = user_user.casecollection_exams
|
||||||
|
|
||||||
if add:
|
if add:
|
||||||
app_exam_map[self.app_name].add(pk)
|
app_exam_map[self.app_name].add(pk)
|
||||||
else:
|
else:
|
||||||
app_exam_map[self.app_name].remove(pk)
|
app_exam_map[self.app_name].remove(pk)
|
||||||
|
|
||||||
|
|
||||||
data = {"status": "success", "added": add}
|
data = {"status": "success", "added": add}
|
||||||
return JsonResponse(data, status=200)
|
return JsonResponse(data, status=200)
|
||||||
|
|
||||||
@@ -927,25 +963,30 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def index(self, request):
|
def index(self, request):
|
||||||
exams = self.Exam.objects.filter(author__id=request.user.id)
|
|
||||||
|
# self.check_user_access(request.user, exam.pk)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
(
|
||||||
self.app_name == "rapids"
|
self.app_name == "rapids"
|
||||||
and request.user.groups.filter(name="rapid_checker").exists()
|
and request.user.groups.filter(name="rapid_checker").exists()
|
||||||
):
|
)
|
||||||
exams = self.Exam.objects.all()
|
or (
|
||||||
if (
|
|
||||||
self.app_name == "anatomy"
|
self.app_name == "anatomy"
|
||||||
and request.user.groups.filter(name="anatomy_checker").exists()
|
and request.user.groups.filter(name="anatomy_checker").exists()
|
||||||
):
|
)
|
||||||
exams = self.Exam.objects.all()
|
or (
|
||||||
if (
|
|
||||||
self.app_name == "longs"
|
self.app_name == "longs"
|
||||||
and request.user.groups.filter(name="long_checker").exists()
|
and request.user.groups.filter(name="long_checker").exists()
|
||||||
|
)
|
||||||
):
|
):
|
||||||
exams = self.Exam.objects.all()
|
exams = self.Exam.objects.all()
|
||||||
|
else:
|
||||||
|
exams = self.Exam.objects.filter(author__id=request.user.id)
|
||||||
|
|
||||||
return render(request, "{}/index.html".format(self.app_name), {"exams": exams})
|
filter = self.ExamFilter(request.GET, queryset=exams)
|
||||||
|
|
||||||
|
return render(request, "generic/exam_index.html", {"filter": filter, "app_name": self.app_name})
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_question_detail(self, request, pk, sk):
|
def exam_question_detail(self, request, pk, sk):
|
||||||
@@ -996,7 +1037,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
for exam in exams:
|
for exam in exams:
|
||||||
if exam.active or self.check_user_access(request.user, exam.pk):
|
if exam.active or self.check_user_access(request.user, exam.pk):
|
||||||
print(exam.name, cid, passcode)
|
print(exam.name, cid, passcode)
|
||||||
if exam.exam_mode and not exam.check_cid_user(cid, passcode, user_id=request.user.pk):
|
if exam.exam_mode and not exam.check_cid_user(
|
||||||
|
cid, passcode, user_id=request.user.pk
|
||||||
|
):
|
||||||
print(exam.name, "fail")
|
print(exam.name, "fail")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1061,7 +1104,10 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{"success": False, "error": "cid or eid or answers not defined"}
|
{
|
||||||
|
"success": False,
|
||||||
|
"error": "cid or eid or answers not defined",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if not uid:
|
if not uid:
|
||||||
@@ -1070,7 +1116,10 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
not isinstance(eid, int) or (not isinstance(answer["ans"], str))
|
not isinstance(eid, int) or (not isinstance(answer["ans"], str))
|
||||||
):
|
):
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{"success": False, "error": "cid or eid or answers not defined"}
|
{
|
||||||
|
"success": False,
|
||||||
|
"error": "cid or eid or answers not defined",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
exam = get_object_or_404(self.Exam, pk=eid)
|
exam = get_object_or_404(self.Exam, pk=eid)
|
||||||
@@ -1100,9 +1149,15 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
if not existing_answers:
|
if not existing_answers:
|
||||||
if uid:
|
if uid:
|
||||||
ans = self.CidUserAnswer(answer=posted_answer, normal=normal, user=User.objects.get(id=uid))
|
ans = self.CidUserAnswer(
|
||||||
|
answer=posted_answer,
|
||||||
|
normal=normal,
|
||||||
|
user=User.objects.get(id=uid),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
ans = self.CidUserAnswer(answer=posted_answer, normal=normal, cid=cid)
|
ans = self.CidUserAnswer(
|
||||||
|
answer=posted_answer, normal=normal, cid=cid
|
||||||
|
)
|
||||||
|
|
||||||
ans.question_id = answer["qid"]
|
ans.question_id = answer["qid"]
|
||||||
ans.exam_id = eid
|
ans.exam_id = eid
|
||||||
@@ -1118,11 +1173,12 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
case "anatomy":
|
case "anatomy":
|
||||||
if not existing_answers:
|
if not existing_answers:
|
||||||
if uid:
|
if uid:
|
||||||
ans = self.CidUserAnswer(answer=posted_answer, uid=User.objects.get(id=uid))
|
ans = self.CidUserAnswer(
|
||||||
|
answer=posted_answer, uid=User.objects.get(id=uid)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
ans = self.CidUserAnswer(answer=posted_answer, cid=cid)
|
ans = self.CidUserAnswer(answer=posted_answer, cid=cid)
|
||||||
|
|
||||||
|
|
||||||
ans.question_id = answer["qid"]
|
ans.question_id = answer["qid"]
|
||||||
ans.exam_id = eid
|
ans.exam_id = eid
|
||||||
ans.score = self.Answer.MarkOptions.UNMARKED
|
ans.score = self.Answer.MarkOptions.UNMARKED
|
||||||
@@ -1168,8 +1224,6 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
ans.full_clean()
|
ans.full_clean()
|
||||||
ans.save()
|
ans.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# if ret is not True:
|
# if ret is not True:
|
||||||
# return ret
|
# return ret
|
||||||
|
|
||||||
@@ -1185,10 +1239,14 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if request.POST.get("cid").startswith("u-"):
|
if request.POST.get("cid").startswith("u-"):
|
||||||
cid_user_exam = exam.get_or_create_cid_user_exam(user_user=request.user, start_time=t)
|
cid_user_exam = exam.get_or_create_cid_user_exam(
|
||||||
|
user_user=request.user, start_time=t
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
c = CidUser.objects.filter(cid=request.POST.get("cid")).first()
|
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 = exam.get_or_create_cid_user_exam(
|
||||||
|
cid_user=c, start_time=t
|
||||||
|
)
|
||||||
|
|
||||||
cid_user_exam.end_time = timezone.now()
|
cid_user_exam.end_time = timezone.now()
|
||||||
cid_user_exam.save()
|
cid_user_exam.save()
|
||||||
@@ -1303,10 +1361,10 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
if not exam.exam_mode:
|
if not exam.exam_mode:
|
||||||
raise Http404("Packet not in 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_marks = defaultdict(list)
|
||||||
user_answers = defaultdict(list)
|
user_answers = defaultdict(list)
|
||||||
#user_names = {}
|
# user_names = {}
|
||||||
# cid_passcodes = {}
|
# cid_passcodes = {}
|
||||||
|
|
||||||
by_question = defaultdict(dict)
|
by_question = defaultdict(dict)
|
||||||
@@ -1333,7 +1391,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
cids.add(cid)
|
cids.add(cid)
|
||||||
|
|
||||||
s = cid_user_answer
|
s = cid_user_answer
|
||||||
#user_names[cid] = cid
|
# user_names[cid] = cid
|
||||||
|
|
||||||
q = cid_user_answer.question
|
q = cid_user_answer.question
|
||||||
|
|
||||||
@@ -1352,7 +1410,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
unmarked.add(index)
|
unmarked.add(index)
|
||||||
user_answers[cid].append(ans)
|
user_answers[cid].append(ans)
|
||||||
user_answers_marks[cid].append(answer_score)
|
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"):
|
if self.app_name in ("rapids", "anatomy", "sbas"):
|
||||||
by_question[q][cid] = (ans, answer_score)
|
by_question[q][cid] = (ans, answer_score)
|
||||||
@@ -1440,13 +1498,13 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
"unmarked": unmarked,
|
"unmarked": unmarked,
|
||||||
"questions": questions,
|
"questions": questions,
|
||||||
"by_question": by_question,
|
"by_question": by_question,
|
||||||
#"user_answers": dict(user_answers),
|
# "user_answers": dict(user_answers),
|
||||||
#"user_answers_marks": dict(user_answers_marks),
|
# "user_answers_marks": dict(user_answers_marks),
|
||||||
"user_scores": user_scores,
|
"user_scores": user_scores,
|
||||||
"user_scores_normalised": user_scores_normalised,
|
"user_scores_normalised": user_scores_normalised,
|
||||||
#"user_scores_list": user_scores_list,
|
# "user_scores_list": user_scores_list,
|
||||||
#"user_names": user_names,
|
# "user_names": user_names,
|
||||||
#"user_answers_and_marks": user_answers_and_marks,
|
# "user_answers_and_marks": user_answers_and_marks,
|
||||||
"max_score": max_score,
|
"max_score": max_score,
|
||||||
"mean": mean,
|
"mean": mean,
|
||||||
"median": median,
|
"median": median,
|
||||||
@@ -1621,7 +1679,10 @@ class CidUserExamView(CidManagerRequiredMixin, SingleTableMixin, FilterView):
|
|||||||
sba_exams = [(i.name, i.pk) for i in SbasExam.objects.filter(**filters)]
|
sba_exams = [(i.name, i.pk) for i in SbasExam.objects.filter(**filters)]
|
||||||
longs_exams = [(i.name, i.pk) for i in LongExam.objects.filter(**filters)]
|
longs_exams = [(i.name, i.pk) for i in LongExam.objects.filter(**filters)]
|
||||||
anatomy_exams = [(i.name, i.pk) for i in AnatomyExam.objects.filter(**filters)]
|
anatomy_exams = [(i.name, i.pk) for i in AnatomyExam.objects.filter(**filters)]
|
||||||
casecollection_exams = [(i.name, i.pk) for i in CaseCollection.objects.filter(archive=False, collection_type__gt = 0)]
|
casecollection_exams = [
|
||||||
|
(i.name, i.pk)
|
||||||
|
for i in CaseCollection.objects.filter(archive=False, collection_type__gt=0)
|
||||||
|
]
|
||||||
|
|
||||||
cid_user_groups = [
|
cid_user_groups = [
|
||||||
(i.name, i.pk) for i in CidUserGroup.objects.filter(archive=False)
|
(i.name, i.pk) for i in CidUserGroup.objects.filter(archive=False)
|
||||||
@@ -1656,7 +1717,10 @@ class CidUserView(CidManagerRequiredMixin, SingleTableMixin, FilterView):
|
|||||||
sba_exams = [(i.name, i.pk) for i in SbasExam.objects.filter(**filters)]
|
sba_exams = [(i.name, i.pk) for i in SbasExam.objects.filter(**filters)]
|
||||||
longs_exams = [(i.name, i.pk) for i in LongExam.objects.filter(**filters)]
|
longs_exams = [(i.name, i.pk) for i in LongExam.objects.filter(**filters)]
|
||||||
anatomy_exams = [(i.name, i.pk) for i in AnatomyExam.objects.filter(**filters)]
|
anatomy_exams = [(i.name, i.pk) for i in AnatomyExam.objects.filter(**filters)]
|
||||||
casecollection_exams = [(i.name, i.pk) for i in CaseCollection.objects.filter(archive=False, collection_type__gt = 0)]
|
casecollection_exams = [
|
||||||
|
(i.name, i.pk)
|
||||||
|
for i in CaseCollection.objects.filter(archive=False, collection_type__gt=0)
|
||||||
|
]
|
||||||
|
|
||||||
context["physics_exams"] = physics_exams
|
context["physics_exams"] = physics_exams
|
||||||
context["rapid_exams"] = rapid_exams
|
context["rapid_exams"] = rapid_exams
|
||||||
@@ -1683,6 +1747,7 @@ def cid_group_view(request):
|
|||||||
{"groups": groups},
|
{"groups": groups},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def cid_group_view_all(request):
|
def cid_group_view_all(request):
|
||||||
groups = CidUserGroup.objects.filter()
|
groups = CidUserGroup.objects.filter()
|
||||||
@@ -1693,6 +1758,7 @@ def cid_group_view_all(request):
|
|||||||
{"groups": groups, "view_all": True},
|
{"groups": groups, "view_all": True},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def user_group_view(request):
|
def user_group_view(request):
|
||||||
groups = UserUserGroup.objects.filter(archive=False)
|
groups = UserUserGroup.objects.filter(archive=False)
|
||||||
@@ -1703,6 +1769,7 @@ def user_group_view(request):
|
|||||||
{"groups": groups},
|
{"groups": groups},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def user_group_view_all(request):
|
def user_group_view_all(request):
|
||||||
groups = UserUserGroup.objects.filter()
|
groups = UserUserGroup.objects.filter()
|
||||||
@@ -1776,12 +1843,11 @@ def group_email_results(request, pk, resend=False):
|
|||||||
def candidate_email_details(request, cid, resend=False):
|
def candidate_email_details(request, cid, resend=False):
|
||||||
user = CidUser.objects.get(cid=cid)
|
user = CidUser.objects.get(cid=cid)
|
||||||
|
|
||||||
email_sent, comment = user.email_details(
|
email_sent, comment = user.email_details(resend=False)
|
||||||
resend=False
|
|
||||||
)
|
|
||||||
|
|
||||||
return JsonResponse({"sent": email_sent, "comment": comment})
|
return JsonResponse({"sent": email_sent, "comment": comment})
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def candidate_email_results(request, cid, resend=False):
|
def candidate_email_results(request, cid, resend=False):
|
||||||
user = CidUser.objects.get(cid=cid)
|
user = CidUser.objects.get(cid=cid)
|
||||||
@@ -1965,6 +2031,12 @@ class CidUserUpdate(RevisionMixin, CidManagerRequiredMixin, UpdateView):
|
|||||||
form_class = CidUserForm
|
form_class = CidUserForm
|
||||||
|
|
||||||
|
|
||||||
|
class CidUserGroupDelete(RevisionMixin, CidManagerRequiredMixin, DeleteView):
|
||||||
|
model = CidUserGroup
|
||||||
|
template_name = "confirm_delete.html"
|
||||||
|
success_url = reverse_lazy("generic:cid_group_view")
|
||||||
|
|
||||||
|
|
||||||
class CidUserGroupCreate(RevisionMixin, CidManagerRequiredMixin, CreateView):
|
class CidUserGroupCreate(RevisionMixin, CidManagerRequiredMixin, CreateView):
|
||||||
model = CidUserGroup
|
model = CidUserGroup
|
||||||
form_class = CidUserGroupForm
|
form_class = CidUserGroupForm
|
||||||
@@ -1974,6 +2046,7 @@ class CidUserGroupUpdate(RevisionMixin, CidManagerRequiredMixin, UpdateView):
|
|||||||
model = CidUserGroup
|
model = CidUserGroup
|
||||||
form_class = CidUserGroupForm
|
form_class = CidUserGroupForm
|
||||||
|
|
||||||
|
|
||||||
class UserUserGroupCreate(RevisionMixin, CidManagerRequiredMixin, CreateView):
|
class UserUserGroupCreate(RevisionMixin, CidManagerRequiredMixin, CreateView):
|
||||||
model = UserUserGroup
|
model = UserUserGroup
|
||||||
form_class = UserUserGroupForm
|
form_class = UserUserGroupForm
|
||||||
@@ -1982,3 +2055,9 @@ class UserUserGroupCreate(RevisionMixin, CidManagerRequiredMixin, CreateView):
|
|||||||
class UserUserGroupUpdate(RevisionMixin, CidManagerRequiredMixin, UpdateView):
|
class UserUserGroupUpdate(RevisionMixin, CidManagerRequiredMixin, UpdateView):
|
||||||
model = UserUserGroup
|
model = UserUserGroup
|
||||||
form_class = UserUserGroupForm
|
form_class = UserUserGroupForm
|
||||||
|
|
||||||
|
|
||||||
|
class UserUserGroupDelete(RevisionMixin, CidManagerRequiredMixin, DeleteView):
|
||||||
|
model = UserUserGroup
|
||||||
|
template_name = "confirm_delete.html"
|
||||||
|
success_url = reverse_lazy("generic:user_group_view")
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
{% extends 'longs/base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% for exam in exams %}
|
|
||||||
<div class="longs">
|
|
||||||
<h1><a href="{% url 'longs:exam_overview' pk=exam.pk %}">Exam: {{ exam.name }} </a></h1>
|
|
||||||
{% if request.user.is_staff %}<a href="{% url 'longs:mark_question_overview' exam_id=exam.pk sk=0 %}">Mark answers</a>{% endif %}
|
|
||||||
{% if request.user.is_staff %}<a href="{% url 'longs:exam_scores_cid' pk=exam.pk %}">Scores</a>{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{% extends 'longs/base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
My Questions:
|
|
||||||
<ul>
|
|
||||||
{% for long in user_longs %}
|
|
||||||
<li{% if long.scrapped %} class='long-scrapped' {% endif %}><a
|
|
||||||
href="{% url 'longs:question_detail' pk=long.pk %}">{{long}}
|
|
||||||
[{{long.get_authors}}]</a></li>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
Other Questions:
|
|
||||||
<ul>
|
|
||||||
{% for long in other_longs %}
|
|
||||||
<li{% if long.scrapped %} class='long-scrapped' {% endif %}><a
|
|
||||||
href="{% url 'longs:question_detail' pk=long.pk %}">{{long}} [{{long.get_authors}}]</a></li>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{% extends 'physics/base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% for exam in exams %}
|
|
||||||
<div class="physics">
|
|
||||||
<h1><a href="{% url 'physics:exam_overview' pk=exam.pk %}">Exam: {{ exam.name }} </a></h1>
|
|
||||||
{% if request.user.is_staff %}<a href="{% url 'physics:exam_scores_cid' pk=exam.pk %}">Scores</a>{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
||||||
+15
-6
@@ -269,18 +269,27 @@ SENDGRID_API_KEY = (
|
|||||||
"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"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
EMAIL_HOST = "smtp.sendgrid.net"
|
# SENDGRID
|
||||||
EMAIL_HOST_USER = "apikey" # this is exactly the value 'apikey'
|
#EMAIL_HOST = "smtp.sendgrid.net"
|
||||||
EMAIL_HOST_PASSWORD = SENDGRID_API_KEY
|
#EMAIL_HOST_USER = "apikey" # this is exactly the value 'apikey'
|
||||||
EMAIL_PORT = 587
|
#EMAIL_HOST_PASSWORD = SENDGRID_API_KEY
|
||||||
EMAIL_USE_TLS = True
|
#EMAIL_PORT = 587
|
||||||
|
#EMAIL_USE_TLS = True
|
||||||
|
|
||||||
# EMAIL_HOST = "smtp.office365.com "
|
# EMAIL_HOST = "smtp.office365.com "
|
||||||
# EMAIL_HOST_USER = "Penra.courses@nhs.net"
|
# EMAIL_HOST_USER = "Penra.courses@nhs.net"
|
||||||
# EMAIL_HOST_PASSWORD = '"got3<}%o"~\J",\Di3<'
|
# EMAIL_HOST_PASSWORD = '"got3<}%o"~\J",\Di3<'
|
||||||
# EMAIL_PORT = 587
|
# EMAIL_PORT = 587
|
||||||
# EMAIL_USE_TLS = True
|
# EMAIL_USE_TLS = True
|
||||||
# EMAIL_USE_SSl = False
|
# EMAIL_USE_SSl = False
|
||||||
# DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
|
|
||||||
|
DEFAULT_FROM_EMAIL = "no-reply@penracourses.org.uk"
|
||||||
|
EMAIL_HOST = "webhosting2027.is.cc"
|
||||||
|
EMAIL_HOST_USER = "no-reply@penracourses.org.uk"
|
||||||
|
EMAIL_HOST_PASSWORD = "I(5E)&1w+Bh)"
|
||||||
|
EMAIL_PORT = 465
|
||||||
|
#EMAIL_USE_TLS = True
|
||||||
|
EMAIL_USE_SSL = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .settings_local import *
|
from .settings_local import *
|
||||||
|
|||||||
@@ -53,8 +53,12 @@ urlpatterns = [
|
|||||||
path("sbas/", include("sbas.urls"), name="sbas"),
|
path("sbas/", include("sbas.urls"), name="sbas"),
|
||||||
path("generic/", include("generic.urls"), name="generic"),
|
path("generic/", include("generic.urls"), name="generic"),
|
||||||
path("atlas/", include("atlas.urls"), name="atlas"),
|
path("atlas/", include("atlas.urls"), name="atlas"),
|
||||||
|
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"),
|
||||||
path("accounts/", include("django.contrib.auth.urls")),
|
path("accounts/", include("django.contrib.auth.urls")),
|
||||||
path("accounts/profile", views.profile, name="profile"),
|
path("accounts/profile", views.profile, name="profile"),
|
||||||
|
path("accounts/profile/<str:slug>/", views.account_profile, name="account_profile"),
|
||||||
path("", TemplateView.as_view(template_name="index.html"), name="home"),
|
path("", TemplateView.as_view(template_name="index.html"), name="home"),
|
||||||
path("cid/results/<int:cid>/", views.cid_results, name="cid_results"),
|
path("cid/results/<int:cid>/", views.cid_results, name="cid_results"),
|
||||||
path("cid/<int:pk>/<str:passcode>", views.cid_scores, name="cid_scores"),
|
path("cid/<int:pk>/<str:passcode>", views.cid_scores, name="cid_scores"),
|
||||||
|
|||||||
+72
-4
@@ -1,6 +1,6 @@
|
|||||||
from atlas.models import CaseCollection, CidReportAnswer
|
from atlas.models import CaseCollection, CidReportAnswer
|
||||||
from generic.decorators import user_is_cid_user_manager
|
from generic.decorators import user_is_cid_user_manager
|
||||||
from generic.views import get_question_and_content_type
|
from generic.views import CidManagerRequiredMixin, get_question_and_content_type
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
@@ -15,7 +15,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
|
||||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView, TemplateView
|
||||||
|
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ from rapids.views import GenericExamViews as RapidsExamsViews
|
|||||||
from longs.views import GenericExamViews as LongsExamViews
|
from longs.views import GenericExamViews as LongsExamViews
|
||||||
|
|
||||||
from generic.forms import QuestionNoteForm
|
from generic.forms import QuestionNoteForm
|
||||||
from generic.models import CidUser, QuestionNote
|
from generic.models import CidUser, QuestionNote, UserProfile
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@@ -71,6 +71,12 @@ def profile(request):
|
|||||||
return render(request, "profile.html", {"user": user})
|
return render(request, "profile.html", {"user": user})
|
||||||
|
|
||||||
|
|
||||||
|
@user_is_cid_user_manager
|
||||||
|
def account_profile(request, slug):
|
||||||
|
user = get_object_or_404(User, username=slug)
|
||||||
|
return render(request, "profile.html", {"user": user})
|
||||||
|
|
||||||
|
|
||||||
def cid_selector(request):
|
def cid_selector(request):
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
@@ -87,7 +93,7 @@ def cid_scores_admin(request, cid):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
#@user_passes_test(lambda u: u.is_superuser)
|
# @user_passes_test(lambda u: u.is_superuser)
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def cid_results(request, cid):
|
def cid_results(request, cid):
|
||||||
cid_user = CidUser.objects.filter(cid=cid).first()
|
cid_user = CidUser.objects.filter(cid=cid).first()
|
||||||
@@ -432,3 +438,65 @@ def server_error(request):
|
|||||||
# response.status_code = 404
|
# response.status_code = 404
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class UserListView(CidManagerRequiredMixin, ListView):
|
||||||
|
model = User
|
||||||
|
template_name = "user_list_view.html"
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateUserView(CidManagerRequiredMixin, UpdateView):
|
||||||
|
model = User
|
||||||
|
fields = ["first_name", "last_name", "email"] # Keep listing whatever fields
|
||||||
|
# the combined UserProfile and User exposes.
|
||||||
|
template_name = "user_update.html"
|
||||||
|
slug_field = "username"
|
||||||
|
slug_url_kwarg = "slug"
|
||||||
|
success_url = reverse_lazy("profile")
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateUserProfileView(CidManagerRequiredMixin, UpdateView):
|
||||||
|
model = UserProfile
|
||||||
|
fields = [
|
||||||
|
"supervisor_name",
|
||||||
|
"supervisor_email",
|
||||||
|
"grade",
|
||||||
|
"registration_number",
|
||||||
|
"peninsula_trainee",
|
||||||
|
] # Keep listing whatever fields
|
||||||
|
template_name = "user_update.html"
|
||||||
|
slug_field = "user__username"
|
||||||
|
slug_url_kwarg = "slug"
|
||||||
|
success_url = reverse_lazy("profile")
|
||||||
|
|
||||||
|
|
||||||
|
# class UpdateUser(TemplateView):
|
||||||
|
#
|
||||||
|
# user_form_class = UserForm
|
||||||
|
# comment_form_class = UserProfileForm
|
||||||
|
# template_name = 'user_update.html'
|
||||||
|
# slug_field = 'username'
|
||||||
|
# slug_url_kwarg = 'slug'
|
||||||
|
#
|
||||||
|
# def post(self, request):
|
||||||
|
# post_data = request.POST or None
|
||||||
|
# user_form = self.user_form_class(post_data, prefix='user')
|
||||||
|
# profile_form = self.profile_form_class(post_data, prefix='userprofile')
|
||||||
|
#
|
||||||
|
# context = self.get_context_data(post_form=user_form,
|
||||||
|
# profile_form=profile_form)
|
||||||
|
#
|
||||||
|
# if user_form.is_valid():
|
||||||
|
# self.form_save(user_form)
|
||||||
|
# if profile_form.is_valid():
|
||||||
|
# self.form_save(profile_form)
|
||||||
|
#
|
||||||
|
# return self.render_to_response(context)
|
||||||
|
#
|
||||||
|
# def form_save(self, form):
|
||||||
|
# obj = form.save()
|
||||||
|
# #messages.success(self.request, "{} saved successfully".format(obj))
|
||||||
|
# return obj
|
||||||
|
#
|
||||||
|
# def get(self, request, *args, **kwargs):
|
||||||
|
# return self.post(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
{% extends 'rapids/base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% for exam in exams %}
|
|
||||||
<div class="rapids">
|
|
||||||
<h1><a href="{% url 'rapids:exam_overview' pk=exam.pk %}">Exam: {{ exam.name }} </a></h1>
|
|
||||||
{% if exam.exam_mode %}
|
|
||||||
{% if request.user.is_staff %}<a href="{% url 'rapids:mark' exam_pk=exam.pk sk=0 %}">Mark answers</a>{% endif %}
|
|
||||||
{% if request.user.is_staff %}<a href="{% url 'rapids:exam_scores_cid' pk=exam.pk %}">Scores</a>{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{% extends 'rapids/base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
My Questions:
|
|
||||||
<ul>
|
|
||||||
{% for rapid in user_rapids %}
|
|
||||||
<li{% if rapid.scrapped %} class='rapid-scrapped' {% endif %}><a
|
|
||||||
href="{% url 'rapids:question_detail' pk=rapid.pk %}">{{rapid}}
|
|
||||||
[{{rapid.get_authors}}]</a></li>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
Other Questions:
|
|
||||||
<ul>
|
|
||||||
{% for rapid in other_rapids %}
|
|
||||||
<li{% if rapid.scrapped %} class='rapid-scrapped' {% endif %}><a
|
|
||||||
href="{% url 'rapids:question_detail' pk=rapid.pk %}">{{rapid}} [{{rapid.get_authors}}]</a></li>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -2,11 +2,18 @@ import pytest
|
|||||||
#from django.contrib.auth.models import User
|
#from django.contrib.auth.models import User
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
APP_NAMES = ("rapids", "anatomy", "longs", "physics", "sbas")
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def create_superuser(db, django_user_model):
|
def create_superuser(db, django_user_model):
|
||||||
return django_user_model.objects.create_superuser("admin", "ross@xkjq.uk", "adminpassword")
|
return django_user_model.objects.create_superuser("admin", "ross@xkjq.uk", "adminpassword")
|
||||||
|
|
||||||
def test_index(client, create_superuser):
|
# 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")
|
client.login(username="admin", password="adminpassword")
|
||||||
response = client.get(reverse('rapids:index'))
|
response = client.get(reverse(f'{app_name}:index'))
|
||||||
|
print(response.status_code)
|
||||||
|
print(response)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{% extends 'sbas/base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% for exam in exams %}
|
|
||||||
<div class="sbas">
|
|
||||||
<h1><a href="{% url 'sbas:exam_overview' pk=exam.pk %}">Exam: {{ exam.name }} </a></h1>
|
|
||||||
{% if request.user.is_staff %}<a href="{% url 'sbas:exam_scores_cid' pk=exam.pk %}">Scores</a>{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="anatomy">
|
<div class="anatomy">
|
||||||
<h1>Your profile</h1>
|
<h1>Profile</h1>
|
||||||
<div>
|
<div>
|
||||||
Username: {{ user.username }}
|
Username: {{ user.username }}
|
||||||
</div>
|
</div>
|
||||||
@@ -13,6 +13,9 @@
|
|||||||
Name: {{ user.first_name }}
|
Name: {{ user.first_name }}
|
||||||
{{ user.last_name }}
|
{{ user.last_name }}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
Grade: {{ user.userprofile.grade }}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Supervisor: {{ user.userprofile.supervisor_name }}<br/>
|
Supervisor: {{ user.userprofile.supervisor_name }}<br/>
|
||||||
Supervisor Email: {{ user.userprofile.supervisor_email }}<br/>
|
Supervisor Email: {{ user.userprofile.supervisor_email }}<br/>
|
||||||
@@ -22,5 +25,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="{% url 'password_change'%}">Change password</a>
|
<a href="{% url 'password_change'%}">Change password</a>
|
||||||
|
<a href="{% url 'account_update' user.username %}">Update user details</a>
|
||||||
|
<a href="{% url 'account_profile_update' user.username %}">Update user profile</a>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{% extends "generic/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Users</h2>
|
||||||
|
<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>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{% extends 'generic/base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Editing user: {{object.username}}</h2>
|
||||||
|
<form method="post">{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<input type="submit" value="Update">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user