feat: implement enhanced submission list
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
d709f020f4
commit
ca3ca52ab2
|
@ -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"))
|
||||||
|
|
|
@ -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}
|
||||||
),
|
),
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
})
|
|
@ -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 %}
|
||||||
|
–
|
||||||
|
{% 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 %}
|
||||||
|
–
|
||||||
|
{% 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 %}
|
|
@ -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,
|
||||||
|
@ -26,6 +27,11 @@ urlpatterns = [
|
||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
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"),
|
path("<code>/", AssigneeView.as_view(), name="assignee"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue