Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • komasolver
  • main
  • renovate/django-5.x
  • renovate/django-debug-toolbar-5.x
  • renovate/django_csp-4.x
  • renovate/djangorestframework-3.x
  • renovate/tzdata-2025.x
  • renovate/uwsgi-2.x
8 results

Target

Select target project
  • konstantin/akplanning
  • matedealer/akplanning
  • kif/akplanning
  • mirco/akplanning
  • lordofthevoid/akplanning
  • voidptr/akplanning
  • xayomer/akplanning-fork
  • mollux/akplanning
  • neumantm/akplanning
  • mmarx/akplanning
  • nerf/akplanning
  • felix_bonn/akplanning
  • sebastian.uschmann/akplanning
13 results
Select Git revision
  • ak-import
  • feature/clear-schedule-button
  • feature/json-export-via-rest-framework
  • feature/json-schedule-import-tests
  • feature/preference-polling
  • feature/preference-polling-form
  • feature/preference-polling-form-rebased
  • feature/preference-polling-rebased
  • fix/add-room-import-only-once
  • main
  • merge-to-upstream
  • renovate/django-5.x
  • renovate/django-debug-toolbar-4.x
  • renovate/django-simple-history-3.x
  • renovate/mysqlclient-2.x
15 results
Show changes
Showing
with 353 additions and 158 deletions
...@@ -76,10 +76,15 @@ class EventRoomsWidget(TemplateStatusWidget): ...@@ -76,10 +76,15 @@ class EventRoomsWidget(TemplateStatusWidget):
def render_actions(self, context: {}) -> list[dict]: def render_actions(self, context: {}) -> list[dict]:
actions = super().render_actions(context) actions = super().render_actions(context)
# Action has to be added here since it depends on the event for URL building # Action has to be added here since it depends on the event for URL building
import_room_url = reverse_lazy("admin:room-import", kwargs={"event_slug": context["event"].slug})
for action in actions:
if action["url"] == import_room_url:
return actions
actions.append( actions.append(
{ {
"text": _("Import Rooms from CSV"), "text": _("Import Rooms from CSV"),
"url": reverse_lazy("admin:room-import", kwargs={"event_slug": context["event"].slug}), "url": import_room_url,
} }
) )
return actions return actions
......
from datetime import timedelta from datetime import datetime, timedelta
from django.conf import settings from django.conf import settings
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.datetime_safe import datetime from django.views.generic import DetailView, ListView
from django.views.generic import ListView, DetailView
from AKModel.models import AKSlot, Room, AKTrack
from AKModel.metaviews.admin import FilterByEventSlugMixin from AKModel.metaviews.admin import FilterByEventSlugMixin
from AKModel.models import AKSlot, AKTrack, Room
class PlanIndexView(FilterByEventSlugMixin, ListView): class PlanIndexView(FilterByEventSlugMixin, ListView):
...@@ -152,7 +151,7 @@ class PlanTrackView(FilterByEventSlugMixin, DetailView): ...@@ -152,7 +151,7 @@ class PlanTrackView(FilterByEventSlugMixin, DetailView):
context = super().get_context_data(object_list=object_list, **kwargs) context = super().get_context_data(object_list=object_list, **kwargs)
# Restrict AKSlot list to given track # Restrict AKSlot list to given track
# while joining AK, room and category information to reduce the amount of necessary SQL queries # while joining AK, room and category information to reduce the amount of necessary SQL queries
context["slots"] = AKSlot.objects.\ context["slots"] = AKSlot.objects. \
filter(event=self.event, ak__track=context['track']).\ filter(event=self.event, ak__track=context['track']). \
select_related('ak', 'room', 'ak__category') select_related('ak', 'room', 'ak__category')
return context return context
...@@ -217,6 +217,9 @@ PLAN_MAX_HIGHLIGHT_UPDATE_SECONDS = 2 * 60 * 60 ...@@ -217,6 +217,9 @@ PLAN_MAX_HIGHLIGHT_UPDATE_SECONDS = 2 * 60 * 60
DASHBOARD_SHOW_RECENT = True DASHBOARD_SHOW_RECENT = True
# How many entries max? # How many entries max?
DASHBOARD_RECENT_MAX = 25 DASHBOARD_RECENT_MAX = 25
# How many events should be featured in the dashboard
# (active events will always be featured, even if their number is higher than this threshold)
DASHBOARD_MAX_FEATURED_EVENTS = 3
# Registration/login behavior # Registration/login behavior
SIMPLE_BACKEND_REDIRECT_URL = "/user/" SIMPLE_BACKEND_REDIRECT_URL = "/user/"
...@@ -224,7 +227,7 @@ LOGIN_REDIRECT_URL = SIMPLE_BACKEND_REDIRECT_URL ...@@ -224,7 +227,7 @@ LOGIN_REDIRECT_URL = SIMPLE_BACKEND_REDIRECT_URL
# Content Security Policy # Content Security Policy
CSP_DEFAULT_SRC = ("'self'",) CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'", "'unsafe-inline'") CSP_SCRIPT_SRC = ("'self'", "'unsafe-inline'", "'unsafe-eval'")
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'", "fonts.googleapis.com") CSP_STYLE_SRC = ("'self'", "'unsafe-inline'", "fonts.googleapis.com")
CSP_IMG_SRC = ("'self'", "data:") CSP_IMG_SRC = ("'self'", "data:")
CSP_FRAME_SRC = ("'self'", ) CSP_FRAME_SRC = ("'self'", )
......
...@@ -24,7 +24,8 @@ class AKAddSlotForm(forms.Form): ...@@ -24,7 +24,8 @@ class AKAddSlotForm(forms.Form):
start = forms.CharField(label=_("Start"), disabled=True) start = forms.CharField(label=_("Start"), disabled=True)
end = forms.CharField(label=_("End"), disabled=True) end = forms.CharField(label=_("End"), disabled=True)
duration = forms.CharField(label=_("Duration"), disabled=True) duration = forms.CharField(label=_("Duration"), disabled=True)
room = forms.IntegerField(label=_("Room"), disabled=True) room = forms.IntegerField(label=_("Room"), disabled=True, widget=forms.HiddenInput())
room_name = forms.CharField(label=_("Room"), disabled=True)
def __init__(self, event): def __init__(self, event):
super().__init__() super().__init__()
......
...@@ -157,6 +157,7 @@ ...@@ -157,6 +157,7 @@
$('#id_start').val(info.startStr); $('#id_start').val(info.startStr);
$('#id_end').val(info.endStr); $('#id_end').val(info.endStr);
$('#id_room').val(info.resource._resource.id); $('#id_room').val(info.resource._resource.id);
$('#id_room_name').val(info.resource._resource.title);
$('#id_duration').val(Math.abs(info.end-info.start)/1000/60/60); $('#id_duration').val(Math.abs(info.end-info.start)/1000/60/60);
$('#id_ak').val(""); $('#id_ak').val("");
$('#newAKSlotModal').modal('show'); $('#newAKSlotModal').modal('show');
...@@ -354,9 +355,11 @@ ...@@ -354,9 +355,11 @@
<h5 class="mt-2">{{ track_slots.grouper }}</h5> <h5 class="mt-2">{{ track_slots.grouper }}</h5>
{% endif %} {% endif %}
{% for slot in track_slots.list %} {% for slot in track_slots.list %}
<div class="unscheduled-slot badge" style='background-color: {{ slot.ak.category.color }}' <div class="unscheduled-slot badge" style='background-color: {% with slot.ak.category.color as color %} {% if color %}{{ color }}{% else %}#000000;{% endif %}{% endwith %}'
data-event='{ "title": "{{ slot.ak.short_name }}", "duration": {"hours": "{{ slot.duration|unlocalize }}"}, "constraint": "roomAvailable", "description": "{{ slot.ak.details | escapejs }}", "slotID": "{{ slot.pk }}", "backgroundColor": "{{ slot.ak.category.color }}", "url": "{% url "admin:AKModel_akslot_change" slot.pk %}"}' data-details="{{ slot.ak.details }}">{{ slot.ak.short_name }} {% with slot.ak.details as details %}
data-event='{ "title": "{{ slot.ak.short_name }}", "duration": {"hours": "{{ slot.duration|unlocalize }}"}, "constraint": "roomAvailable", "description": "{{ details | escapejs }}", "slotID": "{{ slot.pk }}", "backgroundColor": "{{ slot.ak.category.color }}", "url": "{% url "admin:AKModel_akslot_change" slot.pk %}"}' data-details="{{ details }}">{{ slot.ak.short_name }}
({{ slot.duration }} h)<br>{{ slot.ak.owners_list }} ({{ slot.duration }} h)<br>{{ slot.ak.owners_list }}
{% endwith %}
</div> </div>
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
......
...@@ -13,14 +13,19 @@ ...@@ -13,14 +13,19 @@
{% block content %} {% block content %}
<h4 class="mt-4 mb-4">{% trans "AKs with public notes" %}</h4> <h4 class="mt-4 mb-4">{% trans "AKs with public notes" %}</h4>
{% for ak in aks_with_comment %} {% for ak in aks_with_comment %}
<a href="{{ ak.edit_url }}">{{ ak }}</a><br>{{ ak.notes }}<br><br> <a href="{{ ak.detail_url }}">{{ ak }}</a>
<a href="{{ ak.edit_url }}">{% fa6_icon "pen-to-square" %}</a>
<a class="link-warning" href="{% url "admin:AKModel_ak_change" object_id=ak.pk %}">{% fa6_icon "pen-to-square" %}</a><br>
{{ ak.notes }}<br><br>
{% empty %} {% empty %}
- -
{% endfor %} {% endfor %}
<h4 class="mt-4 mb-4">{% trans "AKs without availabilities" %}</h4> <h4 class="mt-4 mb-4">{% trans "AKs without availabilities" %}</h4>
{% for ak in aks_without_availabilities %} {% for ak in aks_without_availabilities %}
<a href="{{ ak.edit_url }}">{{ ak }}</a><br> <a href="{{ ak.detail_url }}">{{ ak }}</a>
<a href="{{ ak.edit_url }}">{% fa6_icon "pen-to-square" %}</a>
<a class="link-warning" href="{% url "admin:AKModel_ak_change" object_id=ak.pk %}">{% fa6_icon "pen-to-square" %}</a><br>
{% empty %} {% empty %}
-<br> -<br>
{% endfor %} {% endfor %}
...@@ -30,7 +35,10 @@ ...@@ -30,7 +35,10 @@
<h4 class="mt-4 mb-4">{% trans "AK wishes with slots" %}</h4> <h4 class="mt-4 mb-4">{% trans "AK wishes with slots" %}</h4>
{% for ak in ak_wishes_with_slots %} {% for ak in ak_wishes_with_slots %}
<a href="{{ ak.detail_url }}">{{ ak }}</a> <a href="{% url "admin:AKModel_akslot_changelist" %}?ak={{ ak.pk }}">({{ ak.akslot__count }})</a><br> <a href="{% url "admin:AKModel_akslot_changelist" %}?ak={{ ak.pk }}">({{ ak.akslot__count }})</a>
<a href="{{ ak.detail_url }}">{{ ak }}</a>
<a href="{{ ak.edit_url }}">{% fa6_icon "pen-to-square" %}</a>
<a class="link-warning" href="{% url "admin:AKModel_ak_change" object_id=ak.pk %}">{% fa6_icon "pen-to-square" %}</a><br>
{% empty %} {% empty %}
-<br> -<br>
{% endfor %} {% endfor %}
...@@ -39,7 +47,9 @@ ...@@ -39,7 +47,9 @@
<h4 class="mt-4 mb-4">{% trans "AKs without slots" %}</h4> <h4 class="mt-4 mb-4">{% trans "AKs without slots" %}</h4>
{% for ak in aks_without_slots %} {% for ak in aks_without_slots %}
<a href="{{ ak.detail_url }}">{{ ak }}</a><br> <a href="{{ ak.detail_url }}">{{ ak }}</a>
<a href="{{ ak.edit_url }}">{% fa6_icon "pen-to-square" %}</a>
<a class="link-warning" href="{% url "admin:AKModel_ak_change" object_id=ak.pk %}">{% fa6_icon "pen-to-square" %}</a><br>
{% empty %} {% empty %}
-<br> -<br>
{% endfor %} {% endfor %}
......
...@@ -41,7 +41,9 @@ class SchedulingAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView): ...@@ -41,7 +41,9 @@ class SchedulingAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView):
context_object_name = "slots_unscheduled" context_object_name = "slots_unscheduled"
def get_queryset(self): def get_queryset(self):
return super().get_queryset().filter(start__isnull=True).select_related('event', 'ak').order_by('ak__track') return super().get_queryset().filter(start__isnull=True).select_related('event', 'ak', 'ak__track',
'ak__category').prefetch_related('ak__types', 'ak__owners', 'ak__conflicts', 'ak__prerequisites',
'ak__requirements', 'ak__conflict').order_by('ak__track', 'ak')
def get_context_data(self, *, object_list=None, **kwargs): def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(object_list=object_list, **kwargs) context = super().get_context_data(object_list=object_list, **kwargs)
...@@ -152,6 +154,13 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi ...@@ -152,6 +154,13 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi
def get_success_url(self): def get_success_url(self):
return self.request.path return self.request.path
def form_valid(self, form):
# Don't create a history entry for this change
form.instance.skip_history_when_saving = True
r = super().form_valid(form)
del form.instance.skip_history_when_saving
return r
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["title"] = f"{_('Enter interest')}" context["title"] = f"{_('Enter interest')}"
......
from datetime import datetime
from rest_framework import status from rest_framework import status
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from rest_framework.response import Response from rest_framework.response import Response
from django.utils.datetime_safe import datetime
from AKModel.models import AK from AKModel.models import AK
......
...@@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _ ...@@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _
from AKModel.availability.forms import AvailabilitiesFormMixin from AKModel.availability.forms import AvailabilitiesFormMixin
from AKModel.availability.models import Availability from AKModel.availability.models import Availability
from AKModel.models import AK, AKOwner, AKCategory, AKRequirement, AKSlot, AKOrgaMessage from AKModel.models import AK, AKOwner, AKCategory, AKRequirement, AKSlot, AKOrgaMessage, AKType
class AKForm(AvailabilitiesFormMixin, forms.ModelForm): class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
...@@ -37,6 +37,7 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm): ...@@ -37,6 +37,7 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
'owners', 'owners',
'description', 'description',
'category', 'category',
'types',
'reso', 'reso',
'present', 'present',
'requirements', 'requirements',
...@@ -48,6 +49,7 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm): ...@@ -48,6 +49,7 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
widgets = { widgets = {
'requirements': forms.CheckboxSelectMultiple, 'requirements': forms.CheckboxSelectMultiple,
'types': forms.CheckboxSelectMultiple,
'event': forms.HiddenInput, 'event': forms.HiddenInput,
} }
...@@ -61,6 +63,10 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm): ...@@ -61,6 +63,10 @@ class AKForm(AvailabilitiesFormMixin, forms.ModelForm):
self.fields["prerequisites"].widget.attrs = {'class': 'chosen-select'} self.fields["prerequisites"].widget.attrs = {'class': 'chosen-select'}
self.fields['category'].queryset = AKCategory.objects.filter(event=self.initial.get('event')) self.fields['category'].queryset = AKCategory.objects.filter(event=self.initial.get('event'))
self.fields['types'].queryset = AKType.objects.filter(event=self.initial.get('event'))
# Don't ask for types if there are no types configured for this event
if self.fields['types'].queryset.count() == 0:
self.fields.pop('types')
self.fields['requirements'].queryset = AKRequirement.objects.filter(event=self.initial.get('event')) self.fields['requirements'].queryset = AKRequirement.objects.filter(event=self.initial.get('event'))
# Don't ask for requirements if there are no requirements configured for this event # Don't ask for requirements if there are no requirements configured for this event
if self.fields['requirements'].queryset.count() == 0: if self.fields['requirements'].queryset.count() == 0:
...@@ -146,6 +152,9 @@ class AKSubmissionForm(AKForm): ...@@ -146,6 +152,9 @@ class AKSubmissionForm(AKForm):
class Meta(AKForm.Meta): class Meta(AKForm.Meta):
# Exclude fields again that were previously included in the parent class # Exclude fields again that were previously included in the parent class
exclude = ['link', 'protocol_link'] #pylint: disable=modelform-uses-exclude exclude = ['link', 'protocol_link'] #pylint: disable=modelform-uses-exclude
widgets = AKForm.Meta.widgets | {
'types': forms.CheckboxSelectMultiple(attrs={'checked' : 'checked'}),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
...@@ -182,6 +191,9 @@ class AKWishForm(AKForm): ...@@ -182,6 +191,9 @@ class AKWishForm(AKForm):
class Meta(AKForm.Meta): class Meta(AKForm.Meta):
# Exclude fields again that were previously included in the parent class # Exclude fields again that were previously included in the parent class
exclude = ['owners', 'link', 'protocol_link'] #pylint: disable=modelform-uses-exclude exclude = ['owners', 'link', 'protocol_link'] #pylint: disable=modelform-uses-exclude
widgets = AKForm.Meta.widgets | {
'types': forms.CheckboxSelectMultiple(attrs={'checked': 'checked'}),
}
class AKOwnerForm(forms.ModelForm): class AKOwnerForm(forms.ModelForm):
......
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-16 16:30+0200\n" "POT-Creation-Date: 2025-03-25 15:58+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -17,16 +17,16 @@ msgstr "" ...@@ -17,16 +17,16 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: AKSubmission/forms.py:93 #: AKSubmission/forms.py:101
#, python-format #, python-format
msgid "\"%(duration)s\" is not a valid duration" msgid "\"%(duration)s\" is not a valid duration"
msgstr "\"%(duration)s\" ist keine gültige Dauer" msgstr "\"%(duration)s\" ist keine gültige Dauer"
#: AKSubmission/forms.py:159 #: AKSubmission/forms.py:164
msgid "Duration(s)" msgid "Duration(s)"
msgstr "Dauer(n)" msgstr "Dauer(n)"
#: AKSubmission/forms.py:161 #: AKSubmission/forms.py:166
msgid "" msgid ""
"Enter at least one planned duration (in hours). If your AK should have " "Enter at least one planned duration (in hours). If your AK should have "
"multiple slots, use multiple lines" "multiple slots, use multiple lines"
...@@ -47,177 +47,173 @@ msgstr "" ...@@ -47,177 +47,173 @@ msgstr ""
#: AKSubmission/templates/AKSubmission/submission_overview.html:7 #: AKSubmission/templates/AKSubmission/submission_overview.html:7
#: AKSubmission/templates/AKSubmission/submission_overview.html:11 #: AKSubmission/templates/AKSubmission/submission_overview.html:11
#: AKSubmission/templates/AKSubmission/submission_overview.html:36 #: AKSubmission/templates/AKSubmission/submission_overview.html:36
#: AKSubmission/templates/AKSubmission/submit_new.html:31 #: AKSubmission/templates/AKSubmission/submit_new.html:38
#: AKSubmission/templates/AKSubmission/submit_new_wish.html:13 #: AKSubmission/templates/AKSubmission/submit_new_wish.html:13
msgid "AK Submission" msgid "AK Submission"
msgstr "AK-Eintragung" msgstr "AK-Eintragung"
#: AKSubmission/templates/AKSubmission/ak_detail.html:77 #: AKSubmission/templates/AKSubmission/ak_detail.html:126
#: AKSubmission/templates/AKSubmission/ak_interest_script.html:50 #: AKSubmission/templates/AKSubmission/ak_interest_script.html:50
msgid "Interest indication currently not allowed. Sorry." msgid "Interest indication currently not allowed. Sorry."
msgstr "Interessenangabe aktuell nicht erlaubt. Sorry." msgstr "Interessenangabe aktuell nicht erlaubt. Sorry."
#: AKSubmission/templates/AKSubmission/ak_detail.html:79 #: AKSubmission/templates/AKSubmission/ak_detail.html:128
#: AKSubmission/templates/AKSubmission/ak_interest_script.html:52 #: AKSubmission/templates/AKSubmission/ak_interest_script.html:52
msgid "Could not save your interest. Sorry." msgid "Could not save your interest. Sorry."
msgstr "Interesse konnte nicht gespeichert werden. Sorry." msgstr "Interesse konnte nicht gespeichert werden. Sorry."
#: AKSubmission/templates/AKSubmission/ak_detail.html:100 #: AKSubmission/templates/AKSubmission/ak_detail.html:149
msgid "Interest" msgid "Interest"
msgstr "Interesse" msgstr "Interesse"
#: AKSubmission/templates/AKSubmission/ak_detail.html:102 #: AKSubmission/templates/AKSubmission/ak_detail.html:151
#: AKSubmission/templates/AKSubmission/ak_table.html:55 #: AKSubmission/templates/AKSubmission/ak_table.html:65
msgid "Show Interest" msgid "Show Interest"
msgstr "Interesse bekunden" msgstr "Interesse bekunden"
#: AKSubmission/templates/AKSubmission/ak_detail.html:108 #: AKSubmission/templates/AKSubmission/ak_detail.html:157
#: AKSubmission/templates/AKSubmission/ak_table.html:46 #: AKSubmission/templates/AKSubmission/ak_table.html:56
msgid "Open external link" msgid "Open external link"
msgstr "Externen Link öffnen" msgstr "Externen Link öffnen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:113 #: AKSubmission/templates/AKSubmission/ak_detail.html:162
msgid "Open protocol link" msgid "Open protocol link"
msgstr "Protokolllink öffnen" msgstr "Protokolllink öffnen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:118 #: AKSubmission/templates/AKSubmission/ak_detail.html:167
#: AKSubmission/templates/AKSubmission/ak_history.html:19 #: AKSubmission/templates/AKSubmission/ak_history.html:19
#: AKSubmission/templates/AKSubmission/ak_history.html:31 #: AKSubmission/templates/AKSubmission/ak_history.html:31
msgid "History" msgid "History"
msgstr "Versionsgeschichte" msgstr "Versionsgeschichte"
#: AKSubmission/templates/AKSubmission/ak_detail.html:121 #: AKSubmission/templates/AKSubmission/ak_detail.html:170
#: AKSubmission/templates/AKSubmission/akmessage_add.html:8 #: AKSubmission/templates/AKSubmission/akmessage_add.html:8
#: AKSubmission/templates/AKSubmission/akmessage_add.html:16 #: AKSubmission/templates/AKSubmission/akmessage_add.html:16
#: AKSubmission/templates/AKSubmission/akmessage_add.html:22 #: AKSubmission/templates/AKSubmission/akmessage_add.html:22
msgid "Add confidential message to organizers" msgid "Add confidential message to organizers"
msgstr "Sende eine private Nachricht an das Organisationsteam" msgstr "Sende eine private Nachricht an das Organisationsteam"
#: AKSubmission/templates/AKSubmission/ak_detail.html:124 #: AKSubmission/templates/AKSubmission/ak_detail.html:173
#: AKSubmission/templates/AKSubmission/ak_detail.html:269 #: AKSubmission/templates/AKSubmission/ak_detail.html:326
#: AKSubmission/templates/AKSubmission/ak_edit.html:16 #: AKSubmission/templates/AKSubmission/ak_edit.html:16
#: AKSubmission/templates/AKSubmission/ak_table.html:51 #: AKSubmission/templates/AKSubmission/ak_table.html:61
msgid "Edit" msgid "Edit"
msgstr "Bearbeiten" msgstr "Bearbeiten"
#: AKSubmission/templates/AKSubmission/ak_detail.html:129 #: AKSubmission/templates/AKSubmission/ak_detail.html:178
#: AKSubmission/templates/AKSubmission/ak_history.html:31 #: AKSubmission/templates/AKSubmission/ak_history.html:31
#: AKSubmission/templates/AKSubmission/ak_table.html:34 #: AKSubmission/templates/AKSubmission/ak_table.html:37
msgid "AK Wish" msgid "AK Wish"
msgstr "AK-Wunsch" msgstr "AK-Wunsch"
#: AKSubmission/templates/AKSubmission/ak_detail.html:136 #: AKSubmission/templates/AKSubmission/ak_detail.html:186
#, python-format #, python-format
msgid "" msgid ""
"\n" "This AK currently takes place for another <span v-html=\"timeUntilEnd\">"
" This AK currently takes place for another " "%(featured_slot_remaining)s</span> minute(s) in %(room)s.&nbsp;"
"%(featured_slot_remaining)s minute(s) in %(room)s.\n"
" &nbsp;\n"
" "
msgstr "" msgstr ""
"\n" "Dieser AK findet noch <span v-html=\"timeUntilEnd\">"
" Dieser AK findet noch %(featured_slot_remaining)s " "%(featured_slot_remaining)s</span> Minute(n) in %(room)s statt.&nbsp;\n"
"Minute(n) in %(room)s statt.&nbsp;\n"
" " " "
#: AKSubmission/templates/AKSubmission/ak_detail.html:142 #: AKSubmission/templates/AKSubmission/ak_detail.html:189
#, python-format #, python-format
msgid "" msgid ""
"\n" "This AK starts in <span v-html=\"timeUntilStart\">"
" This AK starts in %(featured_slot_remaining)s " "%(featured_slot_remaining)s</span> minute(s) in %(room)s.&nbsp;"
"minute(s) in %(room)s.&nbsp;\n"
" "
msgstr "" msgstr ""
"\n" "Dieser AK beginnt in <span v-html=\"timeUntilStart\">"
" This AK beginnt in %(featured_slot_remaining)s " "%(featured_slot_remaining)s</span> Minute(n) in %(room)s.&nbsp;\n"
"Minute(n) in %(room)s.&nbsp;\n"
" " " "
#: AKSubmission/templates/AKSubmission/ak_detail.html:149 #: AKSubmission/templates/AKSubmission/ak_detail.html:194
#: AKSubmission/templates/AKSubmission/ak_detail.html:277 #: AKSubmission/templates/AKSubmission/ak_detail.html:334
msgid "Go to virtual room" msgid "Go to virtual room"
msgstr "Zum virtuellen Raum" msgstr "Zum virtuellen Raum"
#: AKSubmission/templates/AKSubmission/ak_detail.html:158 #: AKSubmission/templates/AKSubmission/ak_detail.html:205
#: AKSubmission/templates/AKSubmission/ak_table.html:10 #: AKSubmission/templates/AKSubmission/ak_table.html:10
msgid "Who?" msgid "Who?"
msgstr "Wer?" msgstr "Wer?"
#: AKSubmission/templates/AKSubmission/ak_detail.html:164 #: AKSubmission/templates/AKSubmission/ak_detail.html:211
#: AKSubmission/templates/AKSubmission/ak_history.html:36 #: AKSubmission/templates/AKSubmission/ak_history.html:36
#: AKSubmission/templates/AKSubmission/ak_table.html:11 #: AKSubmission/templates/AKSubmission/ak_table.html:11
msgid "Category" msgid "Category"
msgstr "Kategorie" msgstr "Kategorie"
#: AKSubmission/templates/AKSubmission/ak_detail.html:171 #: AKSubmission/templates/AKSubmission/ak_detail.html:218
#: AKSubmission/templates/AKSubmission/ak_table.html:13
msgid "Types"
msgstr "Typen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:228
#: AKSubmission/templates/AKSubmission/ak_history.html:37 #: AKSubmission/templates/AKSubmission/ak_history.html:37
msgid "Track" msgid "Track"
msgstr "Track" msgstr "Track"
#: AKSubmission/templates/AKSubmission/ak_detail.html:177 #: AKSubmission/templates/AKSubmission/ak_detail.html:234
#, fuzzy
#| msgid "Present results of this AK"
msgid "Present this AK" msgid "Present this AK"
msgstr "Die Ergebnisse dieses AKs vorstellen" msgstr "Diesen AK vorstellen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:182 #: AKSubmission/templates/AKSubmission/ak_detail.html:239
msgid "(Category Default)" msgid "(Category Default)"
msgstr "(Kategorievoreinstellung)" msgstr "(Kategorievoreinstellung)"
#: AKSubmission/templates/AKSubmission/ak_detail.html:188 #: AKSubmission/templates/AKSubmission/ak_detail.html:245
msgid "Reso intention?" msgid "Reso intention?"
msgstr "Resoabsicht?" msgstr "Resoabsicht?"
#: AKSubmission/templates/AKSubmission/ak_detail.html:195 #: AKSubmission/templates/AKSubmission/ak_detail.html:252
msgid "Requirements" msgid "Requirements"
msgstr "Anforderungen" msgstr "Anforderungen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:208 #: AKSubmission/templates/AKSubmission/ak_detail.html:265
msgid "Conflicting AKs" msgid "Conflicting AKs"
msgstr "AK-Konflikte" msgstr "AK-Konflikte"
#: AKSubmission/templates/AKSubmission/ak_detail.html:216 #: AKSubmission/templates/AKSubmission/ak_detail.html:273
msgid "Prerequisite AKs" msgid "Prerequisite AKs"
msgstr "Vorausgesetzte AKs" msgstr "Vorausgesetzte AKs"
#: AKSubmission/templates/AKSubmission/ak_detail.html:224 #: AKSubmission/templates/AKSubmission/ak_detail.html:281
msgid "Notes" msgid "Notes"
msgstr "Notizen" msgstr "Notizen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:237 #: AKSubmission/templates/AKSubmission/ak_detail.html:294
msgid "When?" msgid "When?"
msgstr "Wann?" msgstr "Wann?"
#: AKSubmission/templates/AKSubmission/ak_detail.html:239 #: AKSubmission/templates/AKSubmission/ak_detail.html:296
#: AKSubmission/templates/AKSubmission/akslot_delete.html:35 #: AKSubmission/templates/AKSubmission/akslot_delete.html:35
msgid "Duration" msgid "Duration"
msgstr "Dauer" msgstr "Dauer"
#: AKSubmission/templates/AKSubmission/ak_detail.html:241 #: AKSubmission/templates/AKSubmission/ak_detail.html:298
msgid "Room" msgid "Room"
msgstr "Raum" msgstr "Raum"
#: AKSubmission/templates/AKSubmission/ak_detail.html:272 #: AKSubmission/templates/AKSubmission/ak_detail.html:329
msgid "Delete" msgid "Delete"
msgstr "Löschen" msgstr "Löschen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:283 #: AKSubmission/templates/AKSubmission/ak_detail.html:340
msgid "Schedule" msgid "Schedule"
msgstr "Schedule" msgstr "Schedule"
#: AKSubmission/templates/AKSubmission/ak_detail.html:295 #: AKSubmission/templates/AKSubmission/ak_detail.html:352
msgid "Add another slot" msgid "Add another slot"
msgstr "Einen neuen AK-Slot hinzufügen" msgstr "Einen neuen AK-Slot hinzufügen"
#: AKSubmission/templates/AKSubmission/ak_detail.html:305 #: AKSubmission/templates/AKSubmission/ak_detail.html:362
msgid "Possible Times" msgid "Possible Times"
msgstr "Mögliche Zeiten" msgstr "Mögliche Zeiten"
#: AKSubmission/templates/AKSubmission/ak_detail.html:309 #: AKSubmission/templates/AKSubmission/ak_detail.html:366
msgid "Start" msgid "Start"
msgstr "Start" msgstr "Start"
#: AKSubmission/templates/AKSubmission/ak_detail.html:310 #: AKSubmission/templates/AKSubmission/ak_detail.html:367
msgid "End" msgid "End"
msgstr "Ende" msgstr "Ende"
...@@ -269,16 +265,16 @@ msgid "Time" ...@@ -269,16 +265,16 @@ msgid "Time"
msgstr "Zeit" msgstr "Zeit"
#: AKSubmission/templates/AKSubmission/ak_history.html:48 #: AKSubmission/templates/AKSubmission/ak_history.html:48
#: AKSubmission/templates/AKSubmission/ak_table.html:25 #: AKSubmission/templates/AKSubmission/ak_table.html:28
msgid "Present results of this AK" msgid "Present results of this AK"
msgstr "Die Ergebnisse dieses AKs vorstellen" msgstr "Die Ergebnisse dieses AKs vorstellen"
#: AKSubmission/templates/AKSubmission/ak_history.html:52 #: AKSubmission/templates/AKSubmission/ak_history.html:52
#: AKSubmission/templates/AKSubmission/ak_table.html:29 #: AKSubmission/templates/AKSubmission/ak_table.html:32
msgid "Intends to submit a resolution" msgid "Intends to submit a resolution"
msgstr "Beabsichtigt eine Resolution einzureichen" msgstr "Beabsichtigt eine Resolution einzureichen"
#: AKSubmission/templates/AKSubmission/ak_list.html:6 AKSubmission/views.py:84 #: AKSubmission/templates/AKSubmission/ak_list.html:6 AKSubmission/views.py:82
msgid "All AKs" msgid "All AKs"
msgstr "Alle AKs" msgstr "Alle AKs"
...@@ -294,11 +290,11 @@ msgstr "AK-Liste" ...@@ -294,11 +290,11 @@ msgstr "AK-Liste"
msgid "Add AK" msgid "Add AK"
msgstr "AK hinzufügen" msgstr "AK hinzufügen"
#: AKSubmission/templates/AKSubmission/ak_table.html:42 #: AKSubmission/templates/AKSubmission/ak_table.html:52
msgid "Details" msgid "Details"
msgstr "Details" msgstr "Details"
#: AKSubmission/templates/AKSubmission/ak_table.html:66 #: AKSubmission/templates/AKSubmission/ak_table.html:76
msgid "There are no AKs in this category yet" msgid "There are no AKs in this category yet"
msgstr "Es gibt noch keine AKs in dieser Kategorie" msgstr "Es gibt noch keine AKs in dieser Kategorie"
...@@ -309,7 +305,7 @@ msgstr "Senden" ...@@ -309,7 +305,7 @@ msgstr "Senden"
#: AKSubmission/templates/AKSubmission/akmessage_add.html:31 #: AKSubmission/templates/AKSubmission/akmessage_add.html:31
#: AKSubmission/templates/AKSubmission/akowner_create_update.html:26 #: AKSubmission/templates/AKSubmission/akowner_create_update.html:26
#: AKSubmission/templates/AKSubmission/akslot_add_update.html:29 #: AKSubmission/templates/AKSubmission/akslot_add_update.html:29
#: AKSubmission/templates/AKSubmission/submit_new.html:52 #: AKSubmission/templates/AKSubmission/submit_new.html:59
msgid "Reset Form" msgid "Reset Form"
msgstr "Formular leeren" msgstr "Formular leeren"
...@@ -317,7 +313,7 @@ msgstr "Formular leeren" ...@@ -317,7 +313,7 @@ msgstr "Formular leeren"
#: AKSubmission/templates/AKSubmission/akowner_create_update.html:30 #: AKSubmission/templates/AKSubmission/akowner_create_update.html:30
#: AKSubmission/templates/AKSubmission/akslot_add_update.html:33 #: AKSubmission/templates/AKSubmission/akslot_add_update.html:33
#: AKSubmission/templates/AKSubmission/akslot_delete.html:45 #: AKSubmission/templates/AKSubmission/akslot_delete.html:45
#: AKSubmission/templates/AKSubmission/submit_new.html:56 #: AKSubmission/templates/AKSubmission/submit_new.html:63
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
...@@ -385,8 +381,8 @@ msgstr "Ich leite bisher keine AKs" ...@@ -385,8 +381,8 @@ msgstr "Ich leite bisher keine AKs"
#: AKSubmission/templates/AKSubmission/submission_overview.html:67 #: AKSubmission/templates/AKSubmission/submission_overview.html:67
#: AKSubmission/templates/AKSubmission/submit_new.html:9 #: AKSubmission/templates/AKSubmission/submit_new.html:9
#: AKSubmission/templates/AKSubmission/submit_new.html:34
#: AKSubmission/templates/AKSubmission/submit_new.html:41 #: AKSubmission/templates/AKSubmission/submit_new.html:41
#: AKSubmission/templates/AKSubmission/submit_new.html:48
msgid "New AK" msgid "New AK"
msgstr "Neuer AK" msgstr "Neuer AK"
...@@ -400,15 +396,21 @@ msgstr "" ...@@ -400,15 +396,21 @@ msgstr ""
"Dieses Event is nicht aktiv. Es können keine AKs hinzugefügt oder bearbeitet " "Dieses Event is nicht aktiv. Es können keine AKs hinzugefügt oder bearbeitet "
"werden" "werden"
#: AKSubmission/templates/AKSubmission/submit_new.html:48 #: AKSubmission/templates/AKSubmission/submit_new.html:29
msgid ""
"only relevant for KIF-AKs - determines whether the AK appears in the slides "
"for the closing plenary session"
msgstr "nur relevant für KIF-AKs - entscheidet, ob der AK in den Folien fürs Abschlussplenum auftaucht"
#: AKSubmission/templates/AKSubmission/submit_new.html:55
msgid "Submit" msgid "Submit"
msgstr "Eintragen" msgstr "Eintragen"
#: AKSubmission/views.py:127 #: AKSubmission/views.py:125
msgid "Wishes" msgid "Wishes"
msgstr "Wünsche" msgstr "Wünsche"
#: AKSubmission/views.py:127 #: AKSubmission/views.py:125
msgid "AKs one would like to have" msgid "AKs one would like to have"
msgstr "" msgstr ""
"AKs die sich gewünscht wurden, aber bei denen noch nicht klar ist, wer sie " "AKs die sich gewünscht wurden, aber bei denen noch nicht klar ist, wer sie "
...@@ -418,60 +420,60 @@ msgstr "" ...@@ -418,60 +420,60 @@ msgstr ""
msgid "Currently planned AKs" msgid "Currently planned AKs"
msgstr "Aktuell geplante AKs" msgstr "Aktuell geplante AKs"
#: AKSubmission/views.py:300 #: AKSubmission/views.py:305
msgid "Event inactive. Cannot create or update." msgid "Event inactive. Cannot create or update."
msgstr "Event inaktiv. Hinzufügen/Bearbeiten nicht möglich." msgstr "Event inaktiv. Hinzufügen/Bearbeiten nicht möglich."
#: AKSubmission/views.py:325 #: AKSubmission/views.py:330
msgid "AK successfully created" msgid "AK successfully created"
msgstr "AK erfolgreich angelegt" msgstr "AK erfolgreich angelegt"
#: AKSubmission/views.py:398 #: AKSubmission/views.py:404
msgid "AK successfully updated" msgid "AK successfully updated"
msgstr "AK erfolgreich aktualisiert" msgstr "AK erfolgreich aktualisiert"
#: AKSubmission/views.py:449 #: AKSubmission/views.py:455
#, python-brace-format #, python-brace-format
msgid "Added '{owner}' as new owner of '{ak.name}'" msgid "Added '{owner}' as new owner of '{ak.name}'"
msgstr "'{owner}' als neue Leitung von '{ak.name}' hinzugefügt" msgstr "'{owner}' als neue Leitung von '{ak.name}' hinzugefügt"
#: AKSubmission/views.py:553 #: AKSubmission/views.py:558
msgid "No user selected" msgid "No user selected"
msgstr "Keine Person ausgewählt" msgstr "Keine Person ausgewählt"
#: AKSubmission/views.py:569 #: AKSubmission/views.py:574
msgid "Person Info successfully updated" msgid "Person Info successfully updated"
msgstr "Personen-Info erfolgreich aktualisiert" msgstr "Personen-Info erfolgreich aktualisiert"
#: AKSubmission/views.py:605 #: AKSubmission/views.py:610
msgid "AK Slot successfully added" msgid "AK Slot successfully added"
msgstr "AK-Slot erfolgreich angelegt" msgstr "AK-Slot erfolgreich angelegt"
#: AKSubmission/views.py:624 #: AKSubmission/views.py:629
msgid "You cannot edit a slot that has already been scheduled" msgid "You cannot edit a slot that has already been scheduled"
msgstr "Bereits geplante AK-Slots können nicht mehr bearbeitet werden" msgstr "Bereits geplante AK-Slots können nicht mehr bearbeitet werden"
#: AKSubmission/views.py:634 #: AKSubmission/views.py:639
msgid "AK Slot successfully updated" msgid "AK Slot successfully updated"
msgstr "AK-Slot erfolgreich aktualisiert" msgstr "AK-Slot erfolgreich aktualisiert"
#: AKSubmission/views.py:652 #: AKSubmission/views.py:657
msgid "You cannot delete a slot that has already been scheduled" msgid "You cannot delete a slot that has already been scheduled"
msgstr "Bereits geplante AK-Slots können nicht mehr gelöscht werden" msgstr "Bereits geplante AK-Slots können nicht mehr gelöscht werden"
#: AKSubmission/views.py:662 #: AKSubmission/views.py:667
msgid "AK Slot successfully deleted" msgid "AK Slot successfully deleted"
msgstr "AK-Slot erfolgreich angelegt" msgstr "AK-Slot erfolgreich angelegt"
#: AKSubmission/views.py:674 #: AKSubmission/views.py:679
msgid "Messages" msgid "Messages"
msgstr "Nachrichten" msgstr "Nachrichten"
#: AKSubmission/views.py:684 #: AKSubmission/views.py:689
msgid "Delete all messages" msgid "Delete all messages"
msgstr "Alle Nachrichten löschen" msgstr "Alle Nachrichten löschen"
#: AKSubmission/views.py:711 #: AKSubmission/views.py:716
msgid "Message to organizers successfully saved" msgid "Message to organizers successfully saved"
msgstr "Nachricht an die Organisator*innen erfolgreich gespeichert" msgstr "Nachricht an die Organisator*innen erfolgreich gespeichert"
......
...@@ -27,6 +27,55 @@ ...@@ -27,6 +27,55 @@
{% block imports %} {% block imports %}
{% include "AKPlan/plan_akslot.html" %} {% include "AKPlan/plan_akslot.html" %}
<script type="module">
const { createApp } = Vue
function getCurrentTimestamp() {
return Date.now() / 1000
}
createApp({
delimiters: ["[[", "]]"],
data() {
return {
featuredSlot: "{% if featured_slot %}true{% else %}false{% endif %}",
timer: null,
now: getCurrentTimestamp(),
akStart: "{{ featured_slot.start | date:'U' }}",
akEnd: "{{ featured_slot.end | date:'U' }}",
showBoxWithoutJS: false,
}
},
computed: {
showFeatured() {
return this.featuredSlot && this.now < this.akEnd
},
isBefore() {
return this.featuredSlot && this.now < this.akStart
},
isDuring() {
return this.featuredSlot && this.akStart < this.now && this.now < this.akEnd
},
timeUntilStart() {
return Math.ceil((this.akStart - this.now) / 60)
},
timeUntilEnd() {
return Math.floor((this.akEnd - this.now) / 60)
}
},
mounted: function () {
if(this.featuredSlot) {
this.timer = setInterval(() => {
this.now = getCurrentTimestamp()
}, 10000)
}
},
beforeUnmount() {
clearInterval(this.timer)
}
}).mount('#app')
</script>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
// CSRF Protection/Authentication // CSRF Protection/Authentication
...@@ -128,30 +177,28 @@ ...@@ -128,30 +177,28 @@
<h2>{% if ak.wish %}{% trans "AK Wish" %}: {% endif %}{{ ak.name }}</h2> <h2>{% if ak.wish %}{% trans "AK Wish" %}: {% endif %}{{ ak.name }}</h2>
{# Show current or upcoming slot featured in a box on top of the plage #} <div id="app">
{% if featured_slot_type != "NONE" %} {# Show current or upcoming slot featured in a box on top of the plage #}
<div class="card border-success mt-3 mb-3"> {% if featured_slot_type != "NONE" %}
<div class="card-body font-weight-bold"> <div class="card border-success mt-3 mb-3" v-show="showFeatured">
{% if featured_slot_type == "CURRENT" %} <div class="card-body font-weight-bold">
{% blocktrans with room=featured_slot.room %} <span v-show="isDuring" style="{% if not featured_slot_type == "CURRENT" %}display:none;{% endif %}">
This AK currently takes place for another {{ featured_slot_remaining }} minute(s) in {{ room }}. {% blocktrans with room=featured_slot.room %}This AK currently takes place for another <span v-html="timeUntilEnd">{{ featured_slot_remaining }}</span> minute(s) in {{ room }}.&nbsp;{% endblocktrans %}
&nbsp; </span>
{% endblocktrans %} <span v-show="isBefore" style="{% if not featured_slot_type == "UPCOMING" %}display:none;{% endif %}">
{% blocktrans with room=featured_slot.room %}This AK starts in <span v-html="timeUntilStart">{{ featured_slot_remaining }}</span> minute(s) in {{ room }}.&nbsp;{% endblocktrans %}
{% elif featured_slot_type == "UPCOMING" %} </span>
{% blocktrans with room=featured_slot.room %}
This AK starts in {{ featured_slot_remaining }} minute(s) in {{ room }}.&nbsp;
{% endblocktrans %}
{% endif %}
{% if "AKOnline"|check_app_installed and featured_slot.room.virtual and featured_slot.room.virtual.url != '' %} {% if "AKOnline"|check_app_installed and featured_slot.room.virtual and featured_slot.room.virtual.url != '' %}
<a class="btn btn-success" target="_parent" href="{{ featured_slot.room.virtual.url }}"> <a class="btn btn-success" target="_parent" href="{{ featured_slot.room.virtual.url }}">
{% fa6_icon 'external-link-alt' 'fas' %} {% trans "Go to virtual room" %} {% fa6_icon 'external-link-alt' 'fas' %} {% trans "Go to virtual room" %}
</a> </a>
{% endif %} {% endif %}
</div>
</div> </div>
</div> {% endif %}
{% endif %} </div>
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
...@@ -166,6 +213,16 @@ ...@@ -166,6 +213,16 @@
{% category_linked_badge ak.category ak.event.slug %} {% category_linked_badge ak.category ak.event.slug %}
</td> </td>
</tr> </tr>
{% if ak.types.count > 0 %}
<tr>
<td>{% trans "Types" %}</td>
<td>
{% for type in ak.types.all %}
<span class="badge bg-info">{{ type }}</span>
{% endfor %}
</td>
</tr>
{% endif %}
{% if ak.track %} {% if ak.track %}
<tr> <tr>
<td>{% trans 'Track' %}</td> <td>{% trans 'Track' %}</td>
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
<th>{% trans "Name" %}</th> <th>{% trans "Name" %}</th>
<th>{% trans "Who?" %}</th> <th>{% trans "Who?" %}</th>
<th>{% trans 'Category' %}</th> <th>{% trans 'Category' %}</th>
{% if show_types %}
<th>{% trans 'Types' %}</th>
{% endif %}
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
...@@ -37,6 +40,13 @@ ...@@ -37,6 +40,13 @@
{% endif %} {% endif %}
</td> </td>
<td>{% category_linked_badge ak.category event.slug %}</td> <td>{% category_linked_badge ak.category event.slug %}</td>
{% if show_types %}
<td>
{% for aktype in ak.types.all %}
<span class="badge bg-info">{{ aktype }}</span>
{% endfor %}
</td>
{% endif %}
<td class="text-end" style="white-space: nowrap;"> <td class="text-end" style="white-space: nowrap;">
<a href="{{ ak.detail_url }}" data-bs-toggle="tooltip" <a href="{{ ak.detail_url }}" data-bs-toggle="tooltip"
title="{% trans 'Details' %}" title="{% trans 'Details' %}"
......
...@@ -23,6 +23,13 @@ ...@@ -23,6 +23,13 @@
); );
}); });
</script> </script>
<style>
#id_present_helptext::after {
content: " ({% trans "only relevant for KIF-AKs - determines whether the AK appears in the slides for the closing plenary session" %})";
color: #6c757d;
}
</style>
{% endblock %} {% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
......
from datetime import timedelta from datetime import datetime, timedelta
from django.test import TestCase from django.test import TestCase
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.datetime_safe import datetime
from AKModel.models import AK, AKSlot, Event from AKModel.models import AK, AKSlot, Event
from AKModel.tests import BasicViewTests from AKModel.tests import BasicViewTests
from AKSubmission.forms import AKSubmissionForm
class ModelViewTests(BasicViewTests, TestCase): class ModelViewTests(BasicViewTests, TestCase):
...@@ -46,8 +46,8 @@ class ModelViewTests(BasicViewTests, TestCase): ...@@ -46,8 +46,8 @@ class ModelViewTests(BasicViewTests, TestCase):
'expected_message': "AK successfully updated"}, 'expected_message': "AK successfully updated"},
{'view': 'akslot_edit', 'target_view': 'ak_detail', 'kwargs': {'event_slug': 'kif42', 'pk': 5}, {'view': 'akslot_edit', 'target_view': 'ak_detail', 'kwargs': {'event_slug': 'kif42', 'pk': 5},
'target_kwargs': {'event_slug': 'kif42', 'pk': 1}, 'expected_message': "AK Slot successfully updated"}, 'target_kwargs': {'event_slug': 'kif42', 'pk': 1}, 'expected_message': "AK Slot successfully updated"},
{'view': 'akowner_edit', 'target_view': 'submission_overview', 'kwargs': {'event_slug': 'kif42', 'slug': 'a'}, {'view': 'akowner_edit', 'target_view': 'submission_overview', 'kwargs': {'event_slug': 'kif42', 'slug': 'a'},
'target_kwargs': {'event_slug': 'kif42'}, 'expected_message': "Person Info successfully updated"}, 'target_kwargs': {'event_slug': 'kif42'}, 'expected_message': "Person Info successfully updated"},
] ]
def test_akslot_edit_delete_prevention(self): def test_akslot_edit_delete_prevention(self):
...@@ -146,7 +146,8 @@ class ModelViewTests(BasicViewTests, TestCase): ...@@ -146,7 +146,8 @@ class ModelViewTests(BasicViewTests, TestCase):
add_redirect_url = reverse_lazy(f"{self.APP_NAME}:submit_ak", kwargs={'event_slug': 'kif42', 'owner_slug': 'a'}) add_redirect_url = reverse_lazy(f"{self.APP_NAME}:submit_ak", kwargs={'event_slug': 'kif42', 'owner_slug': 'a'})
response = self.client.post(select_url, {'owner_id': 1}) response = self.client.post(select_url, {'owner_id': 1})
self.assertRedirects(response, add_redirect_url, status_code=302, target_status_code=200, self.assertRedirects(response, add_redirect_url, status_code=302, target_status_code=200,
msg_prefix=f"Dispatch redirect to ak submission page failed (should go to {add_redirect_url})") msg_prefix=f"Dispatch redirect to ak submission page failed "
f"(should go to {add_redirect_url})")
def test_orga_message_submission(self): def test_orga_message_submission(self):
""" """
...@@ -200,7 +201,8 @@ class ModelViewTests(BasicViewTests, TestCase): ...@@ -200,7 +201,8 @@ class ModelViewTests(BasicViewTests, TestCase):
# Test indication outside of indication window -> HTTP 403, counter not increased # Test indication outside of indication window -> HTTP 403, counter not increased
response = self.client.post(interest_api_url) response = self.client.post(interest_api_url)
self.assertEqual(response.status_code, 403, self.assertEqual(response.status_code, 403,
"API end point still reachable even though interest indication window ended ({interest_api_url})") "API end point still reachable even though interest indication window ended "
"({interest_api_url})")
self.assertEqual(AK.objects.get(pk=1).interest_counter, ak_interest_counter + 1, self.assertEqual(AK.objects.get(pk=1).interest_counter, ak_interest_counter + 1,
"Counter was increased even though interest indication window ended") "Counter was increased even though interest indication window ended")
...@@ -236,3 +238,39 @@ class ModelViewTests(BasicViewTests, TestCase): ...@@ -236,3 +238,39 @@ class ModelViewTests(BasicViewTests, TestCase):
msg_prefix=f"No correct redirect: {add_new_user_to_ak_url} (POST) -> {detail_url}") msg_prefix=f"No correct redirect: {add_new_user_to_ak_url} (POST) -> {detail_url}")
self._assert_message(response, "Added 'New test owner' as new owner of 'Test AK Inhalt'") self._assert_message(response, "Added 'New test owner' as new owner of 'Test AK Inhalt'")
self.assertEqual(AK.objects.get(pk=1).owners.count(), 2) self.assertEqual(AK.objects.get(pk=1).owners.count(), 2)
def test_visibility_requirements_in_submission_form(self):
"""
Test visibility of requirements field in submission form
"""
event = Event.get_by_slug('kif42')
form = AKSubmissionForm(data={'name': 'Test AK', 'event': event}, instance=None, initial={"event": event})
self.assertIn('requirements', form.fields,
msg="Requirements field not present in form even though event has requirements")
event2 = Event.objects.create(name='Event without requirements',
slug='no_req',
start=datetime.now().astimezone(event.timezone),
end=datetime.now().astimezone(event.timezone),
active=True)
form2 = AKSubmissionForm(data={'name': 'Test AK', 'event': event2}, instance=None, initial={"event": event2})
self.assertNotIn('requirements', form2.fields,
msg="Requirements field should not be present for events without requirements")
def test_visibility_types_in_submission_form(self):
"""
Test visibility of types field in submission form
"""
event = Event.get_by_slug('kif42')
form = AKSubmissionForm(data={'name': 'Test AK', 'event': event}, instance=None, initial={"event": event})
self.assertIn('types', form.fields,
msg="Requirements field not present in form even though event has requirements")
event2 = Event.objects.create(name='Event without types',
slug='no_types',
start=datetime.now().astimezone(event.timezone),
end=datetime.now().astimezone(event.timezone),
active=True)
form2 = AKSubmissionForm(data={'name': 'Test AK', 'event': event2}, instance=None, initial={"event": event2})
self.assertNotIn('types', form2.fields,
msg="Requirements field should not be present for events without types")
from datetime import timedelta
from math import floor
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from datetime import datetime, timedelta
from math import floor
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
...@@ -8,19 +8,17 @@ from django.contrib import messages ...@@ -8,19 +8,17 @@ from django.contrib import messages
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.datetime_safe import datetime
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView from django.views.generic import CreateView, DeleteView, DetailView, ListView, TemplateView, UpdateView
from AKModel.availability.models import Availability from AKModel.availability.models import Availability
from AKModel.metaviews import status_manager from AKModel.metaviews import status_manager
from AKModel.metaviews.status import TemplateStatusWidget
from AKModel.models import AK, AKCategory, AKOwner, AKSlot, AKTrack, AKOrgaMessage
from AKModel.metaviews.admin import EventSlugMixin, FilterByEventSlugMixin from AKModel.metaviews.admin import EventSlugMixin, FilterByEventSlugMixin
from AKModel.metaviews.status import TemplateStatusWidget
from AKModel.models import AK, AKCategory, AKOrgaMessage, AKOwner, AKSlot, AKTrack
from AKSubmission.api import ak_interest_indication_active from AKSubmission.api import ak_interest_indication_active
from AKSubmission.forms import AKWishForm, AKOwnerForm, AKSubmissionForm, AKDurationForm, AKOrgaMessageForm, \ from AKSubmission.forms import AKDurationForm, AKForm, AKOrgaMessageForm, AKOwnerForm, AKSubmissionForm, AKWishForm
AKForm
class SubmissionErrorNotConfiguredView(EventSlugMixin, TemplateView): class SubmissionErrorNotConfiguredView(EventSlugMixin, TemplateView):
...@@ -47,7 +45,7 @@ class AKOverviewView(FilterByEventSlugMixin, ListView): ...@@ -47,7 +45,7 @@ class AKOverviewView(FilterByEventSlugMixin, ListView):
template_name = "AKSubmission/ak_overview.html" template_name = "AKSubmission/ak_overview.html"
wishes_as_category = False wishes_as_category = False
def filter_aks(self, context, category): # pylint: disable=unused-argument def filter_aks(self, context, category): # pylint: disable=unused-argument
""" """
Filter which AKs to display based on the given context and category Filter which AKs to display based on the given context and category
...@@ -59,7 +57,7 @@ class AKOverviewView(FilterByEventSlugMixin, ListView): ...@@ -59,7 +57,7 @@ class AKOverviewView(FilterByEventSlugMixin, ListView):
:rtype: QuerySet[AK] :rtype: QuerySet[AK]
""" """
# Use prefetching and relation selection/joining to reduce the amount of necessary queries # Use prefetching and relation selection/joining to reduce the amount of necessary queries
return category.ak_set.select_related('event').prefetch_related('owners').all() return category.ak_set.select_related('event').prefetch_related('owners').prefetch_related('types').all()
def get_active_category_name(self, context): def get_active_category_name(self, context):
""" """
...@@ -73,7 +71,7 @@ class AKOverviewView(FilterByEventSlugMixin, ListView): ...@@ -73,7 +71,7 @@ class AKOverviewView(FilterByEventSlugMixin, ListView):
""" """
return context["categories_with_aks"][0][0].name return context["categories_with_aks"][0][0].name
def get_table_title(self, context): # pylint: disable=unused-argument def get_table_title(self, context): # pylint: disable=unused-argument
""" """
Specify the title above the AK list/table in this view Specify the title above the AK list/table in this view
...@@ -91,7 +89,7 @@ class AKOverviewView(FilterByEventSlugMixin, ListView): ...@@ -91,7 +89,7 @@ class AKOverviewView(FilterByEventSlugMixin, ListView):
redirect to error page if necessary (see :class:`SubmissionErrorNotConfiguredView`) redirect to error page if necessary (see :class:`SubmissionErrorNotConfiguredView`)
""" """
self._load_event() self._load_event()
self.object_list = self.get_queryset() # pylint: disable=attribute-defined-outside-init self.object_list = self.get_queryset() # pylint: disable=attribute-defined-outside-init
# No categories yet? Redirect to configuration error page # No categories yet? Redirect to configuration error page
if self.object_list.count() == 0: if self.object_list.count() == 0:
...@@ -124,12 +122,14 @@ class AKOverviewView(FilterByEventSlugMixin, ListView): ...@@ -124,12 +122,14 @@ class AKOverviewView(FilterByEventSlugMixin, ListView):
if self.wishes_as_category: if self.wishes_as_category:
categories_with_aks.append( categories_with_aks.append(
(AKCategory(name=_("Wishes"), pk=0, description=_("AKs one would like to have")), ak_wishes)) (AKCategory(name=_("Wishes"), pk=0, description=_("AKs one would like to have")), ak_wishes))
context["categories_with_aks"] = categories_with_aks context["categories_with_aks"] = categories_with_aks
context["active_category"] = self.get_active_category_name(context) context["active_category"] = self.get_active_category_name(context)
context['table_title'] = self.get_table_title(context) context['table_title'] = self.get_table_title(context)
context['show_types'] = self.event.aktype_set.count() > 0
# ========================================================== # ==========================================================
# Display interest indication button? # Display interest indication button?
# ========================================================== # ==========================================================
...@@ -181,10 +181,13 @@ class AKListByCategoryView(AKOverviewView): ...@@ -181,10 +181,13 @@ class AKListByCategoryView(AKOverviewView):
This view inherits from :class:`AKOverviewView`, but produces only one list instead of a tabbed one. This view inherits from :class:`AKOverviewView`, but produces only one list instead of a tabbed one.
""" """
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
# Override dispatching # Override dispatching
# Needed to handle the checking whether the category exists # Needed to handle the checking whether the category exists
self.category = get_object_or_404(AKCategory, pk=kwargs['category_pk']) # pylint: disable=attribute-defined-outside-init,line-too-long # noinspection PyAttributeOutsideInit
# pylint: disable=attribute-defined-outside-init
self.category = get_object_or_404(AKCategory, pk=kwargs['category_pk'])
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_active_category_name(self, context): def get_active_category_name(self, context):
...@@ -207,11 +210,12 @@ class AKListByTrackView(AKOverviewView): ...@@ -207,11 +210,12 @@ class AKListByTrackView(AKOverviewView):
This view inherits from :class:`AKOverviewView` and there will be one list per category This view inherits from :class:`AKOverviewView` and there will be one list per category
-- but only AKs of a certain given track will be included in them. -- but only AKs of a certain given track will be included in them.
""" """
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
# Override dispatching # Override dispatching
# Needed to handle the checking whether the track exists # Needed to handle the checking whether the track exists
self.track = get_object_or_404(AKTrack, pk=kwargs['track_pk']) # pylint: disable=attribute-defined-outside-init self.track = get_object_or_404(AKTrack, pk=kwargs['track_pk']) # pylint: disable=attribute-defined-outside-init
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def filter_aks(self, context, category): def filter_aks(self, context, category):
...@@ -290,6 +294,7 @@ class EventInactiveRedirectMixin: ...@@ -290,6 +294,7 @@ class EventInactiveRedirectMixin:
Will add a message explaining why the action was not performed to the user Will add a message explaining why the action was not performed to the user
and then redirect to start page of the submission component and then redirect to start page of the submission component
""" """
def get_error_message(self): def get_error_message(self):
""" """
Error message to display after redirect (can be adjusted by this method) Error message to display after redirect (can be adjusted by this method)
...@@ -349,6 +354,7 @@ class AKSubmissionView(AKAndAKWishSubmissionView): ...@@ -349,6 +354,7 @@ class AKSubmissionView(AKAndAKWishSubmissionView):
Extends :class:`AKAndAKWishSubmissionView` Extends :class:`AKAndAKWishSubmissionView`
""" """
def get_initial(self): def get_initial(self):
# Load initial values for the form # Load initial values for the form
# Used to directly add the first owner and the event this AK will belong to # Used to directly add the first owner and the event this AK will belong to
...@@ -498,7 +504,6 @@ class AKOwnerDispatchView(ABC, EventSlugMixin, View): ...@@ -498,7 +504,6 @@ class AKOwnerDispatchView(ABC, EventSlugMixin, View):
:rtype: HttpResponseRedirect :rtype: HttpResponseRedirect
""" """
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
# This view is solely meant to handle POST requests # This view is solely meant to handle POST requests
# Perform dispatching based on the submitted owner_id # Perform dispatching based on the submitted owner_id
......
...@@ -10,7 +10,7 @@ setup. ...@@ -10,7 +10,7 @@ setup.
### System Requirements ### System Requirements
* Python 3.8+ incl. development tools * Python3.11+ incl. development tools
* Virtualenv * Virtualenv
* pdflatex & beamer * pdflatex & beamer
class (`texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-fonts-extra texlive-luatex`) class (`texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-fonts-extra texlive-luatex`)
......
...@@ -8,14 +8,21 @@ if [ -z ${VIRTUAL_ENV+x} ]; then ...@@ -8,14 +8,21 @@ if [ -z ${VIRTUAL_ENV+x} ]; then
fi fi
# enable really all warnings, some of them are silenced by default # enable really all warnings, some of them are silenced by default
if [[ "$@" == *"--all"* ]]; then for arg in "$@"; do
export PYTHONWARNINGS=all if [[ "$arg" == "--all" ]]; then
fi export PYTHONWARNINGS=all
fi
done
# in case of checking production setup # in case of checking production setup
if [[ "$@" == *"--prod"* ]]; then for arg in "$@"; do
export DJANGO_SETTINGS_MODULE=AKPlanning.settings_production if [[ "$arg" == "--prod" ]]; then
./manage.py check --deploy export DJANGO_SETTINGS_MODULE=AKPlanning.settings_production
fi ./manage.py check --deploy
./manage.py makemigrations --dry-run --check
fi
done
# check the setup
./manage.py check ./manage.py check
./manage.py makemigrations --dry-run --check
...@@ -10,7 +10,7 @@ rm -rf venv/ ...@@ -10,7 +10,7 @@ rm -rf venv/
# Setup Python Environment # Setup Python Environment
# Requires: Virtualenv, appropriate Python installation # Requires: Virtualenv, appropriate Python installation
virtualenv venv -p python3.9 virtualenv venv -p python3.11
source venv/bin/activate source venv/bin/activate
pip install --upgrade setuptools pip wheel pip install --upgrade setuptools pip wheel
pip install -r requirements.txt pip install -r requirements.txt
......
#!/usr/bin/env bash
# Test the AKPlanning setup
# execute as Utils/test.sh
# activate virtualenv when necessary
if [ -z ${VIRTUAL_ENV+x} ]; then
source venv/bin/activate
fi
# enable really all warnings, some of them are silenced by default
for arg in "$@"; do
if [[ "$arg" == "--all" ]]; then
export PYTHONWARNINGS=all
fi
done
# in case of checking production setup
for arg in "$@"; do
if [[ "$arg" == "--prod" ]]; then
export DJANGO_SETTINGS_MODULE=AKPlanning.settings_production
./manage.py test --deploy
fi
done
# run tests
./manage.py test
...@@ -11,7 +11,7 @@ import django ...@@ -11,7 +11,7 @@ import django
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'AK Planning' project = 'AK Planning'
copyright = '2023, N. Geisler, B. Hättasch & more' copyright = '2025, N. Geisler, B. Hättasch & more'
author = 'N. Geisler, B. Hättasch & more' author = 'N. Geisler, B. Hättasch & more'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
......