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

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
  • 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
Show changes
Commits on Source (2)
......@@ -272,3 +272,12 @@ class RoomFormWithAvailabilities(AvailabilitiesFormMixin, RoomForm):
# Filter possible values for m2m when event is specified
if hasattr(self.instance, "event") and self.instance.event is not None:
self.fields["properties"].queryset = AKRequirement.objects.filter(event=self.instance.event)
class JSONImportForm(AdminIntermediateForm):
json_data = forms.CharField(
required=True,
widget=forms.Textarea,
label=_("JSON data"),
help_text=_("JSON data from the scheduling solver"),
)
import itertools
import json
from datetime import timedelta
from django.db import models
......@@ -162,6 +163,66 @@ class Event(models.Model):
.filter(availabilities__count=0, owners__count__gt=0)
)
def time_slots(self, *, slots_in_an_hour=1.0):
from AKModel.availability.models import Availability
rooms = Room.objects.filter(event=self)
slot_duration = timedelta(hours=(1.0 / slots_in_an_hour))
slot_index = 0
current_slot = self.start
current_block = []
previous_slot = None
room_availabilities = list({availability
for room in rooms
for availability in room.availabilities.all()})
while current_slot < self.end:
slot = Availability(event=self,
start=current_slot,
end=current_slot + slot_duration)
if any((availability.contains(slot)
for availability in room_availabilities)):
if previous_slot is not None and previous_slot + slot_duration < current_slot:
yield current_block
current_block = []
current_block.append(slot_index)
previous_slot = current_slot
slot_index += 1
current_slot += slot_duration
yield current_block
def time_slot(self, *, time_slot_index, slots_in_an_hour=1.0):
from AKModel.availability.models import Availability
slot_duration = timedelta(hours=(1.0 / slots_in_an_hour))
start = self.start + time_slot_index * slot_duration
return Availability(event=self,
start=start,
end=start + slot_duration)
def schedule_from_json(self, schedule):
schedule = json.loads(schedule)
slots_in_an_hour = schedule["input"]["timeslots"]["info"]["duration"]
for scheduled_slot in schedule["scheduled_aks"]:
slot = AKSlot.objects.get(scheduled_slot["ak_id"])
slot.room = scheduled_slot["room_id"]
start = min(scheduled_slot["time_slot_ids"])
end = max(scheduled_slot["time_slot_ids"])
slot.start = self.time_slot(time_slot_index=start,
slots_in_an_hour=slots_in_an_hour)
slot.end = self.time_slot(time_slot_index=end + 1,
slots_in_an_hour=slots_in_an_hour)
class AKOwner(models.Model):
""" An AKOwner describes the person organizing/holding an AK.
......@@ -236,6 +297,22 @@ class AKOwner(models.Model):
"""
return AKOwner.objects.get(event=event, slug=slug)
def as_json(self) -> str:
data = {
"id": self.pk,
"info": {
"name": self.name,
},
"capacity": self.capacity,
"fulfilled_room_constraints": [constraint.name
for constraint in self.properties.all()],
"time_constraints": [f"availability-room-{self.pk}"]
}
return json.dumps(data)
class AKCategory(models.Model):
""" An AKCategory describes the characteristics of an AK, e.g. content vs. recreational.
......
{% extends "admin/base_site.html" %}
{% load tz %}
{% block content %}
<pre>
{"aks": [
{% for slot in slots %}{{ slot.as_json }}{% if not forloop.last %},
{% endif %}{% endfor %}
],
"rooms": [
{% for room in rooms %}{{ room.as_json }}{% if not forloop.last %},
{% endif %}{% endfor %}
],
"participants": {{ participants }},
"timeslots": {{ timeslots }}
}
</pre>
{% endblock %}
......@@ -5,8 +5,9 @@ from rest_framework.routers import DefaultRouter
import AKModel.views.api
from AKModel.views.manage import ExportSlidesView, PlanPublishView, PlanUnpublishView, DefaultSlotEditorView, \
AKsByUserView
from AKModel.views.ak import AKRequirementOverview, AKCSVExportView, AKWikiExportView, AKMessageDeleteView
AKsByUserView, AKJSONImportView
from AKModel.views.ak import AKRequirementOverview, AKCSVExportView, AKJSONExportView, AKWikiExportView, \
AKMessageDeleteView
from AKModel.views.event_wizard import NewEventWizardStartView, NewEventWizardPrepareImportView, \
NewEventWizardImportView, NewEventWizardActivateView, NewEventWizardFinishView, NewEventWizardSettingsView
from AKModel.views.room import RoomBatchCreationView
......@@ -96,6 +97,10 @@ def get_admin_urls_event(admin_site):
name="aks_by_owner"),
path('<slug:event_slug>/ak-csv-export/', admin_site.admin_view(AKCSVExportView.as_view()),
name="ak_csv_export"),
path('<slug:event_slug>/ak-json-export/', admin_site.admin_view(AKJSONExportView.as_view()),
name="ak_json_export"),
path('<slug:event_slug>/ak-json-import/', admin_site.admin_view(AKJSONImportView.as_view()),
name="ak_json_import"),
path('<slug:slug>/ak-wiki-export/', admin_site.admin_view(AKWikiExportView.as_view()),
name="ak_wiki_export"),
path('<slug:event_slug>/delete-orga-messages/', admin_site.admin_view(AKMessageDeleteView.as_view()),
......
import json
from datetime import timedelta
from django.contrib import messages
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
......@@ -37,6 +40,88 @@ class AKCSVExportView(AdminViewMixin, FilterByEventSlugMixin, ListView):
return super().get_queryset().order_by("ak__track")
class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView):
"""
View: Export all AK slots of this event in JSON format ordered by tracks
"""
template_name = "admin/AKModel/ak_json_export.html"
model = AKSlot
context_object_name = "slots"
title = _("AK JSON Export")
def get_queryset(self):
return super().get_queryset().order_by("ak__track")
def get_context_data(self, **kwargs):
from AKModel.availability.models import Availability
SLOTS_IN_AN_HOUR = 1
rooms = Room.objects.filter(event=self.event)
participants = []
timeslots = {
"info": {"duration": (1.0 / SLOTS_IN_AN_HOUR), },
"blocks": [],
}
context = super().get_context_data(**kwargs)
context["rooms"] = rooms
context["participants"] = json.dumps(participants)
for slot in context["slots"]:
slot.slots_in_an_hour = SLOTS_IN_AN_HOUR
ak_availabilities = {slot.ak.pk: availability
for slot in context["slots"]
for availability in slot.ak.availabilities.all()}
room_availabilities = {room.pk: availability
for room in rooms
for availability in room.availabilities.all()}
person_availabilities = {person.pk: availability
for person in AKOwner.objects.filter(event=self.event)
for availability in person.availabilities.all()}
for block in self.event.time_slots(slots_in_an_hour=SLOTS_IN_AN_HOUR):
current_block = []
for slot_index in block:
slot = self.event.time_slot(time_slot_index=slot_index,
slots_in_an_hour=SLOTS_IN_AN_HOUR)
constraints = []
if slot.end < self.event.reso_deadline:
constraints.append("resolution")
for (ak, availability) in ak_availabilities.items():
if availability.contains(slot):
constraints.append(f"availability-ak-{ak}")
for (person, availability) in person_availabilities.items():
if availability.contains(slot):
constraints.append(f"availability-person-{person}")
for (room, availability) in room_availabilities.items():
if availability.contains(slot):
constraints.append(f"availability-room-{room}")
current_block.append({
"id": slot_index,
"info": {
"start": slot.simplified,
},
"fulfilled_time_constraints": constraints,
})
timeslots["blocks"].append(current_block)
context["timeslots"] = json.dumps(timeslots)
return context
class AKWikiExportView(AdminViewMixin, DetailView):
"""
View: Export AKs of this event in wiki syntax
......
......@@ -12,7 +12,7 @@ from django.views.generic import TemplateView, DetailView
from django_tex.core import render_template_with_context, run_tex_in_directory
from django_tex.response import PDFResponse
from AKModel.forms import SlideExportForm, DefaultSlotEditorForm
from AKModel.forms import SlideExportForm, DefaultSlotEditorForm, JSONImportForm
from AKModel.metaviews.admin import EventSlugMixin, IntermediateAdminView, IntermediateAdminActionView, AdminViewMixin
from AKModel.models import ConstraintViolation, Event, DefaultSlot, AKOwner
......@@ -245,3 +245,13 @@ class AKsByUserView(AdminViewMixin, EventSlugMixin, DetailView):
model = AKOwner
context_object_name = 'owner'
template_name = "admin/AKModel/aks_by_user.html"
class AKJSONImportView(EventSlugMixin, IntermediateAdminView):
form_class = JSONImportForm
title = _("AK JSON Import")
def form_valid(self, form):
self.event.schedule_from_json(form.data["json_data"])
return redirect("admin:event_status", self.event.slug)
......@@ -133,10 +133,18 @@ class EventAKsWidget(TemplateStatusWidget):
"text": _("Manage ak tracks"),
"url": reverse_lazy("admin:tracks_manage", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Import AK schedule from JSON"),
"url": reverse_lazy("admin:ak_json_import", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Export AKs as CSV"),
"url": reverse_lazy("admin:ak_csv_export", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Export AKs as JSON"),
"url": reverse_lazy("admin:ak_json_export", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Export AKs for Wiki"),
"url": reverse_lazy("admin:ak_wiki_export", kwargs={"slug": context["event"].slug}),
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-05-15 20:03+0200\n"
"POT-Creation-Date: 2024-05-27 01:57+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -38,7 +38,7 @@ msgstr "Veranstaltung"
#: AKPlan/templates/AKPlan/plan_index.html:59
#: AKPlan/templates/AKPlan/plan_room.html:13
#: AKPlan/templates/AKPlan/plan_room.html:59
#: AKPlan/templates/AKPlan/plan_wall.html:65
#: AKPlan/templates/AKPlan/plan_wall.html:67
msgid "Room"
msgstr "Raum"
......@@ -63,12 +63,12 @@ msgid "AK Wall"
msgstr "AK-Wall"
#: AKPlan/templates/AKPlan/plan_index.html:130
#: AKPlan/templates/AKPlan/plan_wall.html:130
#: AKPlan/templates/AKPlan/plan_wall.html:132
msgid "Current AKs"
msgstr "Aktuelle AKs"
#: AKPlan/templates/AKPlan/plan_index.html:137
#: AKPlan/templates/AKPlan/plan_wall.html:135
#: AKPlan/templates/AKPlan/plan_wall.html:137
msgid "Next AKs"
msgstr "Nächste AKs"
......@@ -99,7 +99,7 @@ msgstr "Eigenschaften"
msgid "Track"
msgstr "Track"
#: AKPlan/templates/AKPlan/plan_wall.html:145
#: AKPlan/templates/AKPlan/plan_wall.html:147
msgid "Reload page automatically?"
msgstr "Seite automatisch neu laden?"
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-16 16:30+0200\n"
"POT-Creation-Date: 2024-05-27 01:57+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -17,10 +17,10 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: AKPlanning/settings.py:148
#: AKPlanning/settings.py:147
msgid "German"
msgstr "Deutsch"
#: AKPlanning/settings.py:149
#: AKPlanning/settings.py:148
msgid "English"
msgstr "Englisch"
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-16 16:30+0200\n"
"POT-Creation-Date: 2024-05-27 01:57+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -17,16 +17,16 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: AKSubmission/forms.py:93
#: AKSubmission/forms.py:95
#, python-format
msgid "\"%(duration)s\" is not a valid duration"
msgstr "\"%(duration)s\" ist keine gültige Dauer"
#: AKSubmission/forms.py:159
#: AKSubmission/forms.py:155
msgid "Duration(s)"
msgstr "Dauer(n)"
#: AKSubmission/forms.py:161
#: AKSubmission/forms.py:157
msgid ""
"Enter at least one planned duration (in hours). If your AK should have "
"multiple slots, use multiple lines"
......