Compare commits

...

2 Commits

Author SHA1 Message Date
Luca ca3ca52ab2 feat: implement enhanced submission list
continuous-integration/drone/push Build is passing Details
2024-02-14 00:54:28 +01:00
Luca d709f020f4 fix: urlconf order 2024-02-14 00:49:19 +01:00
6 changed files with 217 additions and 4 deletions

View File

@ -1,7 +1,9 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _
from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceField from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceField
from i18nfield.forms import I18nModelForm from i18nfield.forms import I18nModelForm
from pretalx.person.models import User from pretalx.person.models import User
from pretalx.submission.forms import SubmissionFilterForm
from .models import Assignee, MusicrateSettings, Rating from .models import Assignee, MusicrateSettings, Rating
@ -77,3 +79,7 @@ class AssigneeForm(forms.ModelForm):
model = Assignee model = Assignee
fields = ("user",) fields = ("user",)
widgets = {"user": forms.Select(attrs={"class": "select2"})} widgets = {"user": forms.Select(attrs={"class": "select2"})}
class EnhancedSubmissionFilterForm(SubmissionFilterForm):
require_all_tags = forms.BooleanField(required=False, label=_("require all"))

View File

@ -24,10 +24,20 @@ def pretalx_musicrate_placeholders(sender, **kwargs):
@receiver(nav_event) @receiver(nav_event)
def pretalx_musicrate_qrcode(sender, request, **kwargs): def pretalx_musicrate_nav_event(sender, request, **kwargs):
if not request.user.has_perm("orga.view_submissions", request.event): if not request.user.has_perm("orga.view_submissions", request.event):
return [] return []
return [ return [
{
"active": request.resolver_match.view_name
== "plugins:pretalx_musicrate:enhanced_list",
"icon": "sticky-note-o",
"label": _("Proposals, but better"),
"url": reverse(
"plugins:pretalx_musicrate:enhanced_list",
kwargs={"event": request.event.slug},
),
},
{ {
"active": request.resolver_match.view_name "active": request.resolver_match.view_name
== "plugins:pretalx_musicrate:qrcode", == "plugins:pretalx_musicrate:qrcode",
@ -36,7 +46,7 @@ def pretalx_musicrate_qrcode(sender, request, **kwargs):
"url": reverse( "url": reverse(
"plugins:pretalx_musicrate:qrcode", kwargs={"event": request.event.slug} "plugins:pretalx_musicrate:qrcode", kwargs={"event": request.event.slug}
), ),
} },
] ]

View File

@ -0,0 +1,11 @@
document.addEventListener("DOMContentLoaded", function() {
const updateRequireAllVisibility = () => {
if (document.querySelector("#id_tags").value) {
document.querySelector("#requireAll").classList.remove("d-none")
} else {
document.querySelector("#requireAll").classList.add("d-none")
}
}
$("#id_tags").on("change", updateRequireAllVisibility)
updateRequireAllVisibility()
})

View File

@ -0,0 +1,154 @@
{% extends "orga/base.html" %}
{% load bootstrap4 %}
{% load compress %}
{% load i18n %}
{% load rules %}
{% load static %}
{% load url_replace %}
{% block scripts %}
{% compress js %}
<script src="{% static "orga/js/submission_filter.js" %}"></script>
{% endcompress %}
{% compress js %}
<script src="{% static "pretalx_musicrate/submission_filter.js" %}"></script>
{% endcompress %}
{% endblock %}
{% block content %}
{% has_perm 'orga.change_submission_state' request.user request.event as can_change_submission %}
{% has_perm 'orga.view_speakers' request.user request.event as can_view_speakers %}
<h2>
{{ page_obj.paginator.count }}
{% blocktranslate trimmed count count=page_obj.paginator.count %}
proposal
{% plural %}
proposals
{% endblocktranslate %}
</h2>
<div class="submit-group search-submit-group">
<form class="search-form">
{% bootstrap_form search_form %}
{% if show_submission_types and filter_form.submission_type %}{% bootstrap_field filter_form.submission_type %}{% endif %}
<div class="d-flex flex-column form-group">
{% bootstrap_field filter_form.state layout='inline' %}
<div id="pending" class="d-none">{% bootstrap_field filter_form.pending_state__isnull layout='inline' %}</div>
</div>
{% if filter_form.track %}{% bootstrap_field filter_form.track %}{% endif %}
{% if filter_form.tags %}
<div class="d-flex flex-column form-group">
{% bootstrap_field filter_form.tags layout='inline' %}
<div id="requireAll" class="d-none">{% bootstrap_field filter_form.require_all_tags layout='inline' %}</div>
</div>
{% endif %}
{% if filter_form.content_locale %}{% bootstrap_field filter_form.content_locale %}{% endif %}
<button class="btn btn-success" type="submit">{% translate "Search" %}</button>
</form>
{% if filter_form.is_valid and filter_form.cleaned_data.question %}
<p class="text-muted ml-2">
<span class="fa fa-filter"></span>
{% blocktranslate trimmed with question=filter_form.cleaned_data.question.question %}
List filtered by answers to question "{{ question }}".
{% endblocktranslate %}
<a href="?{% url_replace request 'question' '' 'answer' '' 'answer__options' '' %}" class="text-muted">
<span class="fa fa-times"></span>
{% translate "Remove filter" %}
</a>
</p>
{% endif %}
</div>
<div class="table-responsive">
<table class="table table-sm table-hover table-flip">
<thead>
<tr>
<th>
{% translate "Rating" %}
<a href="?{% url_replace request 'sort' 'rating' %}"><i class="fa fa-caret-down" title="{% translate "Sort by rating (10-0)" %}"></i></a>
<a href="?{% url_replace request 'sort' '-rating' %}"><i class="fa fa-caret-up" title="{% translate "Sort by rating (0-10)" %}"></i></a>
</th>
<th>
{% translate "Title" %}
<a href="?{% url_replace request 'sort' 'title' %}"><i class="fa fa-caret-down" title="{% translate "Sort by title (a-z)" %}"></i></a>
<a href="?{% url_replace request 'sort' '-title' %}"><i class="fa fa-caret-up" title="{% translate "Sort by title (z-a)" %}"></i></a>
</th>
{% if show_submission_types %}
<th>
{% translate "Type" %}
</th>
{% endif %}
<th>
{% translate "State" %}
<a href="?{% url_replace request 'sort' 'state' %}"><i class="fa fa-caret-down" title="{% translate "Sort by state (a-z)" %}"></i></a>
<a href="?{% url_replace request 'sort' '-state' %}"><i class="fa fa-caret-up" title="{% translate "Sort by state (z-a)" %}"></i></a>
</th>
<th>
{% translate "Assignee" %}
<a href="?{% url_replace request 'sort' 'assignee' %}"><i class="fa fa-caret-down" title="{% translate "Sort by assignee (a-z)" %}"></i></a>
<a href="?{% url_replace request 'sort' '-assignee' %}"><i class="fa fa-caret-up" title="{% translate "Sort by assignee (z-a)" %}"></i></a>
</th>
{% if can_change_submission %}
<th></th>
{% endif %}
</tr>
</thead>
<tbody>
{% for submission in submissions %}
<tr>
<td>
{% if submission.rating %}
{{ submission.rating.value }}
{% else %}
&ndash;
{% endif %}
</td>
<td>
<a href="{{ submission.orga_urls.base }}">
{% if can_view_speakers %}{{ submission.title }}{% else %}{{ submission.anonymised.title|default:submission.title }}{% endif %}
</a>
</td>
{% if show_submission_types %}
<td>
{{ submission.submission_type.name }}
</td>
{% endif %}
<td>
{% include "orga/submission/state_dropdown.html" with submission=submission %}
</td>
<td>
{% if submission.assignee %}
{{ submission.assignee.user.name }}
{% else %}
&ndash;
{% endif %}
{% if can_change_submission %}
<a class="btn btn-sm btn-link"
href="{% url "plugins:pretalx_musicrate:assignee" event=request.event.slug code=submission.code %}"
title="{% translate "edit" %}">
<i class="fa fa-edit"></i>
</a>
{% endif %}
</td>
{% if can_change_submission %}
<td class="action-column">
<a href="{{ submission.orga_urls.edit }}"
title="{% translate "edit" %}"
class="btn btn-sm btn-info">
<i class="fa fa-edit"></i>
</a>
<a href="{{ submission.orga_urls.delete }}?from=list"
title="{% translate "Delete" %}"
class="btn btn-sm btn-danger">
<i class="fa fa-trash"></i>
</a>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "orga/pagination.html" %}
{% endblock %}

View File

@ -2,6 +2,7 @@ from django.urls import include, path
from .views import ( from .views import (
AssigneeView, AssigneeView,
EnhancedSubmissionList,
ExportView, ExportView,
JoinView, JoinView,
MayAdvanceView, MayAdvanceView,
@ -25,8 +26,13 @@ urlpatterns = [
"p/pretalx_musicrate/", "p/pretalx_musicrate/",
include( include(
[ [
path("<code>/", AssigneeView.as_view(), name="assignee"),
path("export/", ExportView.as_view(), name="export"), path("export/", ExportView.as_view(), name="export"),
path(
"list/",
EnhancedSubmissionList.as_view(),
name="enhanced_list",
),
path("<code>/", AssigneeView.as_view(), name="assignee"),
] ]
), ),
), ),

View File

@ -15,9 +15,15 @@ from django.views.generic import FormView, TemplateView, View
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from django_context_decorator import context from django_context_decorator import context
from pretalx.common.mixins.views import EventPermissionRequired from pretalx.common.mixins.views import EventPermissionRequired
from pretalx.orga.views.submission import SubmissionList
from pretalx.submission.models import Submission from pretalx.submission.models import Submission
from .forms import AssigneeForm, MusicrateSettingsForm, RatingForm from .forms import (
AssigneeForm,
EnhancedSubmissionFilterForm,
MusicrateSettingsForm,
RatingForm,
)
from .models import Juror, Rating from .models import Juror, Rating
youtube_re = re.compile( youtube_re = re.compile(
@ -470,3 +476,23 @@ class AssigneeView(EventPermissionRequired, FormView, SingleObjectMixin):
def post(self, *args, **kwargs): def post(self, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
return super().post(*args, **kwargs) return super().post(*args, **kwargs)
class EnhancedSubmissionList(SubmissionList):
sortable_fields = ("code", "rating", "title", "state", "assignee")
template_name = "pretalx_musicrate/enhanced_list.html"
def get_filter_form(self):
return EnhancedSubmissionFilterForm(
data=self.request.GET,
event=self.request.event,
usable_states=self.usable_states,
limit_tracks=self.limit_tracks,
)
def filter_queryset(self, qs):
qs = super().filter_queryset(qs.prefetch_related("assignee"))
if self.request.GET.get("require_all_tags", "") == "on":
for tag in self.request.GET.getlist("tags"):
qs = qs.filter(tags__in=[tag])
return qs