commit 4ed3ecef04ada27e8e7324af641c7d6ab554b78a Author: Ed Date: Tue Jul 11 21:32:10 2023 +0200 Initial commit. diff --git a/avatar.png b/avatar.png new file mode 100644 index 0000000..2b5307d Binary files /dev/null and b/avatar.png differ diff --git a/badge_azzurro.png b/badge_azzurro.png new file mode 100644 index 0000000..cc9fe85 Binary files /dev/null and b/badge_azzurro.png differ diff --git a/badge_giallo.png b/badge_giallo.png new file mode 100644 index 0000000..0efad9b Binary files /dev/null and b/badge_giallo.png differ diff --git a/badge_rosso.png b/badge_rosso.png new file mode 100644 index 0000000..661727f Binary files /dev/null and b/badge_rosso.png differ diff --git a/badge_verde.png b/badge_verde.png new file mode 100644 index 0000000..d9f84ee Binary files /dev/null and b/badge_verde.png differ diff --git a/badge_viola.png b/badge_viola.png new file mode 100644 index 0000000..b822a55 Binary files /dev/null and b/badge_viola.png differ diff --git a/ext.py b/ext.py new file mode 100644 index 0000000..6139513 --- /dev/null +++ b/ext.py @@ -0,0 +1,224 @@ +from dataclasses import dataclass +from sanic import Request, exceptions +import httpx +import re +from os.path import join +import json +from time import time + +@dataclass +class Order: + def __init__(self, data): + + self.time = time() + self.data = data + self.status = {'n': 'pending', 'p': 'paid', 'e': 'expired', 'c': 'canceled'}[self.data['status']] + + if not len(self.data['positions']): + self.status = 'canceled' + + self.code = data['code'] + self.pending_update = False + + self.has_card = False + self.sponsorship = None + self.has_early = False + self.has_late = False + + for p in self.data['positions']: + if p['item'] in [16, 38]: + self.position_id = p['id'] + self.position_positionid = p['positionid'] + self.answers = p['answers'] + self.barcode = p['secret'] + + if p['item'] == 17: + self.has_card = True + + if p['item'] == 19: + self.sponsorship = 'normal' if p['variation'] == 13 else 'super' + + if p['country']: + self.country = p['country'] + + if p['attendee_name']: + self.first_name = p['attendee_name_parts']['given_name'] + self.last_name = p['attendee_name_parts']['family_name'] + + if p['item'] == 20: + self.has_early = True + + if p['item'] == 21: + self.has_late = True + + self.total = float(data['total']) + self.fees = 0 + for fee in data['fees']: + self.fees += float(fee['value']) + + answers = ['payment_provider', 'shirt_size', 'birth_date', 'fursona_name', 'room_confirmed', 'room_id'] + + self.payment_provider = data['payment_provider'] + self.shirt_size = self.ans('shirt_size') + self.is_artist = True if self.ans('is_artist') != 'No' else False + self.is_fursuiter = True if self.ans('is_fursuiter') != 'No' else False + self.is_allergic = True if self.ans('is_allergic') != 'No' else False + self.propic_locked = self.ans('propic_locked') + self.propic = self.ans('propic') + self.birth_date = self.ans('birth_date') + self.name = self.ans('fursona_name') + self.room_id = self.ans('room_id') + self.room_confirmed = self.ans('room_confirmed') + self.room_name = self.ans('room_name') + self.pending_room = self.ans('pending_room') + self.pending_roommates = self.ans('pending_roommates').split(',') if self.ans('pending_roommates') else [] + self.room_members = self.ans('room_members').split(',') if self.ans('room_members') else [] + self.room_owner = (self.code == self.room_id) + self.room_secret = self.ans('room_secret') + + + def __getitem__(self, var): + return self.data[var] + + def ans(self, name): + for p in self.data['positions']: + for a in p['answers']: + if a['question_identifier'] == name: + if a['answer'] in ['True', 'False']: + return bool(a['answer'] == 'True') + return a['answer'] + return None + + async def edit_answer(self, name, new_answer): + found = False + self.pending_update = True + for key in range(len(self.answers)): + if self.answers[key]['question_identifier'] == name: + if new_answer != None: + print('EXISTING ANSWER UPDATE', name, '=>', new_answer) + self.answers[key]['answer'] = new_answer + found = True + else: + print('DEL ANSWER', name, '=>', new_answer) + del self.answers[key] + + break + + if (not found) and (new_answer is not None): + + async with httpx.AsyncClient() as client: + res = await client.get(join(base_url, 'questions/'), headers=headers) + res = res.json() + + for r in res['results']: + if r['identifier'] != name: continue + + print('ANSWER UPDATE', name, '=>', new_answer) + self.answers.append({ + 'question': r['id'], + 'answer': new_answer, + 'question_identifier': r['identifier'], + 'options': r['options'] + }) + + async def send_answers(self): + async with httpx.AsyncClient() as client: + res = await client.patch(join(base_url, f'orderpositions/{self.position_id}/'), headers=headers, json={'answers': self.answers}) + self.pending_update = False + self.time = -1 + +@dataclass +class Quotas: + def __init__(self, data): + self.data = data + self.capacity_mapping = { + 1: 16, + 2: 17, + 3: 18, + 4: 19, + 5: 20 + } + + def get_left(self, capacity): + for quota in self.data['results']: + if quota['id'] == self.capacity_mapping[capacity]: + return quota['available_number'] + +async def get_quotas(request: Request=None): + async with httpx.AsyncClient() as client: + res = await client.get(join(base_url, 'quotas/?order=id&with_availability=true'), headers=headers) + res = res.json() + + return Quotas(res) + +async def get_order(request: Request=None): + await request.receive_body() + return await request.app.ctx.om.get_order(request=request) + +class OrderManager: + def __init__(self): + self.cache = {} + self.order_list = [] + + def add_cache(self, order): + self.cache[order.code] = order + if not order.code in self.order_list: + self.order_list.append(order.code) + + def remove_cache(self, code): + if code in self.cache: + del self.cache[code] + self.order_list.remove(code) + + async def get_order(self, request=None, code=None, secret=None, cached=False): + + # Fill the cache on first load + if not self.cache: + p = 0 + + async with httpx.AsyncClient() as client: + while 1: + p += 1 + res = await client.get(join(base_url, f"orders/?page={p}"), headers=headers) + + if res.status_code == 404: break + + data = res.json() + for o in data['results']: + o = Order(o) + if o.status in ['canceled', 'expired']: continue + self.add_cache(Order(o)) + + # If a cached order is needed, just get it if available + if code and cached and code in self.cache and time()-self.cache[code].time < 3600: + return self.cache[code] + + # If it's a request, ignore all the other parameters and just get the order of the requestor + if request: + code = request.cookies.get("foxo_code") + secret = request.cookies.get("foxo_secret") + + if re.match('^[A-Z0-9]{5}$', code or '') and (secret is None or re.match('^[a-z0-9]{16,}$', secret)): + print('Fetching', code, 'with secret', secret) + + async with httpx.AsyncClient() as client: + res = await client.get(join(base_url, f"orders/{code}/"), headers=headers) + if res.status_code != 200: + if request: + raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.") + else: + self.remove_cache(code) + return None + + res = res.json() + + order = Order(res) + if order.status in ['canceled', 'expired']: + if request: + raise exceptions.Forbidden(f"Your order has been deleted. Contact support with your order identifier ({res['code']}) for further info.") + + self.add_cache(order) + + if request and secret != res['secret']: + raise exceptions.Forbidden("Your session has expired due to a token change. Please check your E-Mail for an updated link!") + return order diff --git a/flag.svg b/flag.svg new file mode 100644 index 0000000..20a8bfd --- /dev/null +++ b/flag.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/flags/at.svg b/flags/at.svg new file mode 100644 index 0000000..c282508 --- /dev/null +++ b/flags/at.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/flags/ca.svg b/flags/ca.svg new file mode 100644 index 0000000..f1b2c96 --- /dev/null +++ b/flags/ca.svg @@ -0,0 +1,4 @@ + + + + diff --git a/flags/ch.svg b/flags/ch.svg new file mode 100644 index 0000000..b42d670 --- /dev/null +++ b/flags/ch.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/flags/cz.svg b/flags/cz.svg new file mode 100644 index 0000000..7913de3 --- /dev/null +++ b/flags/cz.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/flags/de.svg b/flags/de.svg new file mode 100644 index 0000000..b08334b --- /dev/null +++ b/flags/de.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/flags/fi.svg b/flags/fi.svg new file mode 100644 index 0000000..470be2d --- /dev/null +++ b/flags/fi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/flags/fr.svg b/flags/fr.svg new file mode 100644 index 0000000..1be6191 --- /dev/null +++ b/flags/fr.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/flags/gb.svg b/flags/gb.svg new file mode 100644 index 0000000..dbac25e --- /dev/null +++ b/flags/gb.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/flags/hr.svg b/flags/hr.svg new file mode 100644 index 0000000..70115ae --- /dev/null +++ b/flags/hr.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flags/hu.svg b/flags/hu.svg new file mode 100644 index 0000000..baddf7f --- /dev/null +++ b/flags/hu.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/flags/it.svg b/flags/it.svg new file mode 100644 index 0000000..20a8bfd --- /dev/null +++ b/flags/it.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/flags/nl.svg b/flags/nl.svg new file mode 100644 index 0000000..4faaf49 --- /dev/null +++ b/flags/nl.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/flags/pl.svg b/flags/pl.svg new file mode 100644 index 0000000..0fa5145 --- /dev/null +++ b/flags/pl.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/flags/si.svg b/flags/si.svg new file mode 100644 index 0000000..223fc49 --- /dev/null +++ b/flags/si.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/flags/us.svg b/flags/us.svg new file mode 100644 index 0000000..73b6245 --- /dev/null +++ b/flags/us.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/gen_badges.py b/gen_badges.py new file mode 100644 index 0000000..4200c87 --- /dev/null +++ b/gen_badges.py @@ -0,0 +1,76 @@ +import httpx +import re +import json +import os +from ext import Order + +headers = {'Host': 'reg.furizon.net', 'Authorization': 'Token [REDACTED]'} +output = {} + +# uid starts from 2 because of the two hard-coded badge numbers +i = 2 + +# Furizon was 200 orders, plenty of pages to go through +for x in range(10): + r = httpx.get(f'https://reg.furizon.net/api/v1/organizers/furizon/events/beyond/orders/?page={x+1}', headers=headers) + if r.status_code == 404: break + + data = r.json() + + for r in data['results']: + badge = open('template.svg', 'r').read() + order = Order(r) + if not order: continue + if order.status != 'paid': continue + + # These orders had an hard-coded UID of 1 and 2 + if order.code == 'HWUC9': + uid = 2 + elif order.code == 'ZUESS': + uid = 1 + else: + i += 1 + uid = i + + output[uid] = (order.code, order.name) + + # Replace the various texts and colors inside of the svg + badge = badge.replace('#deadbe', {None: '#546e7a', 'normal': '#8e24aa', 'super': '#fb8c00'}[order.sponsorship]) + badge = badge.replace('#SPONSOR_STATUS', {None: 'Regular', 'normal': 'Sponsor', 'super': 'Supersponsor'}[order.sponsorship]) + badge = badge.replace('#ATTEND_NAME', order.name) + badge = badge.replace('#CODE', order.code) + badge = badge.replace('#UID', f'{uid:03}') + badge = badge.replace('flag.svg', f"../flags/{order.country.lower()}.svg") + + # Change the background of the badge depending on staff role + if order.ans('staff_role'): + if 'main staff' in order.ans('staff_role'): + badge = badge.replace('badge_viola.png', 'badge_rosso.png') + elif 'staff' in order.ans('staff_role'): + badge = badge.replace('badge_viola.png', 'badge_azzurro.png') + + # Change the background also based on sponsorship + elif order.sponsorship == 'normal': + badge = badge.replace('badge_viola.png', 'badge_viola.png') + elif order.sponsorship == 'super': + badge = badge.replace('badge_viola.png', 'badge_giallo.png') + else: + badge = badge.replace('badge_viola.png', 'badge_verde.png') + + # Fix paths so that they are correct + badge = badge.replace('badge_', '../badge_') + + # Add the propics + if order.propic: + badge = badge.replace('avatar.png', f"../propic/{order.propic}") + else: + badge = badge.replace('avatar.png', f"../avatar.png") + + # Save the badges + with open(f"output/{uid:03}_{order.code}.svg", 'w') as f: + f.write(badge) + +# Use inkscape to convert all badges to pdf and then create a unique file +os.system('inkscape --actions="export-type:pdf;export-do;" output/*.svg') +os.system('rm output/*.svg') +os.system('pdfunite output/* combined.pdf') diff --git a/template.svg b/template.svg new file mode 100644 index 0000000..d236f71 --- /dev/null +++ b/template.svg @@ -0,0 +1,127 @@ + + + +#ATTEND_NAME#UID#CODE