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
  • 520-akowner
  • 520-fix-event-wizard-datepicker
  • 520-fix-scheduling
  • 520-improve-scheduling
  • 520-improve-scheduling-2
  • 520-improve-submission
  • 520-improve-trackmanager
  • 520-improve-wall
  • 520-message-resolved
  • 520-status
  • 520-upgrades
  • add_express_interest_to_ak_overview
  • admin-production-color
  • bugfixes
  • csp
  • featire-ical-export
  • feature-ak-requirement-lists
  • feature-akslide-export-better-filename
  • feature-akslides
  • feature-better-admin
  • feature-better-cv-list
  • feature-colors
  • feature-constraint-checking
  • feature-constraint-checking-wip
  • feature-dashboard-history-button
  • feature-event-status
  • feature-event-wizard
  • feature-export-flag
  • feature-improve-admin
  • feature-improve-filters
  • feature-improved-user-creation-workflow
  • feature-interest-view
  • feature-mails
  • feature-modular-status
  • feature-plan-autoreload
  • feature-present-default
  • feature-register-link
  • feature-remaining-constraint-validation
  • feature-room-import
  • feature-scheduler-improve
  • feature-scheduling-2.0
  • feature-special-attention
  • feature-time-input
  • feature-tracker
  • feature-wiki-wishes
  • feature-wish-slots
  • feature-wizard-buttons
  • features-availabilities
  • fix-ak-times-above-folg
  • fix-api
  • fix-constraint-violation-string
  • fix-cv-checking
  • fix-default-slot-length
  • fix-default-slot-localization
  • fix-doc-minor
  • fix-duration-display
  • fix-event-tz-pytz-update
  • fix-history-interest
  • fix-interest-view
  • fix-js
  • fix-pipeline
  • fix-plan-timezone-now
  • fix-room-add
  • fix-scheduling-drag
  • fix-slot-defaultlength
  • fix-timezone
  • fix-translation-scheduling
  • fix-virtual-room-admin
  • fix-wizard-csp
  • font-locally
  • improve-admin
  • improve-online
  • improve-slides
  • improve-submission-coupling
  • interest_restriction
  • main
  • master
  • meta-debug-toolbar
  • meta-export
  • meta-makemessages
  • meta-performance
  • meta-tests
  • meta-tests-gitlab-test
  • meta-upgrades
  • mollux-master-patch-02906
  • port-availabilites-fullcalendar
  • qs
  • remove-tags
  • renovate/configure
  • renovate/django-4.x
  • renovate/django-5.x
  • renovate/django-bootstrap-datepicker-plus-5.x
  • renovate/django-bootstrap5-23.x
  • renovate/django-bootstrap5-24.x
  • renovate/django-compressor-4.x
  • renovate/django-debug-toolbar-4.x
  • renovate/django-registration-redux-2.x
  • renovate/django-simple-history-3.x
  • renovate/django-split-settings-1.x
  • renovate/django-timezone-field-5.x
100 results
Show changes
Showing
with 926 additions and 1 deletion
import datetime
import json
import os
import tempfile
from itertools import zip_longest
from django.contrib import messages
from django.db.models.functions import Now
from django.utils.dateparse import parse_datetime
from django.utils.translation import gettext_lazy as _
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.metaviews.admin import EventSlugMixin, IntermediateAdminView, IntermediateAdminActionView, AdminViewMixin
from AKModel.models import ConstraintViolation, Event, DefaultSlot, AKOwner
class UserView(TemplateView):
"""
View: Start page for logged in user
Will over a link to backend or inform the user that their account still needs to be confirmed
"""
template_name = "AKModel/user.html"
class ExportSlidesView(EventSlugMixin, IntermediateAdminView):
"""
View: Export slides to present AKs
Over a form to choose some settings for the export and then generate the PDF
"""
title = _('Export AK Slides')
form_class = SlideExportForm
def form_valid(self, form):
# pylint: disable=invalid-name
template_name = 'admin/AKModel/export/slides.tex'
# Settings
NEXT_AK_LIST_LENGTH = form.cleaned_data['num_next']
RESULT_PRESENTATION_MODE = form.cleaned_data["presentation_mode"]
SPACE_FOR_NOTES_IN_WISHES = form.cleaned_data["wish_notes"]
translations = {
'symbols': _("Symbols"),
'who': _("Who?"),
'duration': _("Duration(s)"),
'reso': _("Reso intention?"),
'category': _("Category (for Wishes)"),
'wishes': _("Wishes"),
}
def build_ak_list_with_next_aks(ak_list):
"""
Create a list of tuples cosisting of an AK and a list of upcoming AKs (list length depending on setting)
"""
next_aks_list = zip_longest(*[ak_list[i + 1:] for i in range(NEXT_AK_LIST_LENGTH)], fillvalue=None)
return [(ak, next_aks) for ak, next_aks in zip_longest(ak_list, next_aks_list, fillvalue=[])]
# Get all relevant AKs (wishes separately, and either all AKs or only those who should directly or indirectly
# be presented when restriction setting was chosen)
categories_with_aks, ak_wishes = self.event.get_categories_with_aks(wishes_seperately=True, filter_func=lambda
ak: not RESULT_PRESENTATION_MODE or (ak.present or (ak.present is None and ak.category.present_by_default)))
# Create context for LaTeX rendering
context = {
'title': self.event.name,
'categories_with_aks': [(category, build_ak_list_with_next_aks(ak_list)) for category, ak_list in
categories_with_aks],
'subtitle': _("AKs"),
"wishes": build_ak_list_with_next_aks(ak_wishes),
"translations": translations,
"result_presentation_mode": RESULT_PRESENTATION_MODE,
"space_for_notes_in_wishes": SPACE_FOR_NOTES_IN_WISHES,
}
source = render_template_with_context(template_name, context)
# Perform real compilation (run latex twice for correct page numbers)
with tempfile.TemporaryDirectory() as tempdir:
run_tex_in_directory(source, tempdir, template_name=self.template_name)
os.remove(f'{tempdir}/texput.tex')
pdf = run_tex_in_directory(source, tempdir, template_name=self.template_name)
# Show PDF file to the user (with a filename containing a timestamp to prevent confusions about the right
# version to use when generating multiple versions of the slides, e.g., because owners did last-minute changes
# to their AKs
timestamp = datetime.datetime.now(tz=self.event.timezone).strftime("%Y-%m-%d_%H_%M")
return PDFResponse(pdf, filename=f'{self.event.slug}_ak_slides_{timestamp}.pdf')
class CVMarkResolvedView(IntermediateAdminActionView):
"""
Admin action view: Mark one or multitple constraint violation(s) as resolved
"""
title = _('Mark Constraint Violations as manually resolved')
model = ConstraintViolation
confirmation_message = _("The following Constraint Violations will be marked as manually resolved")
success_message = _("Constraint Violations marked as resolved")
def action(self, form):
self.entities.update(manually_resolved=True)
class CVSetLevelViolationView(IntermediateAdminActionView):
"""
Admin action view: Set one or multitple constraint violation(s) as to level "violation"
"""
title = _('Set Constraint Violations to level "violation"')
model = ConstraintViolation
confirmation_message = _("The following Constraint Violations will be set to level 'violation'")
success_message = _("Constraint Violations set to level 'violation'")
def action(self, form):
self.entities.update(level=ConstraintViolation.ViolationLevel.VIOLATION)
class CVSetLevelWarningView(IntermediateAdminActionView):
"""
Admin action view: Set one or multitple constraint violation(s) as to level "warning"
"""
title = _('Set Constraint Violations to level "warning"')
model = ConstraintViolation
confirmation_message = _("The following Constraint Violations will be set to level 'warning'")
success_message = _("Constraint Violations set to level 'warning'")
def action(self, form):
self.entities.update(level=ConstraintViolation.ViolationLevel.WARNING)
class PlanPublishView(IntermediateAdminActionView):
"""
Admin action view: Publish the plan of one or multitple event(s)
"""
title = _('Publish plan')
model = Event
confirmation_message = _('Publish the plan(s) of:')
success_message = _('Plan published')
def action(self, form):
self.entities.update(plan_published_at=Now(), plan_hidden=False)
class PlanUnpublishView(IntermediateAdminActionView):
"""
Admin action view: Unpublish the plan of one or multitple event(s)
"""
title = _('Unpublish plan')
model = Event
confirmation_message = _('Unpublish the plan(s) of:')
success_message = _('Plan unpublished')
def action(self, form):
self.entities.update(plan_published_at=None, plan_hidden=True)
class DefaultSlotEditorView(EventSlugMixin, IntermediateAdminView):
"""
Admin view: Allow to edit the default slots of an event
"""
template_name = "admin/AKModel/default_slot_editor.html"
form_class = DefaultSlotEditorForm
title = _("Edit Default Slots")
def get_success_url(self):
return self.request.path
def get_initial(self):
initial = super().get_initial()
default_slots = [
{"id": s.id, "start": s.start_iso, "end": s.end_iso, "allDay": False}
for s in self.event.defaultslot_set.all()
]
initial['availabilities'] = json.dumps({
'availabilities': default_slots
})
return initial
def form_valid(self, form):
default_slots_raw = json.loads(form.cleaned_data['availabilities'])["availabilities"]
tz = self.event.timezone
created_count = 0
updated_count = 0
previous_slot_ids = set(s.id for s in self.event.defaultslot_set.all())
# Loop over inputs and update or add slots
for slot in default_slots_raw:
start = parse_datetime(slot["start"]).replace(tzinfo=tz)
end = parse_datetime(slot["end"]).replace(tzinfo=tz)
if slot["id"] != '':
slot_id = int(slot["id"])
if slot_id not in previous_slot_ids:
# Make sure only slots (currently) belonging to this event are edited
# (user did not manipulate IDs and slots have not been deleted in another session in the meantime)
messages.add_message(
self.request,
messages.WARNING,
_("Could not update slot {id} since it does not belong to {event}")
.format(id=slot['id'], event=self.event.name)
)
else:
# Update existing entries
previous_slot_ids.remove(slot_id)
original_slot = DefaultSlot.objects.get(id=slot_id)
if original_slot.start != start or original_slot.end != end:
original_slot.start = start
original_slot.end = end
original_slot.save()
updated_count += 1
else:
# Create new entries
DefaultSlot.objects.create(
start=start,
end=end,
event=self.event
)
created_count += 1
# Delete all slots not re-submitted by the user (and hence deleted in editor)
deleted_count = len(previous_slot_ids)
for d_id in previous_slot_ids:
DefaultSlot.objects.get(id=d_id).delete()
# Inform user about changes performed
if created_count + updated_count + deleted_count > 0:
messages.add_message(
self.request,
messages.SUCCESS,
_("Updated {u} slot(s). created {c} new slot(s) and deleted {d} slot(s)")
.format(u=str(updated_count), c=str(created_count), d=str(deleted_count))
)
return super().form_valid(form)
class AKsByUserView(AdminViewMixin, EventSlugMixin, DetailView):
"""
View: Show all AKs of a given user
"""
model = AKOwner
context_object_name = 'owner'
template_name = "admin/AKModel/aks_by_user.html"
import csv
import django.db
from django.apps import apps
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView
from AKModel.availability.models import Availability
from AKModel.forms import RoomForm, RoomBatchCreationForm
from AKModel.metaviews.admin import AdminViewMixin, EventSlugMixin, IntermediateAdminView
from AKModel.models import Room
class RoomCreationView(AdminViewMixin, CreateView):
"""
Admin view: Create a room
"""
form_class = RoomForm
template_name = 'admin/AKModel/room_create.html'
def get_success_url(self):
print(self.request.POST['save_action'])
if self.request.POST['save_action'] == 'save_add_another':
return reverse_lazy('admin:room-new')
if self.request.POST['save_action'] == 'save_continue':
return reverse_lazy('admin:AKModel_room_change', kwargs={'object_id': self.room.pk})
return reverse_lazy('admin:AKModel_room_changelist')
def form_valid(self, form):
self.room = form.save() # pylint: disable=attribute-defined-outside-init
# translatable string with placeholders, no f-string possible
# pylint: disable=consider-using-f-string
messages.success(self.request, _("Created Room '%(room)s'" % {'room': self.room}))
return HttpResponseRedirect(self.get_success_url())
class RoomBatchCreationView(EventSlugMixin, IntermediateAdminView):
"""
Admin action: Allow to create rooms in batch by inputing a CSV-formatted list of room details into a textbox
This offers the input form, supports creation of virtual rooms if AKOnline is active, too,
and users can specify that default availabilities (from event start to end) should be created for the rooms
automatically
"""
form_class = RoomBatchCreationForm
title = _("Import Rooms from CSV")
def get_success_url(self):
return reverse_lazy('admin:event_status', kwargs={'event_slug': self.event.slug})
def form_valid(self, form):
virtual_rooms_support = False
create_default_availabilities = form.cleaned_data["create_default_availabilities"]
created_count = 0
rooms_raw_dict: csv.DictReader = form.cleaned_data["rooms"]
# Prepare creation of virtual rooms if there is information (an URL) in the data and the AKOnline app is active
if apps.is_installed("AKOnline") and "url" in rooms_raw_dict.fieldnames:
virtual_rooms_support = True
# pylint: disable=import-outside-toplevel
from AKOnline.models import VirtualRoom
# Loop over all inputs
for raw_room in rooms_raw_dict:
# Gather the relevant information (most fields can be empty)
name = raw_room["name"]
location = raw_room["location"] if "location" in rooms_raw_dict.fieldnames else ""
capacity = raw_room["capacity"] if "capacity" in rooms_raw_dict.fieldnames else -1
try:
# Try to create a room (catches cases where the room name contains keywords or symbols that the
# database cannot handle (.e.g., special UTF-8 characters)
r = Room.objects.create(name=name,
location=location,
capacity=capacity,
event=self.event)
# and if necessary an associated virtual room, too
if virtual_rooms_support and raw_room["url"] != "":
VirtualRoom.objects.create(room=r,
url=raw_room["url"])
# If user requested default availabilities, create them
if create_default_availabilities:
a = Availability.with_event_length(event=self.event, room=r)
a.save()
created_count += 1
except django.db.Error as e:
messages.add_message(self.request, messages.WARNING,
_("Could not import room {name}: {e}").format(name=name, e=str(e)))
# Inform the user about the rooms created
if created_count > 0:
messages.add_message(self.request, messages.SUCCESS,
_("Imported {count} room(s)").format(count=created_count))
else:
messages.add_message(self.request, messages.WARNING, _("No rooms imported"))
return super().form_valid(form)
from django.apps import apps
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from AKModel.metaviews import status_manager
from AKModel.metaviews.admin import EventSlugMixin
from AKModel.metaviews.status import TemplateStatusWidget, StatusView
@status_manager.register(name="event_overview")
class EventOverviewWidget(TemplateStatusWidget):
"""
Status page widget: Event overview
"""
required_context_type = "event"
title = _("Overview")
template_name = "admin/AKModel/status/event_overview.html"
def render_status(self, context: {}) -> str:
return "success" if not context["event"].plan_hidden else "primary"
@status_manager.register(name="event_categories")
class EventCategoriesWidget(TemplateStatusWidget):
"""
Status page widget: Category information
Show all categories of the event together with the number of AKs belonging to this category.
Offers an action to add a new category.
"""
required_context_type = "event"
title = _("Categories")
template_name = "admin/AKModel/status/event_categories.html"
actions = [
{
"text": _("Add category"),
"url": reverse_lazy("admin:AKModel_akcategory_add"),
}
]
def render_title(self, context: {}) -> str:
# Store category count as instance variable for re-use in body
self.category_count = context['event'].akcategory_set.count() # pylint: disable=attribute-defined-outside-init
return f"{super().render_title(context)} ({self.category_count})"
def render_status(self, context: {}) -> str:
return "danger" if self.category_count == 0 else "primary"
@status_manager.register(name="event_rooms")
class EventRoomsWidget(TemplateStatusWidget):
"""
Status page widget: Category information
Show all rooms of the event.
Offers actions to add a single new room as well as for batch creation.
"""
required_context_type = "event"
title = _("Rooms")
template_name = "admin/AKModel/status/event_rooms.html"
actions = [
{
"text": _("Add Room"),
"url": reverse_lazy("admin:AKModel_room_add"),
}
]
def render_title(self, context: {}) -> str:
# Store room count as instance variable for re-use in body
self.room_count = context['event'].room_set.count() # pylint: disable=attribute-defined-outside-init
return f"{super().render_title(context)} ({self.room_count})"
def render_status(self, context: {}) -> str:
return "danger" if self.room_count == 0 else "primary"
def render_actions(self, context: {}) -> list[dict]:
actions = super().render_actions(context)
# 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(
{
"text": _("Import Rooms from CSV"),
"url": import_room_url,
}
)
return actions
@status_manager.register(name="event_aks")
class EventAKsWidget(TemplateStatusWidget):
"""
Status page widget: AK information
Show information about the AKs of this event.
Offers a long list of AK-related actions and also scheduling actions of AKScheduling is active
"""
required_context_type = "event"
title = _("AKs")
template_name = "admin/AKModel/status/event_aks.html"
def get_context_data(self, context) -> dict:
context["ak_count"] = context["event"].ak_set.count()
context["unscheduled_slots_count"] = context["event"].akslot_set.filter(start=None).count
return context
def render_actions(self, context: {}) -> list[dict]:
actions = [
{
"text": _("Scheduling"),
"url": reverse_lazy("admin:schedule", kwargs={"event_slug": context["event"].slug}),
},
]
if apps.is_installed("AKScheduling"):
actions.extend([
{
"text": _("AKs requiring special attention"),
"url": reverse_lazy("admin:special-attention", kwargs={"slug": context["event"].slug}),
},
])
if context["event"].ak_set.count() > 0:
actions.append({
"text": _("Enter Interest"),
"url": reverse_lazy("admin:enter-interest",
kwargs={"event_slug": context["event"].slug,
"pk": context["event"].ak_set.all().first().pk}
),
})
actions.extend([
{
"text": _("Edit Default Slots"),
"url": reverse_lazy("admin:default-slots-editor", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Manage ak tracks"),
"url": reverse_lazy("admin:tracks_manage", 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 for Wiki"),
"url": reverse_lazy("admin:ak_wiki_export", kwargs={"slug": context["event"].slug}),
},
{
"text": _("Export AK Slides"),
"url": reverse_lazy("admin:ak_slide_export", kwargs={"event_slug": context["event"].slug}),
},
]
)
return actions
@status_manager.register(name="event_requirements")
class EventRequirementsWidget(TemplateStatusWidget):
"""
Status page widget: Requirement information information
Show information about the requirements of this event.
Offers actions to add new requirements or to get a list of AKs having a given requirement.
"""
required_context_type = "event"
title = _("Requirements")
template_name = "admin/AKModel/status/event_requirements.html"
def render_title(self, context: {}) -> str:
# Store requirements count as instance variable for re-use in body
# pylint: disable=attribute-defined-outside-init
self.requirements_count = context['event'].akrequirement_set.count()
return f"{super().render_title(context)} ({self.requirements_count})"
def render_actions(self, context: {}) -> list[dict]:
return [
{
"text": _("Show AKs for requirements"),
"url": reverse_lazy("admin:event_requirement_overview", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Add Requirement"),
"url": reverse_lazy("admin:AKModel_akrequirement_add"),
},
]
class EventStatusView(EventSlugMixin, StatusView):
"""
View: Show a status dashboard for the given event
"""
title = _("Event Status")
provided_context_type = "event"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["site_url"] = reverse_lazy("dashboard:dashboard_event", kwargs={'slug': context["event"].slug})
return context
from django.contrib import admin
from AKOnline.models import VirtualRoom
@admin.register(VirtualRoom)
class VirtualRoomAdmin(admin.ModelAdmin):
"""
Admin interface for virtual room model
"""
model = VirtualRoom
list_display = ['room', 'event', 'url']
list_filter = ['room__event']
def get_readonly_fields(self, request, obj=None):
# Don't allow changing the room on existing virtual rooms
# Instead, a link to the room editing form will be displayed automatically
if obj:
return self.readonly_fields + ('room', )
return self.readonly_fields
from django.apps import AppConfig
class AkonlineConfig(AppConfig):
"""
App configuration (default -- only to set the app name)
"""
name = 'AKOnline'
from betterforms.multiform import MultiModelForm
from django.forms import ModelForm
from AKModel.forms import RoomForm
from AKOnline.models import VirtualRoom
class VirtualRoomForm(ModelForm):
"""
Form to create a virtual room
Should be used as part of a multi form (see :class:`RoomWithVirtualForm` below)
"""
class Meta:
model = VirtualRoom
# Show all fields except for room
exclude = ['room'] #pylint: disable=modelform-uses-exclude
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Make the URL field optional to allow submitting the multi form without creating a virtual room
self.fields['url'].required = False
class RoomWithVirtualForm(MultiModelForm):
"""
Combined form to create rooms and optionally virtual rooms
Multi-Form that combines a :class:`RoomForm` (from AKModel) and a :class:`VirtualRoomForm` (see above).
The form will always create a room on valid input
and may additionally create a virtual room if the url field of the virtual room form part is set.
"""
form_classes = {
'room': RoomForm,
'virtual': VirtualRoomForm
}
def save(self, commit=True):
objects = super().save(commit=False)
if commit:
room = objects['room']
room.save()
virtual = objects['virtual']
if virtual.url != "":
virtual.room = room
virtual.save()
else:
objects['virtual'] = None
return objects
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-16 16:30+0200\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"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: AKOnline/models.py:11
msgid "URL"
msgstr "URL"
#: AKOnline/models.py:11
msgid "URL to the room or server"
msgstr "URL zum Raum oder Server"
#: AKOnline/models.py:12
msgid "Room"
msgstr "Raum"
#: AKOnline/models.py:16
#: AKOnline/templates/admin/AKOnline/room_create_with_virtual.html:10
msgid "Virtual Room"
msgstr "Virtueller Raum"
#: AKOnline/models.py:17 AKOnline/views.py:38
msgid "Virtual Rooms"
msgstr "Virtuelle Räume"
#: AKOnline/templates/admin/AKOnline/room_create_with_virtual.html:11
msgid "Leave empty if that room is not virtual/hybrid."
msgstr "Leer lassen wenn der Raum nicht virtuell/hybrid ist"
#: AKOnline/views.py:25
#, python-format
msgid "Created Room '%(room)s'"
msgstr "Raum '%(room)s' angelegt"
#: AKOnline/views.py:28
#, python-format
msgid "Created related Virtual Room '%(vroom)s'"
msgstr "Verbundenen virtuellen Raum '%(vroom)s' angelegt"
# Generated by Django 3.0.6 on 2020-05-17 20:02
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('AKModel', '0033_AKOnline'),
]
operations = [
migrations.CreateModel(
name='VirtualRoom',
fields=[
('room_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True,
primary_key=True, serialize=False, to='AKModel.Room')),
('url', models.URLField(blank=True, help_text='URL to the room or server', verbose_name='URL')),
],
options={
'verbose_name': 'Virtual Room',
'verbose_name_plural': 'Virtual Rooms',
},
bases=('AKModel.room',),
),
]
# Generated by Django 4.1.5 on 2023-03-22 12:22
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
replaces = [('AKOnline', '0001_AKOnline'), ('AKOnline', '0002_rework_virtual'), ('AKOnline', '0003_rework_virtual_2'), ('AKOnline', '0004_rework_virtual_3'), ('AKOnline', '0005_rework_virtual_4')]
initial = True
dependencies = [
('AKModel', '0033_AKOnline'),
('AKModel', '0057_upgrades'),
]
operations = [
migrations.CreateModel(
name='VirtualRoom',
fields=[
('room', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='virtual', serialize=False, to='AKModel.room', verbose_name='Room')),
('url', models.URLField(blank=True, help_text='URL to the room or server', verbose_name='URL')),
],
options={
'verbose_name': 'Virtual Room',
'verbose_name_plural': 'Virtual Rooms',
},
),
]
# Generated by Django 4.1.5 on 2023-03-21 23:14
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('AKModel', '0057_upgrades'),
('AKOnline', '0001_AKOnline'),
]
operations = [
migrations.RenameModel(
'VirtualRoom',
'VirtualRoomOld'
),
]
# Generated by Django 4.1.5 on 2023-03-21 23:16
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('AKOnline', '0002_rework_virtual'),
]
operations = [
migrations.CreateModel(
name='VirtualRoom',
fields=[
('room', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True,
related_name='virtual', serialize=False, to='AKModel.room',
verbose_name='Room')),
('url', models.URLField(blank=True, help_text='URL to the room or server', verbose_name='URL')),
],
options={
'verbose_name': 'Virtual Room',
'verbose_name_plural': 'Virtual Rooms',
},
),
]
# Generated by Django 4.1.5 on 2023-03-21 23:21
from django.db import migrations
class Migration(migrations.Migration):
atomic = False
dependencies = [
('AKOnline', '0003_rework_virtual_2'),
]
def copy_rooms(apps, schema_editor):
VirtualRoomOld = apps.get_model('AKOnline', 'VirtualRoomOld')
VirtualRoom = apps.get_model('AKOnline', 'VirtualRoom')
for row in VirtualRoomOld.objects.all():
v = VirtualRoom(room_id=row.pk, url=row.url)
v.save()
operations = [
migrations.RunPython(
copy_rooms,
reverse_code=migrations.RunPython.noop,
elidable=True,
)
]
# Generated by Django 4.1.5 on 2023-03-21 23:41
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('AKOnline', '0004_rework_virtual_3'),
]
operations = [
migrations.DeleteModel(
name='VirtualRoomOld',
),
]
from django.db import models
from django.utils.translation import gettext_lazy as _
from AKModel.models import Room
class VirtualRoom(models.Model):
"""
Add details about a virtual or hybrid version of a room to it
"""
url = models.URLField(verbose_name=_("URL"), help_text=_("URL to the room or server"), blank=True)
room = models.OneToOneField(Room, verbose_name=_("Room"), on_delete=models.CASCADE,
related_name='virtual', primary_key=True)
class Meta:
verbose_name = _('Virtual Room')
verbose_name_plural = _('Virtual Rooms')
@property
def event(self):
"""
Property: Event this virtual room belongs to.
:return: Event this virtual room belongs to
:rtype: Event
"""
return self.room.event
def __str__(self):
return f"{self.room.title}: {self.url}"
{% extends "admin/AKModel/room_create.html" %}
{% load tags_AKModel %}
{% load i18n %}
{% load django_bootstrap5 %}
{% block form_details %}
{% bootstrap_form form.room %}
<h3>{% trans "Virtual Room" %}</h3>
<p>{% trans "Leave empty if that room is not virtual/hybrid." %}</p>
{% bootstrap_form form.virtual %}
{% endblock %}
{% load i18n %}
<ul>
{% for room in event.room_set.all %}
{% if room.virtual and room.virtual.url %}
<li>
<a href="{% url 'admin:AKOnline_virtualroom_change' room.pk %}">{{ room }} ({{ room.virtual.url | truncatechars:30 }})</a>
</li>
{% endif %}
{% endfor %}
</ul>
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.utils.translation import gettext_lazy as _
from AKModel.metaviews import status_manager
from AKModel.metaviews.status import TemplateStatusWidget
from AKModel.views.room import RoomCreationView
from AKOnline.forms import RoomWithVirtualForm
class RoomCreationWithVirtualView(RoomCreationView):
"""
View to create both rooms and optionally virtual rooms by filling one form
"""
form_class = RoomWithVirtualForm
template_name = 'admin/AKOnline/room_create_with_virtual.html'
room = None
def form_valid(self, form):
# This will create the room and additionally a virtual room if the url field is not blank
# objects['room'] will always a room instance afterwards, objects['virtual'] may be empty
objects = form.save()
self.room = objects['room']
# Create a (translated) success message containing information about the created room
messages.success(self.request, _("Created Room '%(room)s'" % {'room': objects['room']})) #pylint: disable=consider-using-f-string, line-too-long
if objects['virtual'] is not None:
# Create a (translated) success message containing information about the created virtual room
messages.success(self.request, _("Created related Virtual Room '%(vroom)s'" % {'vroom': objects['virtual']})) #pylint: disable=consider-using-f-string, line-too-long
return HttpResponseRedirect(self.get_success_url())
@status_manager.register(name="event_virtual_rooms")
class EventVirtualRoomsWidget(TemplateStatusWidget):
"""
Status page widget to contain information about all virtual rooms belonging to the given event
"""
required_context_type = "event"
title = _("Virtual Rooms")
template_name = "admin/AKOnline/status/event_virtual_rooms.html"
# Register your models here.