From cfa40c6918c21374f015a3e5ac53cf6a8a3c5e9c Mon Sep 17 00:00:00 2001 From: Vincent Mahnke Date: Mon, 10 Nov 2025 11:30:30 +0100 Subject: [PATCH] feat: Initial commit --- .gitignore | 62 ++++++ .gitlab-ci.yml | 17 ++ .update-locales | 37 ++++ LICENSE | 13 ++ MANIFEST.in | 3 + Makefile | 10 + README.rst | 46 +++++ pretix_congressschedule/__init__.py | 1 + pretix_congressschedule/api.py | 193 ++++++++++++++++++ pretix_congressschedule/apps.py | 43 ++++ .../locale/de/LC_MESSAGES/django.po | 19 ++ .../locale/de/LC_MESSAGES/djangojs.po | 19 ++ .../locale/de_Informal/.gitkeep | 0 .../locale/de_Informal/LC_MESSAGES/django.po | 19 ++ .../de_Informal/LC_MESSAGES/djangojs.po | 19 ++ pretix_congressschedule/locale/django.pot | 26 +++ pretix_congressschedule/locale/djangojs.pot | 32 +++ pretix_congressschedule/signals.py | 0 pretix_congressschedule/urls.py | 10 + pretixplugin.toml | 10 + pyproject.toml | 39 ++++ pytest.ini | 2 + setup.cfg | 46 +++++ setup.py | 3 + 24 files changed, 669 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100755 .update-locales create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 README.rst create mode 100644 pretix_congressschedule/__init__.py create mode 100644 pretix_congressschedule/api.py create mode 100644 pretix_congressschedule/apps.py create mode 100644 pretix_congressschedule/locale/de/LC_MESSAGES/django.po create mode 100644 pretix_congressschedule/locale/de/LC_MESSAGES/djangojs.po create mode 100644 pretix_congressschedule/locale/de_Informal/.gitkeep create mode 100644 pretix_congressschedule/locale/de_Informal/LC_MESSAGES/django.po create mode 100644 pretix_congressschedule/locale/de_Informal/LC_MESSAGES/djangojs.po create mode 100644 pretix_congressschedule/locale/django.pot create mode 100644 pretix_congressschedule/locale/djangojs.pot create mode 100644 pretix_congressschedule/signals.py create mode 100644 pretix_congressschedule/urls.py create mode 100644 pretixplugin.toml create mode 100644 pyproject.toml create mode 100644 pytest.ini create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cc7fbd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,62 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +.ropeproject/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +#Ipython Notebook +.ipynb_checkpoints diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..1608db5 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,17 @@ +pypi: + image: + name: pretix/ci-image + before_script: + - cat $PYPIRC > ~/.pypirc + - pip install -U pip uv + - uv pip install --system -U wheel setuptools twine build pretix-plugin-build check-manifest + script: + - python -m build + - check-manifest . + - twine check dist/* + - twine upload dist/* + only: + - pypi + artifacts: + paths: + - dist/ diff --git a/.update-locales b/.update-locales new file mode 100755 index 0000000..1a1cbc5 --- /dev/null +++ b/.update-locales @@ -0,0 +1,37 @@ +#!/bin/sh +COMPONENTS=pretix/pretix-plugin-congressschedule pretix/pretix-plugin-congressschedule-js +DIR=pretix_congressschedule/locale +# Renerates .po files used for translating the plugin +set -e +set -x + +# Lock Weblate +for c in $COMPONENTS; do + wlc lock $c; +done + +# Push changes from Weblate to GitHub +for c in $COMPONENTS; do + wlc commit $c; +done + +# Pull changes from GitHub +git pull --rebase + +# Update po files itself +make localegen + +# Commit changes +git add $DIR/*/*/*.po +git add $DIR/*.pot + +git commit -s -m "Update po files +[CI skip]" + +# Push changes +git push + +# Unlock Weblate +for c in $COMPONENTS; do + wlc unlock $c; +done diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4f1c8b5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright 2025 Vincent 'ViMaSter' Mahnke + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..2235582 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +recursive-include pretix_congressschedule/static * +recursive-include pretix_congressschedule/templates * +recursive-include pretix_congressschedule/locale * diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d4e8db5 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +all: localecompile +LNGS:=`find pretix_congressschedule/locale/ -mindepth 1 -maxdepth 1 -type d -printf "-l %f "` + +localecompile: + django-admin compilemessages + +localegen: + django-admin makemessages --keep-pot -i build -i dist -i "*egg*" $(LNGS) + django-admin makemessages -d djangojs --keep-pot -i build -i dist -i "*egg*" $(LNGS) + diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..cd7f8f2 --- /dev/null +++ b/README.rst @@ -0,0 +1,46 @@ +pretix-congressschedule +======================= + +This is a plugin for `pretix`_. It generates a `c3voc-schema`_ compatible `schedule.xml` endpoint for events. + +Accessing schedule.xml +---------------------- + +1. Create an `event-series`_ in pretix; a singular event or non-event shop will not work, as products won't have required start and end times associated with them + +2. Visit `/api/v1/event/{organizationSlug}/{eventSlug}/schedule.xml` and replace `{organizationSlug}` and `{eventSlug}` with the respective slugs + +3. Receive either a 200 status code with an XML document adhering to `schedule.xml.xsd`_ or a 400 error code with additional information inside `` + + +Development setup +^^^^^^^^^^^^^^^^^ + +1. Make sure that you have a working `pretix development setup`_. + +2. Clone this repository, eg to ``local/pretix-congressschedule``. + +3. Activate the virtual environment you use for pretix development. + +4. Execute ``pip install -e .`` within this directory to register this application with pretix's plugin registry. + +5. Execute ``make`` within this directory to compile translations. + +6. Restart your local pretix server. You can now use the plugin from this repository for your events by enabling it in + the 'plugins' tab in the settings. + + +License +------- + +Copyright 2025 Vincent 'ViMaSter' Mahnke + +Released under the terms of the Apache License 2.0 + + + +.. _pretix: https://github.com/pretix/pretix +.. _pretix development setup: https://docs.pretix.eu/en/latest/development/setup.html +.. _c3voc-schema: https://c3voc.de/wiki/schedule#schedule_xml +.. _schedule.xml.xsd: https://c3voc.de/schedule/schema.xsd +.. _event-series: https://docs.pretix.eu/guides/event-series/?h=dates#how-to-create-an-event-series diff --git a/pretix_congressschedule/__init__.py b/pretix_congressschedule/__init__.py new file mode 100644 index 0000000..5becc17 --- /dev/null +++ b/pretix_congressschedule/__init__.py @@ -0,0 +1 @@ +__version__ = "1.0.0" diff --git a/pretix_congressschedule/api.py b/pretix_congressschedule/api.py new file mode 100644 index 0000000..5f457cf --- /dev/null +++ b/pretix_congressschedule/api.py @@ -0,0 +1,193 @@ +from django.http import HttpResponse +from rest_framework import views +from pretix.base.models import Event, SubEvent +import xml.etree.ElementTree as ET +from collections import defaultdict +from datetime import timedelta +import uuid +import re + +from . import __version__ + +class CongressScheduleView(views.APIView): + def get(self, request, organizer, event, *args, **kwargs): + try: + ev = Event.objects.get(organizer__slug=organizer, slug=event) + except Event.DoesNotExist: + return HttpResponse(b'Event not found', status=404, content_type='application/xml') + + if not ev.has_subevents: + return HttpResponse( + b'Event is not an event-series: https://docs.pretix.eu/guides/event-series/?h=dates#how-to-create-an-event-series', + status=400, + content_type='application/xml' + ) + + subs = SubEvent.objects.filter(event=ev).order_by('date_from') + + root = ET.Element('schedule') + + gen = ET.SubElement(root, 'generator') + gen.set('name', 'pretix-congressschedule') + gen.set('version', __version__) + + try: + feed_url = request.build_absolute_uri() + ET.SubElement(root, 'url').text = feed_url + except Exception: + pass + + # Version string – keep simple and stable per event + ET.SubElement(root, 'version').text = f"{ev.slug}-v1" + + conf = ET.SubElement(root, 'conference') + conf_title = ev.name.localize(ev.settings.locale) if hasattr(ev.name, 'localize') else str(ev.name) + ET.SubElement(conf, 'title').text = conf_title or str(ev.slug) + acronym = f"{organizer}_{event}".lower() + ET.SubElement(conf, 'acronym').text = acronym + + # start/end/days based on subevents if available, else fall back to event + all_starts = [se.date_from for se in subs if se.date_from] + all_ends = [se.date_to for se in subs if se.date_to] + + if all_starts: + ET.SubElement(conf, 'start').text = min(all_starts).isoformat() + if all_ends: + ET.SubElement(conf, 'end').text = max(all_ends).isoformat() + + # days count – unique calendar days from subevents + unique_days = sorted({(se.date_from.date() if se.date_from else None) for se in subs} - {None}) + if unique_days: + ET.SubElement(conf, 'days').text = str(len(unique_days)) + + # time zone name – try Event.timezone or settings + tz_name = getattr(ev, 'timezone', None) or getattr(ev.settings, 'timezone', None) + if tz_name: + tz_text = tz_name if isinstance(tz_name, str) else str(tz_name) + ET.SubElement(conf, 'time_zone_name').text = tz_text + + # Group subevents into days and rooms + # days: {date -> {room_name -> [subevents]}} + days: dict = defaultdict(lambda: defaultdict(list)) + + def get_room_name(se): + # Try SubEvent.location if present, else fallback to `Main` + loc = getattr(se, 'location', None) + if hasattr(loc, 'localize'): + try: + txt = loc.localize(ev.settings.locale) + except Exception: + txt = str(loc) + else: + txt = str(loc) if loc else '' + return (txt or 'Main').strip() or 'Main' + + for se in subs: + if not se.date_from: + # Skip entries without a start + continue + day_key = se.date_from.date() + room = get_room_name(se) + days[day_key][room].append(se) + + # Emit elements in chronological order + for day_index, (day_date, rooms) in enumerate(sorted(days.items()), start=1): + # Compute day start/end from all events this day + starts = [se.date_from for r in rooms.values() for se in r if se.date_from] + ends = [se.date_to for r in rooms.values() for se in r if se.date_to] + day_start = min(starts) if starts else None + # If end is missing for any, approximate using +0 duration => start + if ends: + day_end = max(ends) + else: + day_end = (day_start + timedelta(minutes=0)) if day_start else None + + day_el = ET.SubElement(root, 'day') + if day_date: + day_el.set('date', day_date.isoformat()) + if day_start: + day_el.set('start', day_start.isoformat()) + if day_end: + day_el.set('end', day_end.isoformat()) + day_el.set('index', str(day_index)) + + # Emit containers + for room_name, events_in_room in sorted(rooms.items(), key=lambda x: x[0].lower()): + room_el = ET.SubElement(day_el, 'room') + room_el.set('name', room_name) + # Optional guid on room – stable UUID5 based on names + room_el.set('guid', str(uuid.uuid5(uuid.NAMESPACE_DNS, f"room:{organizer}:{event}:{room_name}"))) + + # Emit each in chronological order within the room + for se in sorted(events_in_room, key=lambda s: s.date_from or 0): + ev_el = ET.SubElement(room_el, 'event') + ev_el.set('id', str(se.pk)) + ev_el.set('guid', str(uuid.uuid5(uuid.NAMESPACE_DNS, f"subevent:{ev.pk}:{se.pk}"))) + + # Helper: localize strings + def _localize(val): + if hasattr(val, 'localize'): + try: + return val.localize(ev.settings.locale) + except Exception: + return str(val) + return str(val) if val is not None else '' + + # Required children according to schema + ET.SubElement(ev_el, 'room').text = room_name + title = _localize(se.name) + ET.SubElement(ev_el, 'title').text = title + ET.SubElement(ev_el, 'subtitle').text = '' + ET.SubElement(ev_el, 'type').text = 'subevent' + + # date (full datetime with TZ) + if se.date_from: + ET.SubElement(ev_el, 'date').text = se.date_from.isoformat() + + # start (HH:MM or HH:MM:SS) + if se.date_from: + ET.SubElement(ev_el, 'start').text = se.date_from.strftime('%H:%M') + + # duration from date_to - date_from + dur_txt = '00:00' + if se.date_from and se.date_to and se.date_to >= se.date_from: + delta: timedelta = se.date_to - se.date_from + total_seconds = int(delta.total_seconds()) + hours = total_seconds // 3600 + minutes = (total_seconds % 3600) // 60 + seconds = total_seconds % 60 + # prefer HH:MM if no seconds, else HH:MM:SS + if seconds == 0: + dur_txt = f"{hours:02d}:{minutes:02d}" + else: + dur_txt = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + ET.SubElement(ev_el, 'duration').text = dur_txt + + ET.SubElement(ev_el, 'abstract').text = '' + + # slug (pattern: "[a-z0-9_]{4,}-[a-z0-9\-_]{4,}") + def slugify(text: str) -> str: + text = (text or '').lower() + text = re.sub(r'\s+', '-', text) + text = re.sub(r'[^a-z0-9\-_]', '', text) + text = text.strip('-_') + return text or 'item' + + base = f"{organizer}_{event}".lower() + second = slugify(title) + if len(second) < 4: + second = f"{second}-{se.pk}" + ET.SubElement(ev_el, 'slug').text = f"{base}-{second}" + + # track – use room name as a simple track assignment + ET.SubElement(ev_el, 'track').text = slugify(room_name) or 'general' + + # Optional elements: keep minimal but include language if available + lang = getattr(ev.settings, 'locale', None) + if lang: + ET.SubElement(ev_el, 'language').text = str(lang) + + # Leave optional complex children (persons, recording, links, attachments) empty for now + + xml_bytes = ET.tostring(root, encoding='utf-8', xml_declaration=True) + return HttpResponse(xml_bytes, content_type='application/xml') \ No newline at end of file diff --git a/pretix_congressschedule/apps.py b/pretix_congressschedule/apps.py new file mode 100644 index 0000000..b756542 --- /dev/null +++ b/pretix_congressschedule/apps.py @@ -0,0 +1,43 @@ +from django.apps import AppConfig +from django.utils.functional import cached_property +from django.utils.translation import gettext_lazy + +from . import __version__ + + +class PassbookApp(AppConfig): + name = "pretix_congressschedule" + verbose_name = "Congress Schedule" + + class PretixPluginMeta: + name = gettext_lazy("Congress Schedule") + author = "Vincent Mahnke" + description = gettext_lazy("Provides passbook tickets for pretix") + category = "API" + visible = True + featured = True + version = __version__ + compatibility = "pretix>=4.17.0" + + def ready(self): + from . import signals # NOQA + + @cached_property + def compatibility_errors(self): + import shutil + + errs = [] + if not shutil.which("openssl"): + errs.append("The OpenSSL binary is not installed or not in the PATH.") + return errs + + @cached_property + def compatibility_warnings(self): + errs = [] + try: + from PIL import Image # NOQA + except ImportError: + errs.append( + "Pillow is not installed on this system, which is required for converting and scaling images." + ) + return errs diff --git a/pretix_congressschedule/locale/de/LC_MESSAGES/django.po b/pretix_congressschedule/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..0e651c1 --- /dev/null +++ b/pretix_congressschedule/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,19 @@ +# pretix-congressschedule +# Copyright (C) 2025 Vincent 'ViMaSter' Mahnke +# This file is distributed under the same license as the pretix-congressschedule package. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-16 15:52+0200\n" +"PO-Revision-Date: 2022-03-21 15:45+0000\n" +"Last-Translator: Vincent 'ViMaSter' Mahnke \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" diff --git a/pretix_congressschedule/locale/de/LC_MESSAGES/djangojs.po b/pretix_congressschedule/locale/de/LC_MESSAGES/djangojs.po new file mode 100644 index 0000000..7eceb08 --- /dev/null +++ b/pretix_congressschedule/locale/de/LC_MESSAGES/djangojs.po @@ -0,0 +1,19 @@ +# 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 , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-16 15:52+0200\n" +"PO-Revision-Date: 2017-05-10 13:48+0200\n" +"Last-Translator: Vincent 'ViMaSter' Mahnke \n" +"Language-Team: \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.0.1\n" diff --git a/pretix_congressschedule/locale/de_Informal/.gitkeep b/pretix_congressschedule/locale/de_Informal/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/pretix_congressschedule/locale/de_Informal/LC_MESSAGES/django.po b/pretix_congressschedule/locale/de_Informal/LC_MESSAGES/django.po new file mode 100644 index 0000000..775b229 --- /dev/null +++ b/pretix_congressschedule/locale/de_Informal/LC_MESSAGES/django.po @@ -0,0 +1,19 @@ +# pretix-congressschedule +# Copyright (C) 2025 Vincent 'ViMaSter' Mahnke +# This file is distributed under the same license as the pretix-congressschedule package. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-16 15:52+0200\n" +"PO-Revision-Date: 2022-03-21 15:45+0000\n" +"Last-Translator: Vincent 'ViMaSter' Mahnke \n" +"Language-Team: German (informal) \n" +"Language: de_Informal\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" \ No newline at end of file diff --git a/pretix_congressschedule/locale/de_Informal/LC_MESSAGES/djangojs.po b/pretix_congressschedule/locale/de_Informal/LC_MESSAGES/djangojs.po new file mode 100644 index 0000000..34aebb6 --- /dev/null +++ b/pretix_congressschedule/locale/de_Informal/LC_MESSAGES/djangojs.po @@ -0,0 +1,19 @@ +# 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 , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-16 15:52+0200\n" +"PO-Revision-Date: 2017-05-10 13:48+0200\n" +"Last-Translator: Vincent 'ViMaSter' Mahnke \n" +"Language-Team: \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.0.1\n" \ No newline at end of file diff --git a/pretix_congressschedule/locale/django.pot b/pretix_congressschedule/locale/django.pot new file mode 100644 index 0000000..a174392 --- /dev/null +++ b/pretix_congressschedule/locale/django.pot @@ -0,0 +1,26 @@ +# 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 , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-16 15:52+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: pretix_congressschedule/__init__.py:11 +msgid "Congress Schedule" +msgstr "" + +#: pretix_congressschedule/__init__.py:13 +msgid "Provides passbook tickets for pretix" +msgstr "" \ No newline at end of file diff --git a/pretix_congressschedule/locale/djangojs.pot b/pretix_congressschedule/locale/djangojs.pot new file mode 100644 index 0000000..b1c4970 --- /dev/null +++ b/pretix_congressschedule/locale/djangojs.pot @@ -0,0 +1,32 @@ +# 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 , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-16 15:52+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: pretix_congressschedule/static/pretix_congressschedule/geosuggest.js:4 +msgid "Loading suggested geolocations…" +msgstr "" + +#: pretix_congressschedule/static/pretix_congressschedule/geosuggest.js:13 +msgid "" +"Click on one of the following suggestions to fill in the coordinates " +"automatically:" +msgstr "" + +#: pretix_congressschedule/static/pretix_congressschedule/geosuggest.js:35 +msgid "Error while loading suggested geolocations." +msgstr "" diff --git a/pretix_congressschedule/signals.py b/pretix_congressschedule/signals.py new file mode 100644 index 0000000..e69de29 diff --git a/pretix_congressschedule/urls.py b/pretix_congressschedule/urls.py new file mode 100644 index 0000000..4f9c59d --- /dev/null +++ b/pretix_congressschedule/urls.py @@ -0,0 +1,10 @@ +from django.urls import path +from .api import CongressScheduleView + +urlpatterns = [ + path( + 'api/v1/event///schedule.xml', + CongressScheduleView.as_view(), + name='schedule-xml', + ), +] \ No newline at end of file diff --git a/pretixplugin.toml b/pretixplugin.toml new file mode 100644 index 0000000..7a0a5f0 --- /dev/null +++ b/pretixplugin.toml @@ -0,0 +1,10 @@ +# This file is used by the pretix team internally to coordinate releases of this plugin +[plugin] +package = "pretix-congressschedule" +modules = [ "pretix_congressschedule" ] +marketplace_name = "congressschedule" +pypi = true +repository_servers = { origin = "github.com", gitlab = "code.rami.io" } +tag_targets = [ "origin", "gitlab" ] +branch_targets = [ "origin/master", "gitlab/master", "f:gitlab/pypi" ] + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e194848 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,39 @@ +[project] +name = "pretix-congressschedule" +dynamic = ["version"] +description = "c3voc schedule-compatible schedule.xml endpoint" +readme = "README.rst" +requires-python = ">=3.9" +license = {file = "LICENSE"} +keywords = ["pretix"] +authors = [ + {name = "Vincent Mahnke", email = "pretix-congress@vincent.mahn.ke"}, +] +maintainers = [ + {name = "Vincent Mahnke", email = "pretix-congress@vincent.mahn.ke"}, +] + +[project.entry-points."pretix.plugin"] +congressschedule = "pretix_congressschedule:PretixPluginMeta" + +[project.entry-points."distutils.commands"] +build = "pretix_plugin_build.build:CustomBuild" + +[build-system] +requires = [ + "setuptools", + "pretix-plugin-build" +] + +[project.urls] +homepage = "https://git.hamburg.ccc.de/ViMaSter/pretix-congressschedule" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.dynamic] +version = {attr = "pretix_congressschedule.__version__"} + +[tool.setuptools.packages.find] +include = ["pretix*"] +namespaces = false diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..ccf8194 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +DJANGO_SETTINGS_MODULE=pretix.testutils.settings diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..906fa08 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,46 @@ +[flake8] +ignore = N802,W503,E402 +max-line-length = 160 +exclude = migrations,.ropeproject,static,_static,build,setup.py + +[isort] +combine_as_imports = true +default_section = THIRDPARTY +include_trailing_comma = true +known_third_party = pretix +known_standard_library = typing +multi_line_output = 3 +skip = setup.py +use_parentheses = True +force_grid_wrap = 0 +line_length = 88 +known_first_party = pretix_congressschedule + +[tool:pytest] +DJANGO_SETTINGS_MODULE = pretix.testutils.settings + +[coverage:run] +source = pretix_adyen +omit = */migrations/*,*/urls.py,*/tests/* + +[coverage:report] +exclude_lines = + pragma: no cover + def __str__ + der __repr__ + if settings.DEBUG + NOQA + NotImplementedError + +[check-manifest] +ignore = + .update-locales + .update-locales.sh + .gitlab-ci.yml + .install-hooks.sh + pretixplugin.toml + Makefile + pytest.ini + manage.py + tests/* + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6068493 --- /dev/null +++ b/setup.py @@ -0,0 +1,3 @@ +from setuptools import setup + +setup()