diff --git a/src/core/management/commands/create_conference.py b/src/core/management/commands/create_conference.py new file mode 100644 index 0000000000000000000000000000000000000000..60ca31482bbd1df8b99e6c3246820f572d983512 --- /dev/null +++ b/src/core/management/commands/create_conference.py @@ -0,0 +1,372 @@ +from datetime import datetime + +from zoneinfo import ZoneInfo + +from django.core.management import call_command +from django.core.management.base import BaseCommand + +from core.models import Assembly, Conference, StaticPage, StaticPageNamespace + +NS_INTRO = '_intro_' +INTRO_PAGES = { + 'assemblies': { + 'title': { + 'de': 'Was ist das hier?', + 'en': 'What is this place?', + }, + 'text': { + 'de': 'Hier findest du alle Assemblies, die dieses Jahr dabei sind.', + 'en': 'Here you can find all the assemblies taking part this year.', + }, + }, + 'assemblies_events': { + 'title': { + 'de': 'Assembly Vorträge', + 'en': 'Assembly Events', + }, + 'text': { + 'de': 'Schau dir an, was die Assemblies vorhaben!', + 'en': 'See what the assemblies are up to!', + }, + }, + 'badges': { + 'title': { + 'de': 'Was sind Badges?', + 'en': 'What are badges?', + }, + 'text': { + 'de': 'Hier findest du eine Liste der Badges, die von Assemblies erstellt wurden und die du während dieser Veranstaltung finden könntest.\r\nAssemblies können neue Badges im Backoffice erstellen!', # noqa: E501 + 'en': 'Here you can find a list of badges that assemblies have created and that you may find during this event.\r\nAssemblies can create new badges in the backoffice!', # noqa: E501 + }, + }, + 'projects': { + 'title': { + 'de': 'Was sind Projekte?', + 'en': 'What are projects?', + }, + 'text': { + 'de': 'Assemblies und auch Einzelpersonen stellen hier Projekte vor, an denen sie arbeiten. Schau dir die Liste an und füge vielleicht selbst eines hinzu?', # noqa: E501 + 'en': "Assemblies and individual people like to highlight projects they are working on. Here you'll find a list, consider adding your fancy project?", # noqa: E501 + }, + }, + 'reportcontent': { + 'title': { + 'de': 'DIES IST KEIN KONTAKTFORMULAR', + 'en': 'THIS IS NOT A CONTACT FORM', + }, + 'text': { + 'de': 'Diese Funktion ist dafür gedacht, bösartigen und/oder illegalen Inhalt zu melden. Sie ist nicht dafür gedacht, Änderungen im Wiki zu beantragen oder allgemeine Fragen zu beantworten. Dafür kontaktiere bitte die Orga. Danke!', # noqa: E501 + 'en': 'This function is intended to report malicious and/or illegal contents. It is not meant to request changes in wiki pages or ask questions. For that please contact the orga. Thanks!', # noqa: E501 + }, + }, + 'sos': { + 'title': { + 'de': 'Was ist das hier?', + 'en': 'What ist das hier?', + }, + 'text': { + 'de': 'Hier kannst du selbst organisierte Vorträge und Workshops sehen. Vielleicht möchtest du selbst etwas anbieten?', # noqa: E501 + 'en': 'Here you can find sessions and workshops which are hosted by individuals. You might host your own?', + }, + }, +} + + +def seed_conference(conf: Conference, year: int = 0, force: bool = False): + if Assembly.objects.filter(conference=conf).exists(): + raise ValueError('Conference already has assemblies?!') + + # create info block namespace and texts + StaticPageNamespace.objects.create(conference=conf, prefix=NS_INTRO, groups=['hubteam']) + for page_slug, page_data in INTRO_PAGES.items(): + for lang in ['de', 'en']: + title = page_data['title'][lang] + text = page_data['text'][lang] + page = StaticPage.objects.create( + conference=conf, + slug=f'{NS_INTRO}{page_slug}', + language=lang, + title=title, + protection=StaticPage.Protection.PERM, + privacy=StaticPage.Privacy.NONE, + ) + rev = page.revisions.create(body=text, title=title) + rev.set_public() + + # create SoS assembly + conf.self_organized_sessions_assembly = Assembly.objects.create( + conference=conf, + slug='sos', + name='Self Organized Sessions', + is_official=True, + state_assembly=Assembly.State.HIDDEN, + state_channel=Assembly.State.NONE, + ) + + # create navigation (top level) + nav_conf = conf.nav_items.create( + label_de='Konferenz', + title_de='Offizielle Informationen, Tipps & Tricks zur Veranstaltung', + label_en='Conference', + title_en='official information, tips & tricks for the event', + index=10, + ) + nav_visitors = conf.nav_items.create( + label_de='Besucher', + title_de='Nützliche Links', + label_en='visitors', + title_en='helpful links', + index=15, + ) + nav_my = conf.nav_items.create( + label_de='Mein Congress', + title_de='personalisiertes Erlebnis', + label_en='my congress', + title_en='personalized experience', + index=100, + ) + + entries = [ + # (parent, index, icon, label_de, label_en, title_de, title_en, url) + ( + nav_conf, + 10, + 'rocket-takeoff', + 'Willkommen', + 'Welcome', + 'Startseite', + 'start page', + 'conference://index', + ), + ( + nav_conf, + 19, + 'calendar-event', + 'Events (Jetzt)', + 'events (now)', + 'das Vortragsprogramm als Liste', + 'talks, workshops and other events in list view', + 'conference://fahrplan?mode=list#now', + ), + ( + nav_conf, + 20, + 'calendar-week', + 'Fahrplan', + 'Schedule', + 'das Vortragsprogramm', + 'talks, workshops and other events', + 'conference://fahrplan', + ), + ( + nav_conf, + 21, + 'person-video3', + 'Self-organized Sessions', + 'Self-organized Sessions', + 'Workshops oder Diskussionsrunden in Assemblies oder den Workshopräumen', + 'workshops or discussions in Assemblies or workshop rooms', + 'conference://sos', + ), + ( + nav_conf, + 22, + 'lightning', + 'Lightning Talks', + 'lightning talks', + 'Deine fünf Minuten des Ruhms!', + 'Give a short introduction on a topic', + 'https://c3lt.de/', + ), + ( + nav_conf, + 50, + 'camera-reels', + 'Streams', + 'Streams', + 'Live-Streams der Vorträge', + 'live streaming of talks', + 'https://streaming.media.ccc.de/%SLUG%/', + ), + ( + nav_conf, + 110, + 'book', + 'Wiki', + 'Wiki', + 'gemeinsames Wissen', + 'shared knowledge', + 'wiki://start', + ), + ( + nav_conf, + 111, + 'book', + 'Letzte Änderungen (Wiki)', + 'Recent changes (Wiki)', + 'gemeinsames Wissen', + 'shared knowledge', + 'conference://static_page_global_history', + ), + ( + nav_conf, + 120, + 'person-arms-up', + 'Assemblies', + 'Assemblies', + 'Gruppen von Personen die dabei sind', + 'groups of people you can meet', + 'conference://assemblies', + ), + ( + nav_conf, + 300, + 'lightbulb', + 'Projekte', + 'Projects', + None, + None, + 'conference://projects', + ), + ( + nav_conf, + 400, + 'award', + 'Badges', + 'Badges', + "Schnapp' sie dir alle!", + 'Gotta find them all!', + 'conference://badges', + ), + ( + nav_conf, + 9001, + 'wrench', + 'Maschinenraum', + 'Backoffice', + 'Assemblies, Events, etc. verwalten', + 'Manage Assemblies, Events, etc.', + 'conference://backoffice', + ), + ( + nav_my, + 10, + 'shield', + 'Meine Badges', + 'My Badges', + 'entdecke den Congress', + 'explore the congress', + 'conference://manage_badges', + ), + ( + nav_my, + 200, + 'stars', + 'Mein Fahrplan', + 'My Schedule', + 'favorisierte Vorträge', + 'your favorite events', + 'conference://fahrplan?mode=list&my=y&kind=all', + ), + ( + nav_visitors, + 10, + 'ticket-perforated', + 'Tickets', + 'Tickets', + 'Wie? Wann? Wo?', + 'how? when? where?', + 'https://events.ccc.de/congress/%YEAR%/infos/tickets.html', + ), + ( + nav_visitors, + 15, + 'buildings', + 'Gebäude', + 'Venue', + 'Wie benutzen wir das Gebäude', + 'How we use the venue', + 'wiki://venue', + ), + ( + nav_visitors, + 20, + 'clipboard-fill', + 'Bulletin Board', + 'Bulletin Board', + 'ein digitales schwarzes Brett: suche und finde Dinge', + 'a digital board: search & find stuff', + 'conference://board', + ), + ( + nav_visitors, + 30, + 'patch-question', + 'FAQ', + 'FAQ', + 'häufig gestellte Fragen', + 'frequently asked questions', + 'https://events.ccc.de/congress/%YEAR%/infos/fundamentals-and-faq.html', + ), + ] + + def customize(s): + return (s or '').replace('%SLUG%', conf.slug).replace('%YEAR%', str(year)) + + for parent, index, icon, label_de, label_en, title_de, title_en, url in entries: + if not year and '%YEAR%' in url: + continue + parent.children.create( + conference=conf, + index=index, + icon=icon, + label_de=customize(label_de), + label_en=customize(label_en), + title_de=customize(title_de), + title_en=customize(title_en), + url=customize(url), + ) + + +class Command(BaseCommand): + def add_arguments(self, parser): + parser.add_argument('slug', type=str, nargs='?') + parser.add_argument('--name', type=str) + parser.add_argument('--year', type=int) + parser.add_argument('--bootstrap', action='store_true') + + def handle(self, *args, **options): + if not (slug := options.get('slug')): + slug = input('SLUG of the new conference, e.g. "42c3": ') + if not (name := options.get('name')): + name = input('NAME of the new conference (long title): ') + if not (year := options.get('year')): + year = input("YEAR of the CCC congress (leave empty if it's something else): ") + year = int(year or '0') + + print(f'Creating conference {slug} ({name}) for year {year}') + + # load our bootstrap fixtures + if options.get('bootstrap'): + call_command('loaddata', 'bootstrap_auth_groups.json') + + # create the new conference + conf = Conference.objects.create( + slug=slug, + name=name, + is_public=True, + ) + + # for CCC Jahresendveranstaltung we can guess some timestamps from the current year alone + if year > 1970: + conf.start = datetime(year, 12, 27, 10, 0, 0, tzinfo=ZoneInfo('Europe/Berlin')) + conf.end = datetime(year, 12, 30, 18, 0, 0, tzinfo=ZoneInfo('Europe/Berlin')) + conf.registration_deadline = datetime(year, 12, 1, 0, 0, 0, tzinfo=ZoneInfo('Europe/Berlin')) + + # save everything + conf.save() + self.stdout.write(self.style.SUCCESS(f'Created conference {conf.pk}')) + + # fill in initial data + seed_conference(conf, year=year) + self.stdout.write(self.style.SUCCESS(f'Initial data set up for "{conf.slug}"'))