Improve group management
This commit is contained in:
@@ -8,7 +8,6 @@
|
|||||||
- Test coverage
|
- Test coverage
|
||||||
- Privacy policy
|
- Privacy policy
|
||||||
- Cookie policy
|
- Cookie policy
|
||||||
- Cloning exams should include authors
|
|
||||||
|
|
||||||
|
|
||||||
# Future
|
# Future
|
||||||
|
|||||||
@@ -178,6 +178,12 @@ class ExamBase(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("{}:exam_overview".format(self.app_name), kwargs={"pk": self.pk})
|
return reverse("{}:exam_overview".format(self.app_name), kwargs={"pk": self.pk})
|
||||||
|
|
||||||
|
def get_cid_edit_url(self):
|
||||||
|
return reverse("{}:exam_cids_edit".format(self.app_name), kwargs={"exam_id": self.pk})
|
||||||
|
|
||||||
|
def get_user_edit_url(self):
|
||||||
|
return reverse("{}:exam_users_edit".format(self.app_name), kwargs={"exam_id": self.pk})
|
||||||
|
|
||||||
def get_take_url(self):
|
def get_take_url(self):
|
||||||
return f"{settings.REMOTE_URL}/rts"
|
return f"{settings.REMOTE_URL}/rts"
|
||||||
|
|
||||||
|
|||||||
@@ -3,20 +3,40 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Group: {{group.name}}</h2>
|
<h2>Group: {{group.name}}</h2>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Help</summary>
|
||||||
|
<p>This page shows an overview of the candidates and exams that are associated with the group.</p>
|
||||||
|
|
||||||
|
<p>Once a candidate has been added to a group and the group has been added to an exam individual candidates can be allocated to the exam. Please note: simply adding the group to the exam will not automatically give the group candidates access to that exam, they will have to be added individually/as a group.</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
<h3>Users</h3>
|
<h3>Users</h3>
|
||||||
<p>Count: {{users|length}}</p>
|
<p title="The number of users that have been added to this group">Count: {{users|length}}</p>
|
||||||
|
|
||||||
{% if users %}
|
{% if users %}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
|
||||||
|
{% if group_type == "cid" %}
|
||||||
<tr><th>Email</th><th>CID</th><th>Passcode</th></tr>
|
<tr><th>Email</th><th>CID</th><th>Passcode</th></tr>
|
||||||
|
{% else %}
|
||||||
|
<tr><th>User</th></tr>
|
||||||
|
{% endif %}
|
||||||
</thead>
|
</thead>
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
|
{% if group_type == "cid" %}
|
||||||
<tr><td>{{user.email}}</td><td>{{user.cid}}</td><td>{{user.passcode}}</td></tr>
|
<tr><td>{{user.email}}</td><td>{{user.cid}}</td><td>{{user.passcode}}</td></tr>
|
||||||
|
{% else %}
|
||||||
|
<tr><td>{{user.username}}</td></tr>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
This group has no users. They can be created (or added) <a href="{% url 'generic:manage_cids' %}">here</a>
|
This group has no users.
|
||||||
|
|
||||||
|
{% if group_type == "cid" %}
|
||||||
|
They can be created (or added) <a href="{% url 'generic:manage_cids' %}">here</a>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h3>Exams</h3>
|
<h3>Exams</h3>
|
||||||
The group is currently associated with the following exams
|
The group is currently associated with the following exams
|
||||||
@@ -27,17 +47,34 @@
|
|||||||
{% for exam in value %}
|
{% for exam in value %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{exam.get_absolute_url}}">{{exam}}</a>
|
<a href="{{exam.get_absolute_url}}">{{exam}}</a>
|
||||||
|
{% if group_type == "cid" %}
|
||||||
|
<a href="{{exam.get_cid_edit_url}}" class="edit-link">Edit candidates</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{exam.get_user_edit_url}}" class="edit-link">Edit candidates</a>
|
||||||
|
{% endif %}
|
||||||
|
<span class="cid-number">
|
||||||
|
{% if group_type == "cid" %}
|
||||||
|
(Cid candidate count: {{exam.valid_cid_users.count}})
|
||||||
|
{% else %}
|
||||||
|
(User candidate count: {{exam.valid_user_users.count}})
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
td, th { padding-left: 10px }
|
td, th { padding-left: 10px }
|
||||||
|
|
||||||
|
.cid-number {float: right}
|
||||||
|
|
||||||
|
.edit-link {
|
||||||
|
font-size: smaller;
|
||||||
|
opacity: 60%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -23,7 +23,10 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
{% comment %} Hide the toggle button if there are no users (to add or remove) {% endcomment %}
|
||||||
|
{% if current_cid_users or available_cid_users %}
|
||||||
<p><button class="toggle-all-btn">Toggle all</button></p>
|
<p><button class="toggle-all-btn">Toggle all</button></p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
|
|||||||
@@ -13,15 +13,25 @@
|
|||||||
|
|
||||||
{% if exam.recreate_json %}
|
{% if exam.recreate_json %}
|
||||||
<div class="alert alert-warning" role="alert">
|
<div class="alert alert-warning" role="alert">
|
||||||
Exam JSON is out of date.
|
Exam JSON may be out of date.
|
||||||
|
|
||||||
<a href="{% url app_name|add:':exam_json_recreate' pk=exam.pk %}">Click here to force refresh</a>
|
<a href="{% url app_name|add:':exam_json_recreate' pk=exam.pk %}">Click here to force refresh</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
This exam has {{question_number}} questions. Time limit: {{exam.time_limit}} seconds.
|
|
||||||
|
|
||||||
|
{% if exam.exam_mode %}
|
||||||
|
{% if not candidate_count.0 and not candidate_count.1 %}
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
This exam does not have any candidates.
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
This exam has {{question_number}} questions. Time limit: {{exam.time_limit}} seconds.
|
||||||
Exam mode: {{ exam.exam_mode }}<br />
|
Exam mode: {{ exam.exam_mode }}<br />
|
||||||
|
Candidates - cid: {{candidate_count.0}}, users: {{candidate_count.1}}<br />
|
||||||
Open access: {{ exam.open_access }}<br />
|
Open access: {{ exam.open_access }}<br />
|
||||||
|
|
||||||
<div class="parent-help" title="Click to enable / disable the exam">
|
<div class="parent-help" title="Click to enable / disable the exam">
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
{% extends 'generic/base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h2>Group: {{group.name}}</h2>
|
|
||||||
|
|
||||||
<h3>Users</h3>
|
|
||||||
<p>Count: {{users|length}}</p>
|
|
||||||
|
|
||||||
{% if users %}
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>User</th></tr>
|
|
||||||
</thead>
|
|
||||||
{% for user in users %}
|
|
||||||
<tr><td>{{user.username}}</td></tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% else %}
|
|
||||||
This group has no users.
|
|
||||||
{% endif %}
|
|
||||||
<h3>Exams</h3>
|
|
||||||
The group is currently associated with the following exams
|
|
||||||
{% with group.GetGroupExams as exam_map %}
|
|
||||||
{% for key, value in exam_map.items %}
|
|
||||||
<h4>{{key}}</h4>
|
|
||||||
<ul>
|
|
||||||
{% for exam in value %}
|
|
||||||
<li>
|
|
||||||
<a href="{{exam.get_absolute_url}}">{{exam}}</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endfor %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
td, th { padding-left: 10px }
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
+7
-3
@@ -829,6 +829,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
question_number = len(questions)
|
question_number = len(questions)
|
||||||
|
|
||||||
|
cid_candidates = exam.valid_cid_users.count()
|
||||||
|
users_candidates = exam.valid_user_users.count()
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"{}/exam_overview.html".format(self.app_name),
|
"{}/exam_overview.html".format(self.app_name),
|
||||||
@@ -839,6 +842,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
"can_edit": can_edit,
|
"can_edit": can_edit,
|
||||||
"notes": notes,
|
"notes": notes,
|
||||||
"app_name": self.app_name,
|
"app_name": self.app_name,
|
||||||
|
"candidate_count": (cid_candidates, users_candidates)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2242,7 +2246,7 @@ def cid_group_view_detail(request, group_id):
|
|||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"generic/cid_group_view_detail.html",
|
"generic/cid_group_view_detail.html",
|
||||||
{"group": group, "users": users},
|
{"group": group, "users": users, "group_type": "cid"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
@@ -2263,8 +2267,8 @@ def user_group_view_detail(request, group_id):
|
|||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"generic/user_group_view_detail.html",
|
"generic/cid_group_view_detail.html",
|
||||||
{"group": group, "users": users},
|
{"group": group, "users": users, "group_type": "user"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% load thumbnail %}
|
{% load thumbnail %}
|
||||||
<div class="physics">
|
<div class="physics">
|
||||||
{% include 'generic/exam_overview_headers.html' %}
|
{% include 'generic/exam_overview_headers.html' %}
|
||||||
|
|
||||||
This exam will be available to take <a href="{% url 'physics:exam_start' pk=exam.pk %}">here</a> (when active).
|
This exam will be available to take <a href="{% url 'physics:exam_start' pk=exam.pk %}">here</a> (when active).
|
||||||
@@ -15,20 +15,35 @@
|
|||||||
<li>
|
<li>
|
||||||
{{ question.stem }}
|
{{ question.stem }}
|
||||||
<ol type="a" class="abcde">
|
<ol type="a" class="abcde">
|
||||||
<li>
|
<li class="question-a">
|
||||||
{{ question.a }}: {{ question.a_answer }}
|
<span class="question-text">{{ question.a }}</span>: <span class="question-answer">{{ question.a_answer }}</span>
|
||||||
|
{% if question.a_feedback %}
|
||||||
|
<span class="question-feedback">{{question.a_feedback}}</span>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li class="question-b">
|
||||||
{{ question.b }}: {{ question.b_answer }}
|
<span class="question-text">{{ question.b }}</span>: <span class="question-answer">{{ question.b_answer }}</span>
|
||||||
|
{% if question.b_feedback %}
|
||||||
|
<span class="question-feedback">{{question.b_feedback}}</span>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li class="question-c">
|
||||||
{{ question.c }}: {{ question.c_answer }}
|
<span class="question-text">{{ question.c }}</span>: <span class="question-answer">{{ question.c_answer }}</span>
|
||||||
|
{% if question.c_feedback %}
|
||||||
|
<span class="question-feedback">{{question.c_feedback}}</span>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li class="question-d">
|
||||||
{{ question.d }}: {{ question.d_answer }}
|
<span class="question-text">{{ question.d }}</span>: <span class="question-answer">{{ question.d_answer }}</span>
|
||||||
|
{% if question.d_feedback %}
|
||||||
|
<span class="question-feedback">{{question.d_feedback}}</span>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li class="question-e">
|
||||||
{{ question.e }}: {{ question.e_answer }}
|
<span class="question-text">{{ question.e }}</span>: <span class="question-answer">{{ question.e_answer }}</span>
|
||||||
|
{% if question.e_feedback %}
|
||||||
|
<span class="question-feedback">{{question.e_feedback}}</span>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
Category: {{ question.category }}, <a href="{% url 'physics:question_detail' pk=question.pk %}">View [id: {{question.pk}}]</a> <a
|
Category: {{ question.category }}, <a href="{% url 'physics:question_detail' pk=question.pk %}">View [id: {{question.pk}}]</a> <a
|
||||||
@@ -39,6 +54,20 @@
|
|||||||
</ol>
|
</ol>
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% include 'exam_overview_js.html' %}
|
{% include 'exam_overview_js.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% block css %}
|
||||||
|
<style>
|
||||||
|
.question-answer {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
.question-feedback {
|
||||||
|
opacity: 50%;
|
||||||
|
float: right
|
||||||
|
}
|
||||||
|
.question-feedback::before {
|
||||||
|
content: "Feedback: ";
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user