Skip to content

Commit 0c1796e

Browse files
constantin-kuehneConstantin Kühnerichardebeling
authored
Sorting for participants in Issue #1975 (#2061)
* added sorting for participants (part of issue #1975) * Co-authored-by: @till2 * Make datagrid self-repair on broken localstorage sorting entry * Aggregated sorting for courses. --------- Co-authored-by: Constantin Kühne <[email protected]> Co-authored-by: Richard Ebeling <[email protected]>
1 parent c749b9c commit 0c1796e

9 files changed

+67
-41
lines changed

evap/evaluation/models.py

+6
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,12 @@ def num_voters(self):
813813
return self._voter_count
814814
return self.voters.count()
815815

816+
@property
817+
def voter_ratio(self):
818+
if self.is_single_result or self.num_participants == 0:
819+
return 0
820+
return self.num_voters / self.num_participants
821+
816822
@property
817823
def due_participants(self):
818824
return self.participants.exclude(pk__in=self.voters.all())

evap/results/templates/results_evaluation_detail.html

+3-5
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,17 @@ <h3>{{ evaluation.full_name }} ({{ evaluation.course.semester.name }})</h3>
127127
<div data-col="name">{% trans 'Evaluation' %}</div>
128128
<div data-col="semester">{% trans 'Semester' %}</div>
129129
<div data-col="responsible">{% trans 'Responsible' %}</div>
130-
<div data-col="participants">{% trans 'Voters' %}</div>
130+
<div data-col="voters">{% trans 'Voters' %}</div>
131131
<div data-col="result">{% trans 'Distribution and average grade' %}</div>
132132
</div>
133133
{% if course_evaluations %}
134-
{% include 'results_index_course.html' %}
134+
{% include 'results_index_course.html' with evaluations=course_evaluations %}
135135
{% for course_evaluation in course_evaluations|dictsort:"name" %}
136136
{% if course_evaluation.pk == evaluation.pk %}
137137
{# we cannot add a class to the actual row because it is cached globally. so we use a + selector in css to style the following row. #}
138138
<div class="current-row-follows"></div>
139139
{% endif %}
140-
{% with evaluation=course_evaluation %}
141-
{% include 'results_index_evaluation.html' with links_to_results_page=evaluation|can_results_page_be_seen_by:request.user is_subentry=True %}
142-
{% endwith %}
140+
{% include 'results_index_evaluation.html' with evaluation=course_evaluation links_to_results_page=course_evaluation|can_results_page_be_seen_by:request.user is_subentry=True %}
143141
{% endfor %}
144142
{% else %}
145143
{% include 'results_index_evaluation.html' with links_to_results_page=False is_subentry=False %}

evap/results/templates/results_index.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ <h5 class="card-title mb-lg-0">
7777
<div data-col="name" class="col-order">{% trans 'Evaluation' %}</div>
7878
<div data-col="semester" class="col-order">{% trans 'Semester' %}</div>
7979
<div data-col="responsible" class="col-order">{% trans 'Responsible' %}</div>
80-
<div data-col="participants">{% trans 'Voters' %}</div>
80+
<div data-col="voters" class="col-order">{% trans 'Voters' %}</div>
8181
<div data-col="result" class="col-order">{% trans 'Distribution and average grade' %}</div>
8282
</div>
8383
<div class="d-flex d-lg-none">
@@ -96,6 +96,7 @@ <h5 class="card-title mb-lg-0">
9696
<option value="name">{% trans 'Evaluation' %}</option>
9797
<option value="semester">{% trans 'Semester' %}</option>
9898
<option value="responsible">{% trans 'Responsible' %}</option>
99+
<option value="voters">{% trans 'Voters' %}</option>
99100
<option value="result">{% trans 'Distribution and average grade' %}</option>
100101
</select>
101102
</div>

evap/results/templates/results_index_course_impl.html

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<div data-col="responsible" data-order="{{ course.responsibles.first.last_name|lower }}">
1818
{{ course.responsibles_names }}
1919
</div>
20+
<div data-col="voters" data-order="{{ evaluations|aggregated_voters_order }}"></div>
2021
<div data-col="result" data-order="{{ course.avg_grade|default:7 }}">
2122
{% if course.not_all_evaluations_are_published %}
2223
<div class="d-flex" data-bs-toggle="tooltip" data-bs-placement="left" title="{% trans 'Course result is not yet available.' %}">

evap/results/templates/results_index_evaluation_impl.html

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{% load evaluation_filters %}
22
{% load results_templatetags %}
3+
{% load l10n %}
34

45
<{% if links_to_results_page %}a href="{% url 'results:evaluation_detail' evaluation.course.semester.id evaluation.id %}"{% else %}div{% endif %}
56
class="results-grid-row {% if not is_subentry %}heading-row{% else %}evaluation-row{% endif %}
@@ -49,14 +50,13 @@
4950
{{ evaluation.course.responsibles_names }}
5051
</div>
5152
{% endif %}
52-
{% if evaluation.is_single_result %}
53-
<div data-col="participants" class="text-center"><span class="fas fa-user"></span>&nbsp;{{ evaluation.single_result_rating_result.count_sum }}</div>
54-
{% else %}
55-
{% with num_participants=evaluation.num_participants num_voters=evaluation.num_voters %}
56-
<div data-col="participants">{% include 'progress_bar.html' with done=num_voters total=num_participants %}</div>
57-
{% endwith %}
58-
{% endif %}
59-
53+
<div data-col="voters" class="text-center" data-order="{{ evaluation|voters_order }}">
54+
{% if evaluation.is_single_result %}
55+
<span class="fas fa-user"></span>&nbsp;{{ evaluation.single_result_rating_result.count_sum }}
56+
{% else %}
57+
{% include 'progress_bar.html' with done=evaluation.num_voters total=evaluation.num_participants %}
58+
{% endif %}
59+
</div>
6060
<div data-col="result"{% if not is_subentry %} data-order="{% if evaluation.is_single_result %}{{ evaluation.single_result_rating_result.average }}{% else %}{{ evaluation.avg_grade|default:7 }}{% endif %}"{% endif %}>
6161
{% if not evaluation.can_staff_see_average_grade %}
6262
<div class="d-flex" data-bs-toggle="tooltip" data-bs-placement="left" title="{% trans 'Evaluation result is not yet available.' %}">

evap/results/templatetags/results_templatetags.py

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any, Iterable
2+
13
from django.template import Library
24

35
from evap.results.tools import (
@@ -40,3 +42,14 @@ def has_answers(rating_result: RatingResult):
4042
@register.filter
4143
def is_published(rating_result: RatingResult):
4244
return RatingResult.is_published(rating_result)
45+
46+
47+
@register.filter
48+
def voters_order(evaluation) -> str:
49+
"""float to string conversion done in python to circumvent localization breaking number parsing"""
50+
return str(evaluation.voter_ratio)
51+
52+
53+
@register.filter
54+
def aggregated_voters_order(evaluations: Iterable[Any]) -> str:
55+
return str(max(evaluation.voter_ratio for evaluation in evaluations))

evap/results/views.py

+21-20
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ def _delete_course_template_cache_impl(course):
6161
def update_template_cache(evaluations):
6262
assert all(evaluation.state in STATES_WITH_RESULT_TEMPLATE_CACHING for evaluation in evaluations)
6363
evaluations = get_evaluations_with_course_result_attributes(get_evaluations_with_prefetched_data(evaluations))
64-
courses_to_render = {evaluation.course for evaluation in evaluations if evaluation.course.evaluation_count > 1}
64+
65+
courses_and_evaluations = unordered_groupby((evaluation.course, evaluation) for evaluation in evaluations)
6566

6667
current_language = translation.get_language()
6768

@@ -72,25 +73,25 @@ def update_template_cache(evaluations):
7273
for lang in ["en", "de"]:
7374
translation.activate(lang)
7475

75-
for course in courses_to_render:
76-
caches["results"].set(
77-
get_course_result_template_fragment_cache_key(course.id, lang),
78-
results_index_course_template.render({"course": course}),
79-
)
80-
81-
for evaluation in evaluations:
82-
assert evaluation.state in STATES_WITH_RESULT_TEMPLATE_CACHING
83-
is_subentry = evaluation.course.evaluation_count > 1
84-
base_args = {"evaluation": evaluation, "is_subentry": is_subentry}
85-
86-
caches["results"].set(
87-
get_evaluation_result_template_fragment_cache_key(evaluation.id, lang, True),
88-
results_index_evaluation_template.render({**base_args, "links_to_results_page": True}),
89-
)
90-
caches["results"].set(
91-
get_evaluation_result_template_fragment_cache_key(evaluation.id, lang, False),
92-
results_index_evaluation_template.render({**base_args, "links_to_results_page": False}),
93-
)
76+
for course, course_evaluations in courses_and_evaluations.items():
77+
if len(course_evaluations) > 1:
78+
caches["results"].set(
79+
get_course_result_template_fragment_cache_key(course.id, lang),
80+
results_index_course_template.render({"course": course, "evaluations": course_evaluations}),
81+
)
82+
83+
for evaluation in course_evaluations:
84+
assert evaluation.state in STATES_WITH_RESULT_TEMPLATE_CACHING
85+
base_args = {"evaluation": evaluation, "is_subentry": len(course_evaluations) > 1}
86+
87+
caches["results"].set(
88+
get_evaluation_result_template_fragment_cache_key(evaluation.id, lang, True),
89+
results_index_evaluation_template.render({**base_args, "links_to_results_page": True}),
90+
)
91+
caches["results"].set(
92+
get_evaluation_result_template_fragment_cache_key(evaluation.id, lang, False),
93+
results_index_evaluation_template.render({**base_args, "links_to_results_page": False}),
94+
)
9495

9596
finally:
9697
translation.activate(current_language) # reset to previously set language to prevent unwanted side effects

evap/static/scss/components/_grid.scss

+6-6
Original file line numberDiff line numberDiff line change
@@ -90,29 +90,29 @@
9090
@extend %table-grid;
9191

9292
grid:
93-
"name semester responsible participants result"
94-
/ auto 6rem 12rem 7rem 11rem;
93+
"name semester responsible voters result"
94+
/ auto 6rem 12rem 7rem 11rem;
9595

9696
@include media-breakpoint-down(lg) {
9797
grid:
9898
"name semester responsible responsible"
99-
"name semester participants result"
99+
"name semester voters result"
100100
/ auto 3rem 7rem 10rem;
101101
}
102102

103103
@include media-breakpoint-down(md) {
104104
grid:
105105
" name name semester"
106106
" responsible responsible responsible"
107-
"participants result result"
107+
" voters result result"
108108
/ 1fr 1fr 6rem;
109109
}
110110

111111
&:not(.grid-header) [data-col=responsible] {
112112
font-style: italic;
113113
}
114114

115-
[data-col=participants] {
115+
[data-col=voters] {
116116
max-width: 8rem;
117117
}
118118

@@ -164,7 +164,7 @@
164164
}
165165
}
166166

167-
@each $col in name, semester, responsible, participants, result, answer, edit, review, flag {
167+
@each $col in name, semester, responsible, voters, result, answer, edit, review, flag {
168168
[data-col=#{$col}] {
169169
grid-area: $col;
170170
}

evap/static/ts/src/datagrid.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,13 @@ abstract class DataGrid {
158158
header.classList.remove("col-order-asc", "col-order-desc");
159159
}
160160
for (const [column, ordering] of this.state.order) {
161-
this.sortableHeaders.get(column)!.classList.add(`col-order-${ordering}`);
161+
const header = this.sortableHeaders.get(column);
162+
if (header === undefined) {
163+
// Silently ignore non-existing columns: They were probably renamed.
164+
// A correct state will be built the next time the user sorts the datagrid.
165+
continue;
166+
}
167+
header.classList.add(`col-order-${ordering}`);
162168
}
163169

164170
this.rows.sort((a, b) => {

0 commit comments

Comments
 (0)