From 348627d7c3642f877cd897c19b5c9d480543fa1e Mon Sep 17 00:00:00 2001 From: alz Date: Tue, 6 Feb 2024 14:21:13 +0000 Subject: [PATCH 1/6] feat: add 2024 badges --- .gitignore | 4 + namensschilder/README.md | 19 + namensschilder/api_get.py | 69 ++ namensschilder/bin/convert2024.py | 615 ++++++++++++++++++ namensschilder/bin/create_extra_badges.py | 2 +- namensschilder/bin/workshoplisten.py | 6 +- namensschilder/create_namensschilder.sh | 23 + namensschilder/namensschilder.tex | 2 +- namensschilder/namensschilder2024.tex | 24 + .../namensschilder2024_commands.tex | 86 +++ namensschilder/namensschilder2024_innen.tex | 47 ++ .../namensschilder2024_sichtbar.tex | 41 ++ 12 files changed, 933 insertions(+), 5 deletions(-) create mode 100644 namensschilder/api_get.py create mode 100644 namensschilder/bin/convert2024.py create mode 100755 namensschilder/create_namensschilder.sh create mode 100644 namensschilder/namensschilder2024.tex create mode 100644 namensschilder/namensschilder2024_commands.tex create mode 100644 namensschilder/namensschilder2024_innen.tex create mode 100644 namensschilder/namensschilder2024_sichtbar.tex diff --git a/.gitignore b/.gitignore index 414abab..88643ed 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ !imgs/2023/fossgis2023_background.pdf !namensschilder/bin/ *.log +*.fls +*.fdb_latexmk *.aux *.pyc *.out @@ -14,3 +16,5 @@ *.synctex.gz.properties imgs/signature.png pretix.csv +namensschilder/workshopliste.tex +*.synctex(busy) diff --git a/namensschilder/README.md b/namensschilder/README.md index 49e20b3..4884d13 100644 --- a/namensschilder/README.md +++ b/namensschilder/README.md @@ -51,7 +51,26 @@ Dies LaTeX scripte lesen die CSV um ein PDF für die Aussen- und Innenseite zu e 5.## (optional) PDFs mixen +### CLI tool +[qpdf](https://packages.debian.org/bookworm/qpdf) ist hier sehr hilfreich weil automatisierbar ... + +``` +apt update && apt install qpdf +``` + +Zum verbinden der beiden Ausgangsdatein deren Pfad/Name angeben und bestätigen - herauskommt eine out.pdf die Vorder- und Innenseite beinhaltet. Beim druck auf duplex achten 😇 + +``` +qpdf --empty --collate=1 --pages namensschilder2024_sichtbar.pdf namensschilder2024_inn +en.pdf -- out.pdf +``` + +### GUI tools +#### pdf24 🐑 +https://help.pdf24.org/de/fragen/frage/pdf-per-reissverschlussverfahren-zusammenfuehren-sortieren/ + +#### PDF SAM 📎 Nun kann man noch beiden PDFs zu einem machen, indem jede 2. Seite, d.h. die Rückseite eines A4 Blattes, die Innenseite eines Badges darstellt. diff --git a/namensschilder/api_get.py b/namensschilder/api_get.py new file mode 100644 index 0000000..64536dd --- /dev/null +++ b/namensschilder/api_get.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +import requests +import json + +# URL-endpoints +BASEURL = "https://pretix.eu/api/v1/organizers/fossgis/events/" +EVENT_ID = "demo-2020" +# https://docs.pretix.eu/en/latest/api/resources/orders.html#get--api-v1-organizers-(organizer)-events-(event)-orders- +ORDER_URL = BASEURL + EVENT_ID + "/orders/" +# https://docs.pretix.eu/en/latest/api/resources/invoices.html#get--api-v1-organizers-(organizer)-events-(event)-invoices- +INVOICE_URL = BASEURL + EVENT_ID + "/invoices/" +# ????????????????? +NREI_URL = BASEURL + EVENT_ID + "/orders?identifier=dekodi_nrei" +# https://docs.pretix.eu/en/latest/api/resources/items.html#get--api-v1-organizers-(organizer)-events-(event)-items- +ITEMS_URL = BASEURL + EVENT_ID + "/items/" + +# Auth* +headers = { + "Authorization": "Token INSERTTOKENHERE" +} + + +def getOrders(): + # Get orders + orders = requests.get(ORDER_URL, headers=headers) + order_data = orders.json().get("results", []) + + # Save orders to a file + with open("./data/orders.json", "w") as orders_file: + json.dump(order_data, orders_file, indent=4) + + +def getInvoices(): + # Get invoices + invoices = requests.get(INVOICE_URL, headers=headers) + invoice_data = invoices.json().get("results", []) + + # Save invoices to a file + with open("./data/invoices.json", "w") as invoices_file: + json.dump(invoice_data, invoices_file, indent=4) + + +def getNREI(): + # Get nrei + nrei = requests.get(NREI_URL, headers=headers) + nrei_data = nrei.json().get("results", []) + + # Save nrei to a file + with open("./data/nrei.json", "w") as nrei_file: + json.dump(nrei_data, nrei_file, indent=4) + + +def getItems(): + # Get items/products + items = requests.get(ITEMS_URL, headers=headers) + items_data = items.json().get("results", []) + + # Save items to a file + with open("./data/items.json", "w") as items_file: + json.dump(items_data, items_file, indent=4) + + +# Call the functions to execute the code +getOrders() +getInvoices() +getNREI() +getItems() diff --git a/namensschilder/bin/convert2024.py b/namensschilder/bin/convert2024.py new file mode 100644 index 0000000..47265d2 --- /dev/null +++ b/namensschilder/bin/convert2024.py @@ -0,0 +1,615 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +import collections +import csv +import json +import pathlib +import random +import re +import sys +from typing import Dict, List + +PATH_JSON = './bin/2024_pretixdata.json' # Bestelldaten +PATH_NREI = './bin/2024_nrei.json' # 'Rechnungsdaten -> für Firmennamen +PATH_ITEMS = './data/items.json' # Produktdaten +PATH_BADGE_CSV = 'badges.D.csv' + +# if True -> generiert ein pseudodata.badges.csv mit Max & Maria Musterfrau data +PSEUDODATA: bool = False + +# hier ggf. eine Liste mit order codes nutzen um selektiv badges zu erstellen +# https://pretix.eu/control/event/fossgis/2023/orders// +# +ORDER_CODES = None +# ORDER_CODES = ['PCJ7N'] + +# if >0 -> beschränkt das aus der json generierte CSV auf CSV_LIMIT Zeilen. +# Gut um schnell zu testen ob das PDF sinnvoll aussieht +CSV_LIMIT: int = -1 + +# Auflistung IDs für Workshop Tickets +# + + +TICKET_IDs = [ + 416723, # Konferenzticket + 416724, # Konferenzticket - reduzierter Preis + 416725, # Konferenzticket Community + 416726, # Konferenzticket Beitragende x + 416727, # Konferenzticket Aussteller + 416728, # Konferenzticket für Helfende + 416739, # OSM-Samstag +] + +# Workshop IDs +WORKSHOP_IDs = [ + 451468, # PostgreSQL / PostGIS Workshop für Einsteiger + 451473, # QGIS Workshop + 451477, # Orchestrierung einer GDI über Container Images + 416762, # QGIS-Programmierung ohne Python-Vorkenntnisse + 453049, # Einführung GeoServer + 454304, # Geodaten Prozessieren mit GeoPandas + 454307, # Nahtlose Feldarbeit dank QField und QFieldCloud + 454308, # Webmapping und Geoverarbeitung: Turf.js + 454330, # Oberflächenklassifikation aus Luft- und Satellitenbildern mit Hilfe von actinia + 454337, # Webmapping mit Leaflet - in nur 90 Minuten! + 454338, # MapProxy im Praxiseinsatz + 454344, # Labeling mit QGIS + 454350, # Hands on Masterportal + 454353, # Datenschutz und geographische Informationen + 454358, # Mapbender Workshop mit Schwerpunkt auf Mapbender 4 + 454364, # QGIS Expressions Workshop: Overlay und Aggregate Funktionen nutzen + 454366, # Vom Desktop-GIS zum WebGIS - mit MapComponents und React + 454367, # Kartendrucke in QGIS mit Python automatisieren + 454368, # Die Open Database License (ODbL) der OpenStreetMap-Daten + 454369, # Keine Angst vor sperrigen Ausdrücken im QGIS: Der Workshop zur LiveDemo + 454370, # QGIS Beziehungen (Relationen) und ihre Widgets +] + +EXKURSIONEN_IDs = [ + 458277, # Exkursion Blankenese – Altes Land – Finkenwerder – Landungsbrücken (Dienstag, 14:45 Uhr) + 454377, # Einblick in die Kartensammlung der Staats- und Universitätsbibliothek Hamburg (Donnerstag, 9 Uhr) + 416720, # Exkursion Hamburger Unterwelten (Freitag, 18:00 Uhr) + 458278, # Exkursion Abendliche Hafenrundfahrt (Freitag, 17:45 Uhr) + 454378, # MILLERNTOUR! Stadionführung bei St. Pauli (Samstag, 14:00 Uhr) + 458279, # Exkursion – Stadtrundgang (Samstag, 09:15 Uhr) +] + +# Hier können typos korrigiert, Firmennamen gekürzt und vereinheitlicht werden +DELETE_FROM_NAMES = [ + re.compile(r'FD Vermesssung und Geodaten Stadt Hildesheim[ ]*'), + re.compile(r'Software Development[ ]*'), + re.compile(r'Web GIS Freelance[ ]*'), + re.compile(r'.* Consultants[ ]*'), + re.compile(r'.* GmbH[ ]*'), + re.compile(r'FH Aachen[ ]*'), + re.compile(r'NTI Deutschland.*'), +] +REPLACE_IN_COMPANIES = { + 'Bundesamt für Kartographie und Geodäsie': re.compile('(BKG|Bundesamt für Kartographie und Geodäsie)'), + 'WhereGroup GmbH': re.compile(r'WhereGrouo?p GmbH', re.I), + 'DB Systel GmbH': re.compile('DB Systel GmbH c/o Deutsche Bahn AG'), + 'Landesamt für Geoinformation und Landesvermessung Niedersachsen': re.compile( + r'LGLN|Landesamt für Geoinformation und Landesvermessung Niedersachsen', re.I), + 'Landesamt für Vermessung und Geobasisinformation Rheinland-Pfalz': re.compile( + r'Landesamt für Vermessung und Geobasisinformation Rheinland-Pfalz', re.I), + 'Landesamt für Geoinformation und Landentwicklung Baden-Württemberg': + re.compile(r'Landesamt für Geoinformation und Landentwicklung (Baden-Württemberg|BW)', re.I), + 'Landesvermessung und Geobasisinformation Brandenburg': re.compile('^LGB$'), + 'Staatsbibliothek zu Berlin': re.compile(r'staatsbibliothek zu berlin', re.I), + 'Umweltbundesamt (UBA)': re.compile(r'umweltbundesamt|\(UBA\)', re.I), + 'Stadt Leipzig': re.compile(r'Stadt Leipzig', re.I), + 'Technische Universität Chemnitz': re.compile('Technische Universität Chemnitz'), + 'Bezirksamt Tempelhof-Schöneberg von Berlin': re.compile(r'Bezirksamt Tempelhof-Schöneberg von Berlin', re.I), + 'DB Fahrwegdienste GmbH': re.compile(r'DB Fahrwegdienste GmbH', re.I), + 'Landesamt für Geoinformation \& Landesvermessung Niedersachsen': re.compile('LGLN'), + 'Leibniz-Zentrum für Agrarlandschaftsforschung (ZALF)': re.compile('ZALF'), + 'Deutsches Zentrum für Luft- und Raumfahrt (DLR)': re.compile('Deutsches Zentrum für Luft- und Raumfahrt'), +} + +# END SETTINGS + +PATH_JSON = pathlib.Path(PATH_JSON).resolve() +PATH_NREI = pathlib.Path(PATH_NREI).resolve() +PATH_ITEMS = pathlib.Path(PATH_ITEMS).resolve() +PATH_BADGE_CSV = pathlib.Path(PATH_BADGE_CSV).resolve() +PATH_ITEM_CSV = PATH_BADGE_CSV.parent / re.sub(r'\.csv$', '.Items.csv', PATH_BADGE_CSV.name) + + +class BadgeInfo(object): + """ + Alles Infos die in eine *.csv Zeile und mit einem Badge ausgedruckt werden sollen. + """ + + def __init__(self, + name: str = None, + company: str = None, + mail: str = None, + orderCode: str = None, + ticket: str = None, + notes: str = None): + self.order: str = orderCode + self.name: str = normalizeName(name) if isinstance(name, str) else None + self.nachname: str = extractSurname(self.name) if isinstance(self.name, str) else None + self.company: str = company + self.mail: str = mail + self.ticket: str = ticket + self.tl_name: str = None + self.tl_veroeff: bool = False + self.tl_erhalten: bool = False + self.essen: str = None + self.tshirt: str = None + self.av: bool = False + self.tb: bool = False + self.tb_adresse: str = None + self.osm_samstag: bool = False + self.osm_name: str = None + self.exkursionen: List[str] = [] + self.workshops: List[str] = [] + self.notes: str = notes # sonstiges + self.nickname: str = None + + + def id(self) -> str: + return f'{self.name}:{self.mail}' + + def __str__(self): + return f'Ticket:#{self.order},{self.nachname},{self.name}' + + +class csvDialect(csv.Dialect): + """Describe the usual properties of Unix-generated CSV files.""" + delimiter = ';' + quotechar = '"' + doublequote = True + skipinitialspace = False + lineterminator = '\n' + quoting = csv.QUOTE_MINIMAL + + +def normalizeName(name: str) -> str: + """ + + :param name: + :return: + """ + name = name.replace(", BSc", "") + if name.find(" (") > 0: + name = name[:name.find(" (")] + name = re.sub(r'Dipl\.-(Ing|Geogr|Geol)\.[ ]+]', '', name) + name = re.sub( + r'(FD Vermesssung und Geodaten Stadt Hildesheim|Staatsbibliothek zu Berlin|Development and Operations| / Sourcepole)[ ]*', + '', name) + if ',' in name: + name = ' '.join(reversed(re.split(r'[ ]*,[ ]*', name))) + return name + + +# escape LaTeX characters +# credits to https://stackoverflow.com/questions/16259923/how-can-i-escape-latex-special-characters-inside-django-templates +conv = { + '&': r'\&', + '%': r'\%', + '$': r'\$', + '#': r'\#', + '_': r'\_', + '{': r'\{', + '}': r'\}', + '~': r'\textasciitilde{}', + '^': r'\^{}', + '\\': r'\textbackslash{}', + '<': r'\textless{}', + '>': r'\textgreater{}', +} +rx_tex_escape = re.compile('|'.join(re.escape(str(key)) for key in sorted(conv.keys(), key=lambda item: - len(item)))) + + +def tex_escape(text): + """ + :param text: a plain text message + :return: the message escaped to appear correctly in LaTeX + """ + return rx_tex_escape.sub(lambda match: conv[match.group()], text) + + +def readCompanyNames(path: pathlib.Path) -> Dict[str, str]: + CN = {} + with open(path, 'r', encoding='utf-8') as f: + jsonData = json.load(f) + + for data in jsonData['Data']: + orderCode = data['Hdr']['OID'] + company = data['Hdr']['CN'] + CN[orderCode] = company + + return CN + + +def replace_strings(text: str, replacements: dict): + for newtext, oldtext in replacements.items(): + if isinstance(oldtext, str): + text = text.replace(oldtext, newtext) + if oldtext.search(text): + return newtext + return text + + +def extractSurname(name: str) -> str: + name = normalizeName(name) + split = name.split(" ") + return split[-1] + + +def extractFirstName(name: str) -> str: + name = normalizeName(name) + return name.split(' ')[0] + + +# print(json.dumps(data, indent=4)) + +def readEventItems(jsonData: dict) -> Dict[int, str]: + """ + Liest alle Event Items ein + :param jsonData: + :return: dict + """ + items = {} + for item in jsonData["event"]["items"]: + iid = item['id'] + iname = item['name'] + items[iid] = iname + for var in item["variations"]: + vid = var["id"] + assert vid not in items + items[vid] = f'{var["name"]}' + + items = collections.OrderedDict(sorted(items.items())) + return items + + +def readEventQuestions(jsonData: dict) -> Dict[int, str]: + questions = {} + for question in jsonData['event']['questions']: + qid = question['id'] + assert qid not in questions + questions[qid] = question['question'] + + return questions + + +def writeItems(items: Dict[int, str], path_csv: pathlib.Path): + path_csv = pathlib.Path(path_csv) + # print Items and write them into a CSV + with open(path_csv, 'w', encoding='utf-8', newline='') as f: + header = ['ItemID', 'Name'] + writer = csv.DictWriter(f, header) + writer.writeheader() + for key, value in items.items(): + writer.writerow(dict(ItemID=key, Name=value)) + + +def readPseudoBadgeInfos() -> Dict[str, BadgeInfo]: + names = ['Max Mustermann', 'Maria Musterfrau', + 'Max Power', 'Wiener Schnitzel mit Kartoffelsalat', + 'Erwin Gerwin', 'Arne-Unhold Obermeier'] + letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + workshops = ['Workshop 1', + 'Workshop 2 mit langen Namen ärgerlichen Sonderzeichen', + 'Workshop 3 mit äußerst langem Namen und CSV zerstörenden ;:,\t Zeichen'] + + exkursionen = ['Exkursion 1', + 'Exkursion 2 mit langen Namen ärgerlichen Sonderzeichen', + 'Exkursion 3 mit äußerst langem Namen und CSV zerstörenden ;:,\t Zeichen'] + + tickets = ['Konferenzticket Beitragende', 'Konferenzticket Community', 'Konferenzticket - reduzierter Preis', + 'Konferenzticket für Helfende'] + + essen = ['Vegan', 'Vegetarisch', 'Fleisch/Fisch'] + + companies = ['Firma In-der-Kürze-liegt die Würze GmbH mit dennoch langem Namen', 'Bundesamt für XY und Z', + 'Obelix GmbH & Co KG'] + + notes = ['', ''] + tshirts = [] + for schnitt in ['tailliert geschnitten', 'gerade geschnitten']: + for size in ['M', 'L', 'XL', '2XL', '3XL']: + tshirts.append(f'{schnitt} - {size}') + + BADGES = {} + + def rnd(options: List, multiple: bool = False): + if random.choice([True, False]): + if multiple: + return random.sample(options, random.choice(range(len(options)))) + else: + return random.choice(options) + + else: + return None + + for name in names: + badge = BadgeInfo() + badge.name = name + badge.order = ''.join(random.sample(letters, 5)) + badge.nachname = extractSurname(name) + badge.tshirt = rnd(tshirts) + badge.ticket = random.choice(tickets) + badge.av = random.choice([True, False]) + badge.tb = random.choice([True, False]) + badge.tb_adresse = f'Pseudo Adresses {badge.name}' + badge.osm_samstag = random.choice([True, False]) + badge.osm_name = extractFirstName(name).lower() + badge.mail = f'{badge.osm_name}{badge.nachname.lower()}@nomail.xyz' + badge.workshops = rnd(workshops, multiple=True) + badge.exkursionen = rnd(exkursionen, multiple=True) + badge.essen = rnd(essen) + badge.notes = rnd(notes) + badge.company = random.choice(companies) + badge.nickname = nickname + BADGES[name] = badge + return BADGES + + +def readBadgeInfos(jsonData: dict, companyNames: Dict[str, str] = None) -> Dict[str, BadgeInfo]: + BADGES: Dict[str, BadgeInfo] = {} + + ITEMS = readEventItems(jsonData) + QUESTIONS = readEventQuestions(jsonData) + + ORDERS_TO_CHECK: Dict[str, str] = {} + + # p = payed, s = storniert, n = nicht bezahlt, c = canceled + orders = [o for o in jsonData["event"]["orders"] if o['status'] not in ['s', 'c']] + + # 1. Create Badge for each ticket order + for o in orders: + code = o['code'] + if isinstance(ORDER_CODES, list) and code not in ORDER_CODES: + continue + for pos in o['positions']: + mail = pos['attendee_email'] + name = pos['attendee_name'] + questions = {qa['question']: qa['answer'] for qa in pos['answers']} + if pos['item'] in TICKET_IDs and isinstance(mail, str): + cn = CompanyNames.get(code, None) + if isinstance(cn, str): + cn = re.split(r'(, | \\ )', cn)[0] + badge = BadgeInfo(mail=mail, + name=questions.get(69219, name), + orderCode=code, + company=cn) + BADGES[badge.id()] = badge + + for cntOrder, order in enumerate(jsonData["event"]["orders"]): + # !eine Order kann mehrere Tickets enthalten + # !eine Order kann nur Online Tickets enthalten und wie brauchen wir hier nicht + + code = order['code'] # z.B. "LSVKC" + if isinstance(ORDER_CODES, list) and code not in ORDER_CODES: + continue + status = order['status'] + # p = payed, s = storniert, n = nicht bezahlt, c = canceled + if status in ['s', 'c']: + continue + assert status in ['p', 'n'], f'unhandled status {status}' + + badge: BadgeInfo = None + for cntPos, pos in enumerate(order["positions"]): + itemID = pos['item'] + itemName = ITEMS[itemID] + + if itemID in [ + 416717, # Online - Konferenzticket - reduzierter Preis + 416718, # Online - Konferenzticket Community + 416716, # Online - Konferenzticket + ]: + badge = None + continue + elif itemID in TICKET_IDs: + mail = pos['attendee_email'] + name = pos['attendee_name'] + badgeID = f'{name}:{mail}' + if badgeID in BADGES: + badge = BADGES[badgeID] + if badge.ticket is None: + badge.ticket = itemName + else: + s = "" + if not isinstance(badge, BadgeInfo): + continue + + itemID = pos['item'] + variationID = pos['variation'] + itemName = ITEMS[itemID] + variationName = ITEMS[variationID] if variationID else itemName + + # read questions + questions = {qa['question']: qa['answer'] for qa in pos['answers']} + + for qid, qanswer in questions.items(): + qname = QUESTIONS[qid] + + if qid in [69296, # Ich helfe... + 69291, # Ich bin... + 69295, # In dr FOSSGIS-Community bin ich aktiv ... + 69293, # Wo liegt dein Sourcecode? + 73483, # vollständige Adresse TB + ]: + continue + # OSM-Samstag? + elif itemID == 271553 and questions.get(71695, '') in \ + ['', # im Zweifel: nehmen wir an ja, Teilnehmer:in wird in Hamburg vor Ort sein + 'Ich werde in Hamburg vor Ort sein.']: + badge.osm_samstag = True + if badge.ticket is None: + badge.ticket = itemName + elif qid == 69219: # Name Teilnehmerliste + badge.tl_name = qanswer + elif qid == 100533: # Ich möchte meinen Namen und meine Email-Adresse in der Teilnehmer:innenliste veröffentlichen. + badge.tl_veroeff = str(qanswer).lower() in ['true'] + elif qid == 69294: # Name Englesystem + badge.engel = qanswer + elif qid == 109418: # Wie is dein Mappername? + badge.osm_name = qanswer + elif qid == 109418: # Wie is dein Nickname? + badge.nickname = qanswer + else: + s = "" + del qid, qname, qanswer + + ## Add other items + if itemID == 416735: # T-Shirt + badge.tshirt = variationName + elif itemID == 416736 : # Helfende T-Shirt: + badge.tshirt = variationName + elif itemID == 416734: # Ich möchte einen Tagungsband erhalten + badge.tb = True + elif itemID == 416734: # Ihre vollständige Versandadresse für gedruckten Tagungsband: + badge.tb_adresse = '' + elif itemID == 271558: # Ich möchte eine Teilnehmer:innenliste erhalten + badge.tl_erhalten = True + elif itemID in [ # Mittagessen [268219, 274360, 274773, 281276, 275242]: + 268219, # Mittagessen ( 3 x ) # x - fehlte, keine Bestellunge + 274360, # Mittagessen für Helfer # x - fehlte + 416730, # "Mittagessen (Mi, Do, Fr)" + 275242, # Mittagessennachbuchung + 281276, # Mittagessen für Helfende + ]: + + badge.essen = variationName + elif itemID == 451658: # Ja, ich nehme an der Abendveranstaltung teil. + badge.av = True + elif itemID in EXKURSIONEN_IDs: + badge.exkursionen.append(variationName) + elif itemID in WORKSHOP_IDs: + badge.workshops.append(variationName) + elif itemID == 416739: # OSM-Samstag + badge.osm_samstag = True + else: + s = "" + s = "" + for k, badge in BADGES.items(): + if badge.company in [None, '']: + if isinstance(badge.mail, str) and re.search(r'@(geo|student)?\.?hu-berlin\.de$', badge.mail): + badge.company = 'Humboldt-Universität zu Berlin' + + if len(ORDERS_TO_CHECK) > 0: + print('Überprüfen:') + for k, v in ORDERS_TO_CHECK.items(): + print(f'{k}: {v}', file=sys.stderr) + return BADGES + + +def writeBadgeCsv(badgeInfos: Dict[str, BadgeInfo], path_csv: pathlib.Path): + path_csv = pathlib.Path(path_csv) + + badgeInfos = sorted([p for p in badgeInfos.values()], key=lambda p: p.nachname) + with open(path_csv, 'w', encoding='utf-8', newline='') as f: + + # schreibe alle Attribute als CSV Spalte + p = badgeInfos[0] + header = [k for k in p.__dict__.keys() if not k.startswith('_')] + header.append('needs_check') + writer = csv.DictWriter(f, header, dialect=csvDialect) + writer.writeheader() + + cnt = 0 + for person in badgeInfos: + if CSV_LIMIT > 0 and cnt >= CSV_LIMIT: + break + data = {k: person.__dict__.get(k, None) for k in header} + for k in list(data.keys()): + v = data[k] + if isinstance(v, list): + latex = f'{len(v)}' + if len(v) > 0: + v = [tex_escape(line) for line in v] + if False: + latex += r'\\ -- ' + r' \\ -- '.join(v) + else: + # geht leider nicht, weil + latex += r' \begin{itemize} ' + latex += r' \item ' + r' \item '.join(v) + latex += r' \end{itemize}\leavevmode ' + v = latex + elif isinstance(v, str): + if k == 'company': + v = replace_strings(v, REPLACE_IN_COMPANIES) + v = tex_escape(v) + if k == 'name': + # Füge bei sehr langen Namen ein Leerzeichen ein + # damit auf dem Badge ein Zeilenumbruch entsteht + v = re.sub(r'(B\.?Sc|M\.?Sc|Dipl\.[- ]*(Geogr|Geol|Ing)\.?)[ ]+', '', v) + for rx in DELETE_FROM_NAMES: + v = rx.sub('', v) + v = re.sub(r'\(.+\)', '', v) + v = v.strip() + v = re.split(r'\|', v)[0] + if ',' in v: + print(f'Check "{v}"', file=sys.stderr) + data['needs_check'] = True + parts = re.split(r'[ ]+', v) + for i in range(len(parts)): + part = parts[i] + if len(part) > 15: + parts[i] = re.sub('-', '-""', part) + v = ' '.join(parts) + data[k] = v + writer.writerow(data) + + cnt += 1 + + +if __name__ == '__main__': + + if PSEUDODATA: + print('Create Badges with Pseudo-Data') + badges = readPseudoBadgeInfos() + PATH_BADGE_CSV = PATH_BADGE_CSV.parent / f'pseudodata.{PATH_BADGE_CSV.name}' + else: + # 1. lese JSON + with open(PATH_JSON, 'r', encoding='utf-8') as f: + jsonData = json.load(f) + + # 2. lese & schreibe items + variations aka Dinge die gebucht werden können + items = readEventItems(jsonData) + questions = readEventQuestions(jsonData) + print("Items:") + for key, value in items.items(): + print(f'{key}, # {value}') + writeItems(items, PATH_ITEM_CSV) + + print('\nQuestions:') + for key, value in questions.items(): + print(f'{key}, #{value}') + + if PATH_NREI.is_file(): + CompanyNames = readCompanyNames(PATH_NREI) + else: + CompanyNames = {} + + # 3. Lese Badge Data + badges = readBadgeInfos(jsonData, companyNames=CompanyNames) + + if False: + # füge Sondergäste hinzu + from create_extra_badges import extra_badges + + for i, b in enumerate(extra_badges.values()): + badges[f'guest_{i + 1}'] = b + + # Füge 10 leere Badges hinzu und + emptyBadges = 10 + + # Fülle A4 Blatt auf + while (len(badges) + emptyBadges) % 4 != 0: + emptyBadges += 1 + + for i in range(emptyBadges): + badges[f'empty_{i + 1}'] = BadgeInfo(name='') + + s = "" + + # 4. Schreibe badge CSV + writeBadgeCsv(badges, PATH_BADGE_CSV) + print("\nNumber of attendees: " + str(len(badges))) diff --git a/namensschilder/bin/create_extra_badges.py b/namensschilder/bin/create_extra_badges.py index 1e96e46..913e8e0 100644 --- a/namensschilder/bin/create_extra_badges.py +++ b/namensschilder/bin/create_extra_badges.py @@ -3,7 +3,7 @@ """ import pathlib from typing import Dict, List -from convert2023 import BadgeInfo, writeBadgeCsv +from convert2024 import BadgeInfo, writeBadgeCsv path = pathlib.Path('extra_badges.csv') diff --git a/namensschilder/bin/workshoplisten.py b/namensschilder/bin/workshoplisten.py index dab1bf1..755c6bc 100644 --- a/namensschilder/bin/workshoplisten.py +++ b/namensschilder/bin/workshoplisten.py @@ -3,10 +3,10 @@ import pathlib import re from typing import Dict, List -from convert2023 import TICKET_IDs, tex_escape +from convert2024 import TICKET_IDs, tex_escape -PATH_JSON = '2023_pretixdata.json' # Bestelldaten -PATH_NREI = '2023_nrei.json' # 'Rechnungsdaten -> für Firmennamen +PATH_JSON = './bin/2024_pretixdata.json' # Bestelldaten +PATH_NREI = './bin/2024_nrei.json' # 'Rechnungsdaten -> für Firmennamen PATH_CSV = 'workshopliste.csv' PATH_CSV = pathlib.Path(PATH_CSV).resolve() diff --git a/namensschilder/create_namensschilder.sh b/namensschilder/create_namensschilder.sh new file mode 100755 index 0000000..096dfa9 --- /dev/null +++ b/namensschilder/create_namensschilder.sh @@ -0,0 +1,23 @@ +# Diese Script erstellt ein A4 PDF +# Ein A4 Blatt ergibt 4 Badge-Streifen. +# Badge-Streifen = 2x A7 querformat zum zusammenklappen, +# so das aussen das sichtbare Namensschild und innen ggf.nötige Zusatzinfos stehen +# +# 1 PDF mit den A4 Vorderseiten (namensschilder_sichtbar.tex -> namensschilder_sichtbar.pdf) +# 2 PDF mit den A4 Innenseiten (namensschilder_innenseite.tex -> namensschilder_innenseite.pdf) +# und fügt deren seiten abwechselnd zuammen zum namensschilder2024.pdf, +# wo jede zweite 2 die Innenseite eines Badges darstellt +# CSV = die *.csv datei mit den nötigen Angaben für Aussen- und Innenseite +# set CSV=bin/badges.empty.csv +export CSV=badges.D.csv +echo "Converting JSON to CSV" +python3 bin/convert2024.py +echo "Sanitize csv" +sed -i 's/\"//g' badges.D.csv +echo "writing inside of badges" +pdflatex namensschilder2024_innen.tex +echo "writing outside of badges" +pdflatex namensschilder2024_sichtbar.tex +echo "combining in- and outside" +pdflatex namensschilder2024.tex +# qpdf --empty --collate=1 --pages namensschilder2024_sichtbar.pdf namensschilder2024_innen.pdf -- out.pdf \ No newline at end of file diff --git a/namensschilder/namensschilder.tex b/namensschilder/namensschilder.tex index 461d7ea..5564c84 100644 --- a/namensschilder/namensschilder.tex +++ b/namensschilder/namensschilder.tex @@ -91,7 +91,7 @@ \begin{document} \DTLsetseparator{;} -\DTLloaddb{CSV}{bin/pretix.csv} +%\DTLloaddb{CSV}{bin/pretix.csv} \DTLsort{Nachname}{CSV} \DTLforeach{CSV}{\person=Name,\type=Variant,\nickname=268184,\shirt=274773,\count=WSCount,\dialoge=271556,\tagungsband=271557} %\DTLforeach{CSV}{\person=Name,\nickname=268184} diff --git a/namensschilder/namensschilder2024.tex b/namensschilder/namensschilder2024.tex new file mode 100644 index 0000000..86d63cc --- /dev/null +++ b/namensschilder/namensschilder2024.tex @@ -0,0 +1,24 @@ +\documentclass[a4paper, 12pt]{letter} +\usepackage[total={210mm,297mm},top=0mm,left=0mm,includefoot]{geometry} +\usepackage{pdfpages} +\usepackage{pgffor} +\begin{document} +\pdfximage{namensschilder2024_innen.pdf} +\def\nbPages{\the\pdflastximagepages} + + +\ifdefined\PDFSichtbar +\else + \newcommand{\PDFSichtbar}{namensschilder2024_sichtbar.pdf} +\fi +\ifdefined\PDFInnen +\else + \newcommand{\PDFInnen}{namensschilder2024_innen.pdf} +\fi + +% setzt das finale PDF aus jeweil Vorder- und Innenseite zusammen +\foreach \i in {1,...,\nbPages} {% + \includepdf[pages=\i]{\PDFSichtbar} + \includepdf[pages=\i]{\PDFInnen} +} +\end{document} \ No newline at end of file diff --git a/namensschilder/namensschilder2024_commands.tex b/namensschilder/namensschilder2024_commands.tex new file mode 100644 index 0000000..36cda32 --- /dev/null +++ b/namensschilder/namensschilder2024_commands.tex @@ -0,0 +1,86 @@ +\ifdefined\BadgeCSV +\else + \newcommand{\BadgeCSV}{badges.D.csv} +\fi + + +\renewcommand{\ticketdefault}{}% + + +\newcommand{\striked}[3]{ + \ifthenelse{\equal{#3}{#2}}{ + \sout{#1} + }{ + #1 #2 + } +} + +\newcommand{\badgesichtbar}[4]{\ticket{% +% das fossgis2023_background.pdf liegt in A6 quer vor % +% und kann so komplett als Hintergrund Bild genutzt werden % + +% Breite DIN A 7 74mm x 105mm +\put(0,0){\includegraphics[width=105mm]{../imgs/2024/fossgis_background.pdf}} +\put(5mm,30mm){ + % in der \parbox werden lange Namen umgebrochen + % \centering durch \raggedleft oder \raggedright erstetze um + % zu prüfen ob die Seitenränder eingehalten werden + + \parbox{93mm}{ \vfill \raggedleft \fontsize{32}{45} + \textbf{#1} + %\textbf{ + %\begin{Form} + %\textbf{\TextField[multiline,value=foobar,width=10cm]{}} + %\end{Form} + %} + \normalsize \\ {#4} + } + + } + +\put(85mm,60mm){\parbox{10mm}{\raggedleft \footnotesize + \ifthenelse{\equal{#2}{True}}{AV\\}{ } + \ifthenelse{\equal{#3}{True}}{OS\\}{ } + +}} +}} + + + +\newcommand{\badgeinnenlinks}[8]{\ticket{ +\put(8,45){ + % in der \parbox werden auch lange namen umgebrochen + \parbox{8.5cm}{ + % dezente Farbe, damit diese infos nicht auf die Vorderseite durchscheinen + \color{gray} \raggedright \small + + %%Nachname,Vorname,Ticket,TShirt,TBand,TListe,AV,Mittag,WS,EX,OSM + Order: #1 \\ + Name: #2 \\ + Mail: #3 \\ + Ticket: #4 \\ + %Tagungsband: \ifthenelse{\equal{#5}{True}}{Ja}{---------} \\ + \striked{Tagungsband:}{ #5}{False} \\ + \striked{T-Shirt:}{ #6}{} \\ + \striked{Essen:}{ #7}{} \\ + Anmerkungen: #8 +% T-Shirt: \ifthenelse{\equal{#6}{}}{---------}{#6} \\ +% Essen: \ifthenelse{\equal{#7}{}}{---------}{#7} \\ + } + } +}} + +\newcommand{\badgeinnenrechts}[2]{\ticket{% +\put(2,50){ + % in der \parbox werden auch lange namen umgebrochen + \parbox{90mm}{ + % dezente Farbe, damit diese infos nicht auf die Vorderseite durchscheinen + \color{gray} \raggedright \small + %%Nachname,Vorname,Ticket,TShirt,TBand,TListe,AV,Mittag,WS,EX,OSM + Workshops: #1 \\ + Exkursionen: #2 \\ + %\begin{itemize} \item Orchestrierung einer GDI über Docker \item Mapbender - Einstieg in den Aufbau von WebGIS-Anwendungen \item React basierte WebGIS Clients mit MapComponents \& MapLibre-gl \end{itemize} + %\begin{itemize} \item item1 \item 2\end{itemize} + } + } +}} diff --git a/namensschilder/namensschilder2024_innen.tex b/namensschilder/namensschilder2024_innen.tex new file mode 100644 index 0000000..fd9a3fd --- /dev/null +++ b/namensschilder/namensschilder2024_innen.tex @@ -0,0 +1,47 @@ + +\documentclass[a4paper, 12pt]{letter} +\usepackage[total={210mm,297mm},top=0mm,left=0mm,includefoot]{geometry} +\usepackage[schilder, rowmode]{ticket} +\usepackage{graphicx,palatino} +\usepackage{xcolor} +\usepackage[utf8]{inputenc} +\usepackage{datatool} +\usepackage{ifthen} +\usepackage[ngerman]{babel} +\usepackage[sfdefault]{ClearSans} %% option 'sfdefault' activates Clear Sans as the default text font +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[normalem]{ulem} +\usepackage{enumitem} +\setlist[itemize]{leftmargin=5mm} +\setlist{nosep} + + %\overfullrule 10pt + \hyphenpenalty 1000 + \exhyphenpenalty 10000 + +%\renewcommand*\rmdefault{iwona} +\newcounter{ticketcount} +\renewcommand{\ticketdefault}{}% +\makeatletter +\@boxedtrue % Rahmen um Namensschild +\@emptycrossmarktrue% Falzmarken +\@cutmarktrue % Schnittmarken +\makeatother + +\begin{document} +% lädt u.a. \BadgeCSV +\include{namensschilder2024_commands.tex} +\DTLsetseparator{;} +\DTLloaddb{CSV}{\BadgeCSV} +% order;name;nachname;mail;ticket;tl_name;tl_veroeff;tl_erhalten; +% essen;tshirt;av;tb;tb_adresse;osm_samstag;osm_name;exkursionen;workshops +\DTLforeach{CSV}{\order=order, \name=name,\type=ticket,\tshirt=tshirt,\mail=mail, +\av=av,\essen=essen,\ws=workshops, \ex=exkursionen, \tb=tb, \notizen=notes} +{ + \typeout{Ticket \arabic{ticketcount}: \order "\name "} + \badgeinnenlinks{\order}{\name}{\mail}{\type}{\tb}{\tshirt}{\essen}{\notizen} + \badgeinnenrechts{\ws}{\ex} + \addtocounter{ticketcount}{1} +} +\end{document} diff --git a/namensschilder/namensschilder2024_sichtbar.tex b/namensschilder/namensschilder2024_sichtbar.tex new file mode 100644 index 0000000..a9415ae --- /dev/null +++ b/namensschilder/namensschilder2024_sichtbar.tex @@ -0,0 +1,41 @@ + +\documentclass[a4paper, 12pt]{letter} +\usepackage[total={210mm,297mm},top=0mm,left=0mm, bottom=0mm, top=0mm]{geometry} +\usepackage[schilder,rowmode]{ticket} +\usepackage{graphicx,palatino} +\usepackage{xcolor} +\usepackage{hyperref} +\usepackage[utf8]{inputenc} +\usepackage{datatool} +\usepackage{ifthen} +\usepackage[sfdefault]{ClearSans} %% option 'sfdefault' activates Clear Sans as the default text font +\usepackage[T1]{fontenc} + + %\overfullrule 10pt + \hyphenpenalty 1000 + \exhyphenpenalty 10000 + + +\renewcommand{\ticketdefault}{}% +\makeatletter +\@boxedfalse % Rahmen um Namensschild +\@emptycrossmarkfalse % Falzmarken +\@cutmarktrue % Schnittmarken +\@crossmarkfalse % Schnittmarken +\makeatother + + +\begin{document} +% lädt u.a. \BadgeCSV +\include{namensschilder2024_commands.tex} +\DTLsetseparator{;} +% % order;name;nachname;company;mail;ticket;tl_name;tl_veroeff;tl_erhalten; +% essen;tshirt;av;tb;tb_adresse;osm_samstag;osm_name;exkursionen;workshops;notes;needs_check +\DTLloaddb{CSV}{\BadgeCSV} + +\DTLforeach{CSV}{\name=name, \nickname=osm_name, \av=av, \osm=osm_samstag, \company=company} +{ + \badgesichtbar{\name}{\av}{\osm}{\company} + \badgesichtbar{\name}{\av}{\osm}{\company} +} +\end{document} From 11f4462036e07adc7a73c4ad01c83a1295fc1ec4 Mon Sep 17 00:00:00 2001 From: alz Date: Sun, 17 Mar 2024 20:45:31 +0000 Subject: [PATCH 2/6] =?UTF-8?q?finaler=20stand=20f=C3=BCr=2020240317?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- namensschilder/bin/convert2024.py | 5 ++- namensschilder/bin/create_extra_badges.py | 39 ++++++++++++++---- namensschilder/create_namensschilder.sh | 13 +++++- namensschilder/food.png | Bin 0 -> 15668 bytes .../namensschilder2024_commands.tex | 7 ++-- .../namensschilder2024_sichtbar.tex | 2 +- namensschilder/osm.png | Bin 0 -> 17329 bytes 7 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 namensschilder/food.png create mode 100644 namensschilder/osm.png diff --git a/namensschilder/bin/convert2024.py b/namensschilder/bin/convert2024.py index 47265d2..c679bfe 100644 --- a/namensschilder/bin/convert2024.py +++ b/namensschilder/bin/convert2024.py @@ -311,6 +311,7 @@ def readPseudoBadgeInfos() -> Dict[str, BadgeInfo]: notes = ['', ''] tshirts = [] + nickname = ['yolo', 'rofl', '1337', '1312'] for schnitt in ['tailliert geschnitten', 'gerade geschnitten']: for size in ['M', 'L', 'XL', '2XL', '3XL']: tshirts.append(f'{schnitt} - {size}') @@ -598,8 +599,8 @@ def writeBadgeCsv(badgeInfos: Dict[str, BadgeInfo], path_csv: pathlib.Path): for i, b in enumerate(extra_badges.values()): badges[f'guest_{i + 1}'] = b - # Füge 10 leere Badges hinzu und - emptyBadges = 10 + # Füge 30 leere Badges hinzu und + emptyBadges = 30 # Fülle A4 Blatt auf while (len(badges) + emptyBadges) % 4 != 0: diff --git a/namensschilder/bin/create_extra_badges.py b/namensschilder/bin/create_extra_badges.py index 913e8e0..6b764aa 100644 --- a/namensschilder/bin/create_extra_badges.py +++ b/namensschilder/bin/create_extra_badges.py @@ -8,14 +8,37 @@ path = pathlib.Path('extra_badges.csv') extra_badges: List[BadgeInfo] = [ - BadgeInfo(name='Josephine Auerbach', company='Humboldt-Universität zu Berlin', - notes='Eröffnungsgast'), - BadgeInfo(name='Prof. Dr. Christoph Schneider', company='Humboldt-Universität zu Berlin', - notes='Eröffnungsgast'), - BadgeInfo(name='Wolfgang Crom', company='Staatsbibliothek zu Berlin', - notes='Eröffnungsgast'), - BadgeInfo(name='Dr. Ralf Kleindiek', company='Senatsverwaltung für Inneres, Digitalisierung und Sport', - notes='Eröffnungsgast'), + BadgeInfo(name='Anna Zagorski', company='Umweltbundesamt (UBA)', + notes='Panelist:innen'), + BadgeInfo(name='Miriam Seyffarth', company='Open Source Business Alliance (OSBA)', + notes='Panelist:innen'), + BadgeInfo(name='Pierre Golz', company='FB Digitalisierung Stadt Herne', + notes='Panelist:innen'), + BadgeInfo(name='Alexander Smolianitski', company='Zentrum Digitale Souveränität (ZenDIS)', + notes='Panelist:innen'), + BadgeInfo(name='Prof. Heike Flämig', company='TUHH', + notes=''), + BadgeInfo(name='Laura Kuhlmann', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='Leonie Dittrich', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='Daniela Wagner', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='Jacqueline Maaß', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='Christoph Meyer', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='Ineke Jäger', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='', company='Mitarbeitende der TUHH', + notes=''), + BadgeInfo(name='', company='', + notes=''), + ] diff --git a/namensschilder/create_namensschilder.sh b/namensschilder/create_namensschilder.sh index 096dfa9..b935138 100755 --- a/namensschilder/create_namensschilder.sh +++ b/namensschilder/create_namensschilder.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Diese Script erstellt ein A4 PDF # Ein A4 Blatt ergibt 4 Badge-Streifen. # Badge-Streifen = 2x A7 querformat zum zusammenklappen, @@ -9,15 +11,24 @@ # wo jede zweite 2 die Innenseite eines Badges darstellt # CSV = die *.csv datei mit den nötigen Angaben für Aussen- und Innenseite # set CSV=bin/badges.empty.csv -export CSV=badges.D.csv + +export CSV=badges.D.csv # the real data +#export CSV=pseudodata.badges.D.csv # testdata +#export CSV="extra_badges.csv" # extrabadges +echo "CREATING BADGES for CSV: $CSV" + echo "Converting JSON to CSV" python3 bin/convert2024.py + echo "Sanitize csv" sed -i 's/\"//g' badges.D.csv + echo "writing inside of badges" pdflatex namensschilder2024_innen.tex + echo "writing outside of badges" pdflatex namensschilder2024_sichtbar.tex + echo "combining in- and outside" pdflatex namensschilder2024.tex # qpdf --empty --collate=1 --pages namensschilder2024_sichtbar.pdf namensschilder2024_innen.pdf -- out.pdf \ No newline at end of file diff --git a/namensschilder/food.png b/namensschilder/food.png new file mode 100644 index 0000000000000000000000000000000000000000..cb5439157c436d00bf6af6126bd6696ef16bdc5b GIT binary patch literal 15668 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 literal 0 HcmV?d00001 diff --git a/namensschilder/namensschilder2024_commands.tex b/namensschilder/namensschilder2024_commands.tex index 36cda32..dfd3118 100644 --- a/namensschilder/namensschilder2024_commands.tex +++ b/namensschilder/namensschilder2024_commands.tex @@ -1,5 +1,6 @@ \ifdefined\BadgeCSV \else + %%\newcommand{\BadgeCSV}{extra_badges.csv} \newcommand{\BadgeCSV}{badges.D.csv} \fi @@ -38,9 +39,9 @@ } -\put(85mm,60mm){\parbox{10mm}{\raggedleft \footnotesize - \ifthenelse{\equal{#2}{True}}{AV\\}{ } - \ifthenelse{\equal{#3}{True}}{OS\\}{ } +\put(85mm,60mm){\parbox{10mm}{\raggedleft + \ifthenelse{\equal{#2}{True}}{\includegraphics[width=10mm]{food.png}} { } %% CC BY-SA 4.0 https://openmoji.org/library/emoji-1F958/ + \ifthenelse{\equal{#3}{True}}{\includegraphics[width=10mm]{osm.png}} { } %% CC BY-SA 4.0 https://openmoji.org/library/emoji-E04A/ }} }} diff --git a/namensschilder/namensschilder2024_sichtbar.tex b/namensschilder/namensschilder2024_sichtbar.tex index a9415ae..3ced3cb 100644 --- a/namensschilder/namensschilder2024_sichtbar.tex +++ b/namensschilder/namensschilder2024_sichtbar.tex @@ -33,7 +33,7 @@ % essen;tshirt;av;tb;tb_adresse;osm_samstag;osm_name;exkursionen;workshops;notes;needs_check \DTLloaddb{CSV}{\BadgeCSV} -\DTLforeach{CSV}{\name=name, \nickname=osm_name, \av=av, \osm=osm_samstag, \company=company} +\DTLforeach{CSV}{\name=name, \av=av, \osm=osm_samstag, \company=company} { \badgesichtbar{\name}{\av}{\osm}{\company} \badgesichtbar{\name}{\av}{\osm}{\company} diff --git a/namensschilder/osm.png b/namensschilder/osm.png new file mode 100644 index 0000000000000000000000000000000000000000..f65802b46d9f29b58e4513b1976ba1acb2e0074d GIT binary patch literal 17329 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 Date: Mon, 18 Mar 2024 00:18:46 +0000 Subject: [PATCH 3/6] nickname fails --- namensschilder/namensschilder2024_sichtbar.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/namensschilder/namensschilder2024_sichtbar.tex b/namensschilder/namensschilder2024_sichtbar.tex index 3ced3cb..31f8e67 100644 --- a/namensschilder/namensschilder2024_sichtbar.tex +++ b/namensschilder/namensschilder2024_sichtbar.tex @@ -33,9 +33,9 @@ % essen;tshirt;av;tb;tb_adresse;osm_samstag;osm_name;exkursionen;workshops;notes;needs_check \DTLloaddb{CSV}{\BadgeCSV} -\DTLforeach{CSV}{\name=name, \av=av, \osm=osm_samstag, \company=company} +\DTLforeach{CSV}{\name=name, \av=av, \osm=osm_samstag, \company=company, \osm_name=osm_name, \nickname=nickname} { - \badgesichtbar{\name}{\av}{\osm}{\company} - \badgesichtbar{\name}{\av}{\osm}{\company} + \badgesichtbar{\name}{\av}{\osm}{\company}%\ifthenelse{\equal{\osm_name}{}}{}{ \osm_name } + \badgesichtbar{\name}{\av}{\osm}{\company}%\ifthenelse{\equal{\nickname}{}}{}{ \nickname } } \end{document} From 6e41473f74eb798700dff39c76ca583609e6a5d5 Mon Sep 17 00:00:00 2001 From: alz Date: Mon, 18 Mar 2024 00:20:09 +0000 Subject: [PATCH 4/6] fix: edgecase 416737, 451658 zweimal abendveranstaltung im pretix --- namensschilder/bin/convert2024.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/namensschilder/bin/convert2024.py b/namensschilder/bin/convert2024.py index c679bfe..498b2f7 100644 --- a/namensschilder/bin/convert2024.py +++ b/namensschilder/bin/convert2024.py @@ -450,7 +450,7 @@ def readBadgeInfos(jsonData: dict, companyNames: Dict[str, str] = None) -> Dict[ badge.tl_veroeff = str(qanswer).lower() in ['true'] elif qid == 69294: # Name Englesystem badge.engel = qanswer - elif qid == 109418: # Wie is dein Mappername? + elif qid == 100535: # Wie is dein Mappername? badge.osm_name = qanswer elif qid == 109418: # Wie is dein Nickname? badge.nickname = qanswer @@ -461,7 +461,7 @@ def readBadgeInfos(jsonData: dict, companyNames: Dict[str, str] = None) -> Dict[ ## Add other items if itemID == 416735: # T-Shirt badge.tshirt = variationName - elif itemID == 416736 : # Helfende T-Shirt: + elif itemID == 416736: # Helfende T-Shirt: badge.tshirt = variationName elif itemID == 416734: # Ich möchte einen Tagungsband erhalten badge.tb = True @@ -480,6 +480,8 @@ def readBadgeInfos(jsonData: dict, companyNames: Dict[str, str] = None) -> Dict[ badge.essen = variationName elif itemID == 451658: # Ja, ich nehme an der Abendveranstaltung teil. badge.av = True + elif itemID == 416737: # Ja, ich nehme an der Abendveranstaltung teil. + badge.av = True elif itemID in EXKURSIONEN_IDs: badge.exkursionen.append(variationName) elif itemID in WORKSHOP_IDs: From efbd2ebe4f2e92bf36de415fd86768d911f26f5c Mon Sep 17 00:00:00 2001 From: alz Date: Tue, 15 Oct 2024 19:57:58 +0200 Subject: [PATCH 5/6] =?UTF-8?q?uebergabe2025=F0=9F=A4=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * danke janine und hannes --- namensschilder/.dockerignore | 25 +++++++++++++++++++++++++ namensschilder/Dockerfile | 14 ++++++++++++++ namensschilder/docker-compose.yml | 11 +++++++++++ namensschilder/dockerhowto-helpls.md | 26 ++++++++++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 namensschilder/.dockerignore create mode 100644 namensschilder/Dockerfile create mode 100644 namensschilder/docker-compose.yml create mode 100644 namensschilder/dockerhowto-helpls.md diff --git a/namensschilder/.dockerignore b/namensschilder/.dockerignore new file mode 100644 index 0000000..7347a7f --- /dev/null +++ b/namensschilder/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/namensschilder/Dockerfile b/namensschilder/Dockerfile new file mode 100644 index 0000000..929ca40 --- /dev/null +++ b/namensschilder/Dockerfile @@ -0,0 +1,14 @@ +FROM ubuntu:24.10 +LABEL Name=fossgis-namensschilder Version=0.0.2 +RUN apt update && apt install -y \ + pandoc texlive texlive-fonts-extra texlive-bibtex-extra \ + texlive-latex-base texlive-latex-extra texlive-font-utils \ + texlive-lang-german locales curl && \ + apt clean && \ + rm -rf /var/lib/apt/lists/* + +RUN sed -i -e 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/' /etc/locale.gen && \ + dpkg-reconfigure --frontend=noninteractive locales && \ + update-locale LANG=de_DE.UTF-8 +ENV LANGUAGE=de_DE.UTF-8 LANG=de_DE.UTF-8 LC_ALL=de_DE.UTF-8 +WORKDIR /home/ubuntu diff --git a/namensschilder/docker-compose.yml b/namensschilder/docker-compose.yml new file mode 100644 index 0000000..8de3269 --- /dev/null +++ b/namensschilder/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3.4' + +services: + fossgis: + image: namensschilder + build: + context: . + dockerfile: ./Dockerfile + volumes: + - "./:/home/ubuntu/" + tty: true \ No newline at end of file diff --git a/namensschilder/dockerhowto-helpls.md b/namensschilder/dockerhowto-helpls.md new file mode 100644 index 0000000..ec7940a --- /dev/null +++ b/namensschilder/dockerhowto-helpls.md @@ -0,0 +1,26 @@ +# how to ? ! + +hannes und janine 🤝 axel +- 2023 [fossgis_accessories/namensschilder at master · fossgis/fossgis_accessories (github.com)](https://github.com/fossgis/fossgis_accessories/tree/master/namensschilder) +- 2024 [fossgis_accessories/namensschilder at master · axza/fossgis_accessories (github.com)](https://github.com/axza/fossgis_accessories/tree/master/namensschilder) +- 2025 [(https://tja.mal.schaun.was.weird)???](https://tja.mal.schaun.was.weird) + +## build image +``` +docker build --pull --rm -f "Dockerfile" -t "." +``` + +### run image (interactive -it) +``` +docker run --rm -it namensschilder:latest +``` + +## docker compose +``` +docker compose up -d +``` + +### attach to terminal in container: +``` +docker exec -it namensschilder sh +``` \ No newline at end of file From 28eda3547604d6a78dffd469e1e465ecddc8ed00 Mon Sep 17 00:00:00 2001 From: alz Date: Wed, 16 Oct 2024 09:18:49 +0200 Subject: [PATCH 6/6] =?UTF-8?q?more=20layers=20=F0=9F=AB=A0=20-=20is=20now?= =?UTF-8?q?=20a=20bit=20faster=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- namensschilder/Dockerfile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/namensschilder/Dockerfile b/namensschilder/Dockerfile index 929ca40..be663f9 100644 --- a/namensschilder/Dockerfile +++ b/namensschilder/Dockerfile @@ -1,9 +1,15 @@ FROM ubuntu:24.10 LABEL Name=fossgis-namensschilder Version=0.0.2 -RUN apt update && apt install -y \ - pandoc texlive texlive-fonts-extra texlive-bibtex-extra \ - texlive-latex-base texlive-latex-extra texlive-font-utils \ - texlive-lang-german locales curl && \ +RUN apt update +RUN apt install -y pandoc +RUN apt install -y texlive +RUN apt install -y texlive-fonts-extra +RUN apt install -y texlive-bibtex-extra +RUN apt install -y texlive-latex-base +RUN apt install -y texlive-latex-extra +RUN apt install -y texlive-font-utils +RUN apt install -y texlive-lang-german +RUN apt install -y curl locales && \ apt clean && \ rm -rf /var/lib/apt/lists/*