Improve group management
This commit is contained in:
@@ -8,7 +8,6 @@
|
||||
- Test coverage
|
||||
- Privacy policy
|
||||
- Cookie policy
|
||||
- Cloning exams should include authors
|
||||
|
||||
|
||||
# Future
|
||||
|
||||
@@ -178,6 +178,12 @@ class ExamBase(models.Model):
|
||||
def get_absolute_url(self):
|
||||
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):
|
||||
return f"{settings.REMOTE_URL}/rts"
|
||||
|
||||
|
||||
@@ -3,20 +3,40 @@
|
||||
{% block content %}
|
||||
<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>
|
||||
<p>Count: {{users|length}}</p>
|
||||
<p title="The number of users that have been added to this group">Count: {{users|length}}</p>
|
||||
|
||||
{% if users %}
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
{% if group_type == "cid" %}
|
||||
<tr><th>Email</th><th>CID</th><th>Passcode</th></tr>
|
||||
{% else %}
|
||||
<tr><th>User</th></tr>
|
||||
{% endif %}
|
||||
</thead>
|
||||
{% for user in users %}
|
||||
{% if group_type == "cid" %}
|
||||
<tr><td>{{user.email}}</td><td>{{user.cid}}</td><td>{{user.passcode}}</td></tr>
|
||||
{% else %}
|
||||
<tr><td>{{user.username}}</td></tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% 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 %}
|
||||
<h3>Exams</h3>
|
||||
The group is currently associated with the following exams
|
||||
@@ -27,17 +47,34 @@
|
||||
{% for exam in value %}
|
||||
<li>
|
||||
<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>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
|
||||
<style>
|
||||
td, th { padding-left: 10px }
|
||||
|
||||
.cid-number {float: right}
|
||||
|
||||
.edit-link {
|
||||
font-size: smaller;
|
||||
opacity: 60%;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@@ -23,7 +23,10 @@
|
||||
{% endfor %}
|
||||
</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>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
|
||||
@@ -13,15 +13,25 @@
|
||||
|
||||
{% if exam.recreate_json %}
|
||||
<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>
|
||||
</div>
|
||||
{% 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 />
|
||||
Candidates - cid: {{candidate_count.0}}, users: {{candidate_count.1}}<br />
|
||||
Open access: {{ exam.open_access }}<br />
|
||||
|
||||
<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)
|
||||
|
||||
cid_candidates = exam.valid_cid_users.count()
|
||||
users_candidates = exam.valid_user_users.count()
|
||||
|
||||
return render(
|
||||
request,
|
||||
"{}/exam_overview.html".format(self.app_name),
|
||||
@@ -839,6 +842,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
"can_edit": can_edit,
|
||||
"notes": notes,
|
||||
"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(
|
||||
request,
|
||||
"generic/cid_group_view_detail.html",
|
||||
{"group": group, "users": users},
|
||||
{"group": group, "users": users, "group_type": "cid"},
|
||||
)
|
||||
|
||||
@user_is_cid_user_manager
|
||||
@@ -2263,8 +2267,8 @@ def user_group_view_detail(request, group_id):
|
||||
|
||||
return render(
|
||||
request,
|
||||
"generic/user_group_view_detail.html",
|
||||
{"group": group, "users": users},
|
||||
"generic/cid_group_view_detail.html",
|
||||
{"group": group, "users": users, "group_type": "user"},
|
||||
)
|
||||
|
||||
@user_is_cid_user_manager
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% load thumbnail %}
|
||||
<div class="physics">
|
||||
{% load thumbnail %}
|
||||
<div class="physics">
|
||||
{% 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).
|
||||
@@ -15,20 +15,35 @@
|
||||
<li>
|
||||
{{ question.stem }}
|
||||
<ol type="a" class="abcde">
|
||||
<li>
|
||||
{{ question.a }}: {{ question.a_answer }}
|
||||
<li class="question-a">
|
||||
<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>
|
||||
{{ question.b }}: {{ question.b_answer }}
|
||||
<li class="question-b">
|
||||
<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>
|
||||
{{ question.c }}: {{ question.c_answer }}
|
||||
<li class="question-c">
|
||||
<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>
|
||||
{{ question.d }}: {{ question.d_answer }}
|
||||
<li class="question-d">
|
||||
<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>
|
||||
{{ question.e }}: {{ question.e_answer }}
|
||||
<li class="question-e">
|
||||
<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>
|
||||
</ol>
|
||||
Category: {{ question.category }}, <a href="{% url 'physics:question_detail' pk=question.pk %}">View [id: {{question.pk}}]</a> <a
|
||||
@@ -39,6 +54,20 @@
|
||||
</ol>
|
||||
{% endautoescape %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% include 'exam_overview_js.html' %}
|
||||
{% 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