Compare commits

...

10 Commits

Author SHA1 Message Date
Luca d319559272 fix(computescores): unexpected keyword argument
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2025-02-23 17:57:25 +01:00
Luca 1b264054fc chore: bump version
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2025-02-23 17:46:19 +01:00
Luca c5b39e1890 feat(computescores): add option to include frozen jurors 2025-02-23 17:45:55 +01:00
Luca 97a1dbf33a chore: bump version
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2025-02-23 14:41:17 +01:00
Luca 27e0b1e2c5 feat: add flag to Juror to disable them 2025-02-23 14:40:52 +01:00
Luca ec3acac3fc fix: assignee assignment
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2025-01-30 18:47:07 +01:00
Luca d5e3a35056 chore: bump version
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2024-12-09 22:33:19 +01:00
Luca 9accf9714e feat: add score export 2024-12-09 22:32:50 +01:00
Luca 2d7efeaf09 chore: bump version
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2024-12-08 15:49:16 +01:00
Luca 50d189d8ed feat: 'fix' last submission when submission types change 2024-12-08 15:48:24 +01:00
9 changed files with 92 additions and 35 deletions

View File

@ -1 +1 @@
__version__ = "2025.2.0"
__version__ = "2025.6.1"

View File

@ -62,12 +62,9 @@ class AssigneeForm(forms.ModelForm):
except Assignee.DoesNotExist:
self.instance = Assignee(submission=submission)
super().__init__(*args, instance=self.instance, **kwargs)
self.fields["user"].queryset = User.objects.none().union(
*(
t.members.all()
for t in submission.event.teams.filter(can_change_submissions=True)
)
)
self.fields["user"].queryset = User.objects.filter(
teams__in=submission.event.teams, teams__can_change_submissions=True
).distinct()
class Meta:
model = Assignee

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: pretalx-musicrate 0.9.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-02 19:23+0100\n"
"PO-Revision-Date: 2024-02-14 22:30+0100\n"
"POT-Creation-Date: 2024-12-09 22:22+0100\n"
"PO-Revision-Date: 2024-12-09 22:23+0100\n"
"Last-Translator: Luca <Luca@hackerspace-bamberg.de>\n"
"Language-Team: Luca <Luca@hackerspace-bamberg.de>\n"
"Language: de_DE\n"
@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.1\n"
"X-Generator: Poedit 3.4.2\n"
#: pretalx_musicrate/apps.py:12 pretalx_musicrate/signals.py:59
msgid "pretalx-musicrate"
@ -194,31 +194,35 @@ msgstr "pretalx-musicrate-Einstellungen"
msgid "Export ratings"
msgstr "Bewertungen exportieren"
#: pretalx_musicrate/templates/pretalx_musicrate/submission_base.html:29
#: pretalx_musicrate/templates/pretalx_musicrate/settings.html:16
msgid "Export scores"
msgstr "Auswertung exportieren"
#: pretalx_musicrate/templates/pretalx_musicrate/submission_base.html:34
msgid "(not specified)"
msgstr "(nicht angegeben)"
#: pretalx_musicrate/templates/pretalx_musicrate/submission_base.html:42
#: pretalx_musicrate/templates/pretalx_musicrate/submission_base.html:47
msgid "Previous"
msgstr "Zurück"
#: pretalx_musicrate/templates/pretalx_musicrate/submission_base.html:50
#: pretalx_musicrate/templates/pretalx_musicrate/submission_base.html:55
msgid "Next"
msgstr "Weiter"
#: pretalx_musicrate/views.py:48
#: pretalx_musicrate/views.py:66
msgid "Invalid token"
msgstr "Ungültiges Token"
#: pretalx_musicrate/views.py:123
#: pretalx_musicrate/views.py:133
msgid "The pretalx-musicrate settings were updated."
msgstr "Die pretalx-musicrate-Einstellungen wurden gespeichert."
#: pretalx_musicrate/views.py:282 pretalx_musicrate/views.py:469
#: pretalx_musicrate/views.py:289 pretalx_musicrate/views.py:482
msgid "Saved!"
msgstr "Gespeichert!"
#: pretalx_musicrate/views.py:386
#: pretalx_musicrate/views.py:398
#, python-format
msgid "%(num_ratings)d of %(num_jurors)d has rated this submission"
msgid_plural "%(num_ratings)d of %(num_jurors)d have rated this submission"

View File

@ -20,6 +20,9 @@ class Command(BaseCommand):
help = "Compute submission scores from ratings"
def add_arguments(self, parser):
parser.add_argument(
"-a", "--all", action="store_true", help="include frozen jurors"
)
parser.add_argument("event")
def handle(self, *args, **kwargs):
@ -31,9 +34,15 @@ class Command(BaseCommand):
with scope(event=event):
submissions = {}
for juror in event.jurors.prefetch_related("ratings__submission").order_by(
jurors = event.jurors.prefetch_related("ratings__submission").order_by(
"token"
):
)
if not kwargs["all"]:
jurors = jurors.filter(frozen=False)
self.stderr.write(f"computing scores from {jurors.count()} jurors")
for juror in jurors:
ratings = list(
juror.ratings.exclude(rating="").order_by("submission__created")
)

View File

@ -0,0 +1,18 @@
# Generated by Django 5.1.5 on 2025-02-23 13:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretalx_musicrate", "0009_score"),
]
operations = [
migrations.AddField(
model_name="juror",
name="frozen",
field=models.BooleanField(default=False),
),
]

View File

@ -78,6 +78,7 @@ class Juror(models.Model):
related_name="jurors",
null=True,
)
frozen = models.BooleanField(default=False)
class Rating(models.Model):

View File

@ -11,4 +11,8 @@
<i class="fa fa-download"></i>
{% translate "Export ratings" %}
</a>
<a class="btn btn-success btn-lg btn-block" href="{% url "plugins:pretalx_musicrate:scores" event=request.event.slug %}">
<i class="fa fa-download"></i>
{% translate "Export scores" %}
</a>
{% endblock %}

View File

@ -11,6 +11,7 @@ from .views import (
PresenterView,
QRCodeView,
RatingView,
ScoreExportView,
)
urlpatterns = [
@ -33,6 +34,7 @@ urlpatterns = [
EnhancedSubmissionList.as_view(),
name="enhanced_list",
),
path("scores/", ScoreExportView.as_view(), name="scores"),
path("<code>/", AssigneeView.as_view(), name="assignee"),
]
),

View File

@ -4,7 +4,7 @@ from hmac import compare_digest
from urllib.parse import parse_qs, urlparse
from django.contrib import messages
from django.db.models import Case, FilteredRelation, Q, Value, When
from django.db.models import Case, F, FilteredRelation, Q, Value, When
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
@ -39,15 +39,14 @@ def get_last_submission(settings, submissions, submission=None):
if submission is not None and submission.state != SubmissionStates.SUBMITTED:
submission = None
return (
submission
or submissions.filter(
submission_type__in=settings.submission_types.all(),
state=SubmissionStates.SUBMITTED,
)
.order_by("created")
.first()
submissions = submissions.filter(
submission_type__in=settings.submission_types.all(),
state=SubmissionStates.SUBMITTED,
)
if submission is not None and not submissions.filter(pk=submission.pk).exists():
submission = None
return submission or submissions.order_by("created").first()
class JoinView(TemplateView):
@ -55,7 +54,7 @@ class JoinView(TemplateView):
def validate_token(self, token):
try:
self.juror = Juror.objects.get(token=token)
self.juror = Juror.objects.get(token=token, frozen=False)
return True
except Juror.DoesNotExist:
self.juror = None
@ -257,7 +256,7 @@ class RatingView(FormView, SubmissionMixin):
@cached_property
def juror(self):
return get_object_or_404(
Juror, token=self.request.resolver_match.kwargs["token"]
Juror, token=self.request.resolver_match.kwargs["token"], frozen=False
)
@context
@ -385,8 +384,8 @@ class MayAdvanceView(EventPermissionRequired, SubmissionMixin, View):
permission_required = "orga.view_submissions"
def get(self, request, *args, **kwargs):
num_ratings = self.submission.ratings.count()
num_jurors = self.request.event.jurors.count()
num_ratings = self.submission.ratings.filter(juror__frozen=False).count()
num_jurors = self.request.event.jurors.filter(frozen=False).count()
return JsonResponse(
{
"mayAdvance": num_ratings
@ -418,13 +417,12 @@ class ExportView(EventPermissionRequired, View):
writer = csv.writer(response)
genre_question = request.event.pretalx_musicrate_settings.genre_question
origin_question = request.event.pretalx_musicrate_settings.origin_question
jurors = request.event.jurors.order_by("token")
jurors = request.event.jurors.filter(frozen=False).order_by("token")
for submission in (
request.event.submissions.prefetch_related("answers")
.select_related("submission_type")
.filter(
submission_type__in=request.event.pretalx_musicrate_settings.submission_types.all(),
state=SubmissionStates.SUBMITTED,
submission_type__in=request.event.pretalx_musicrate_settings.submission_types.all()
)
.only("title", "submission_type__name")
.order_by("created")
@ -515,3 +513,27 @@ class EnhancedSubmissionList(SubmissionList):
if not self.filter_form.is_valid():
return qs
return self.filter_form.filter_queryset(qs)
class ScoreExportView(EventPermissionRequired, View):
permission_required = "orga.view_submissions"
def get(self, request, *args, **kwargs):
response = HttpResponse(
content_type="text/csv",
headers={
"Content-Disposition": f'attachment; filename="{request.event.slug}.csv"'
},
)
csv.writer(response).writerows(
request.event.submissions.select_related("score", "submission_type")
.filter(
submission_type__in=request.event.pretalx_musicrate_settings.submission_types.all()
)
.exclude(state__in=(SubmissionStates.CANCELED, SubmissionStates.WITHDRAWN))
.order_by(F("score__value").desc(nulls_last=True))
.values_list("score__value", "title")
)
return response