diff --git a/boops.py b/README.md similarity index 100% rename from boops.py rename to README.md diff --git a/app.py b/app.py index bc5368d..8bb944b 100644 --- a/app.py +++ b/app.py @@ -29,12 +29,9 @@ from export import bp as export_bp from stats import bp as stats_bp from api import bp as api_bp from carpooling import bp as carpooling_bp -from nfc import bp as nfc_bp from checkin import bp as checkin_bp -from money import bp as money_bp -from boop import bp as boop_bp -app.blueprint([room_bp, karaoke_bp, propic_bp, export_bp, stats_bp, api_bp, carpooling_bp, nfc_bp, checkin_bp, money_bp, boop_bp]) +app.blueprint([room_bp, karaoke_bp, propic_bp, export_bp, stats_bp, api_bp, carpooling_bp, checkin_bp]) @app.exception(exceptions.SanicException) async def clear_session(request, exception): @@ -57,15 +54,9 @@ async def main_start(*_): log.info("Cache fill done!") app.ctx.nfc_counts = sqlite3.connect('data/nfc_counts.db') - app.ctx.boop = sqlite3.connect('data/boop.db') - app.ctx.money = sqlite3.connect('data/money.db') - app.ctx.money.row_factory = sqlite3.Row app.ctx.login_codes = {} - app.ctx.nfc_reads = {} - app.ctx.boops = Queue() - app.ctx.tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=True) app.ctx.tpl.globals.update(time=time) app.ctx.tpl.globals.update(PROPIC_DEADLINE=PROPIC_DEADLINE) @@ -80,7 +71,7 @@ async def gen_barcode(request, code): return raw(img.getvalue(), content_type="image/png") -@app.route("/furizon/beyond/order///open/") +@app.route(f"/{ORGANIZER}/{EVENT_NAME}/order///open/") async def redirect_explore(request, code, secret, order: Order, secret2=None): r = redirect(app.url_for("welcome")) @@ -89,6 +80,7 @@ async def redirect_explore(request, code, secret, order: Order, secret2=None): if not order: async with httpx.AsyncClient() as client: res = await client.get(join(base_url, f"orders/{code}/"), headers=headers) + print(res.json()) if res.status_code != 200: raise exceptions.NotFound("This order code does not exist. Check that your order wasn't deleted, or the link is correct.") diff --git a/boop.py b/boop.py deleted file mode 100644 index a619384..0000000 --- a/boop.py +++ /dev/null @@ -1,86 +0,0 @@ -from sanic import Blueprint, exceptions, response -from time import time -from asyncio import Future -import asyncio -from datetime import datetime -from asyncio import Queue -from random import randint -from boop_process import boop_process - -bp = Blueprint("boop", url_prefix="/boop") - -bp.ctx.boopbox_queue = {'a': Queue(), 'b': Queue(), 'c': Queue()} -bp.ctx.busy = {'a': False, 'b': False, 'c': False} -bp.ctx.last_tag = {'a': None, 'b': None, 'c': None} -bp.ctx.repeats = {'a': None, 'b': None, 'c': None} - -def enable_nfc(enable, boopbox_id): - log.info('NFC is ' + ('enabled' if enable else 'disabled')) - app.ctx.queue.put_nowait({'_': 'nfc', 'enabled': enable}) - app.ctx.nfc_enabled = enable; - -@bp.get("/refresh") -async def refresh_boops(request): - await boop_process(request.app.ctx.om.cache.values(), request.app.ctx.boop) - return response.text('ok') - -@bp.get("/") -async def show_boopbox(request): - tpl = request.app.ctx.tpl.get_template('boopbox.html') - return response.html(tpl.render()) - -@bp.get("/getqueue/") -async def boop_queue(request, boopbox_id): - - items = [] - queue = bp.ctx.boopbox_queue[boopbox_id] - while 1: - try: - item = queue.get_nowait() - except asyncio.queues.QueueEmpty: - if items: - break - - # Try one last time to get a task, then fail. - bp.ctx.busy[boopbox_id] = False - try: - item = await asyncio.wait_for(queue.get(), timeout=5) - except asyncio.exceptions.TimeoutError: - break - - items.append(item) - - if len(items): - bp.ctx.busy[boopbox_id] = True - return response.json(items) - -@bp.post("/read") -async def handle_boop(request): - payload = request.json - queue = bp.ctx.boopbox_queue[payload['boopbox_id']] - - if bp.ctx.busy[payload['boopbox_id']]: return response.text('busy') - - - await queue.put({'_': 'play', 'src': f"/res/snd/error.wav"}) - - if bp.ctx.last_tag[payload['boopbox_id']] == payload['id']: - bp.ctx.repeats[payload['boopbox_id']] += 1 - else: - bp.ctx.last_tag[payload['boopbox_id']] = payload['id'] - bp.ctx.repeats[payload['boopbox_id']] = 0 - - if bp.ctx.repeats[payload['boopbox_id']] > 5: - await queue.put({'_': 'play', 'src': f"/res/snd/ratelimit.wav"}) - await queue.put({'_': 'bye'}) - return response.text('ok') - - if bp.ctx.repeats[payload['boopbox_id']] > 10: - await queue.put({'_': 'talk', 'who': 'tiger', 'msg': f"Hey! Stop that! You're not the only one here!"}) - await queue.put({'_': 'bye'}) - return response.text('ok') - - request.app.ctx.boop.execute('INSERT INTO boop(tag_id, station, ts) VALUES (?,?,?)', (payload['id'], payload['boopbox_id'], int(time()))) - request.app.ctx.boop.commit() - - return response.text('ok') diff --git a/boop_process.py b/boop_process.py deleted file mode 100644 index db70c4d..0000000 --- a/boop_process.py +++ /dev/null @@ -1,42 +0,0 @@ -from datetime import datetime -from random import randint -from hashlib import md5 -from base64 import b16encode - -fibonacci = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233] -current_date = datetime.today() - -async def boop_process(orders, db): - print(f'Processing {len(orders)} orders') - db.execute('DELETE FROM player') - - for o in orders: - - tags = [] - code = o.code - tag_id = o.nfc_id or b16encode(md5(code.encode()).digest()).decode()[:14] - birthday = o.birth_date - badge_id = o.badge_id or randint(0,200) - room = o.actual_room or randint(100,400) - country = o.country - name = o.first_name.lower().strip() - - birth_date = datetime.strptime(birthday, "%Y-%m-%d").date() - - age = current_date.year - birth_date.year - if (current_date.month, current_date.day) < (birth_date.month, birth_date.day): - age -= 1 - - # Check if the birthday is today - if (current_date.month, current_date.day) == (birth_date.month, birth_date.day): - tags.append('birthday') - - # Check if the badge is a fib number - if badge_id in fibonacci: - tags.append('fibonacci') - - db.execute('INSERT INTO player(tag_id, code, tags, birthday, badge_id, age, name, room, country) VALUES (?,?,?,?,?,?,?,?,?)', - (tag_id, code, ','.join(tags), birthday, badge_id, age, name, room, country) - ) - - db.commit() diff --git a/data/achievement.db b/data/achievement.db deleted file mode 100644 index 6fd2e45..0000000 Binary files a/data/achievement.db and /dev/null differ diff --git a/data/boop.db b/data/boop.db new file mode 100644 index 0000000..5bf5436 Binary files /dev/null and b/data/boop.db differ diff --git a/data/event.db b/data/event.db index 86b5f3d..2728c20 100644 Binary files a/data/event.db and b/data/event.db differ diff --git a/data/money.db b/data/money.db new file mode 100644 index 0000000..ddf15e6 Binary files /dev/null and b/data/money.db differ diff --git a/data/nfc_counts.db b/data/nfc_counts.db new file mode 100644 index 0000000..8982e01 Binary files /dev/null and b/data/nfc_counts.db differ diff --git a/ext.py b/ext.py index 96a18f9..903ef7b 100644 --- a/ext.py +++ b/ext.py @@ -34,7 +34,7 @@ class Order: self.checked_in = False for p in self.data['positions']: - if p['item'] in [16, 38]: + if p['item'] in ITEM_IDS['ticket']: self.position_id = p['id'] self.position_positionid = p['positionid'] self.answers = p['answers'] @@ -42,11 +42,11 @@ class Order: self.address = f"{p['street']} - {p['zipcode']} {p['city']} - {p['country']}" self.checked_in = bool(p['checkins']) - if p['item'] == 17: + if p['item'] in ITEM_IDS['membership_card']: self.has_card = True - if p['item'] == 19: - self.sponsorship = 'normal' if p['variation'] == 13 else 'super' + if p['item'] in ITEM_IDS['sponsorship']: + self.sponsorship = 'normal' if p['variation'] == ITEMS_IDS['sponsorship'][0] else 'super' if p['country']: self.country = p['country'] @@ -55,10 +55,10 @@ class Order: self.first_name = p['attendee_name_parts']['given_name'] self.last_name = p['attendee_name_parts']['family_name'] - if p['item'] == 20: + if p['item'] == ITEM_IDS['early_arrival']: self.has_early = True - if p['item'] == 21: + if p['item'] == ITEM_IDS['late_departure']: self.has_late = True self.total = float(data['total']) @@ -172,17 +172,10 @@ class Order: 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]: + if quota['id'] == ROOM_MAP[capacity]: return quota['available_number'] async def get_quotas(request: Request=None): diff --git a/money.py b/money.py deleted file mode 100644 index 7808745..0000000 --- a/money.py +++ /dev/null @@ -1,53 +0,0 @@ -from sanic import Blueprint, exceptions, response -from time import time -from asyncio import Future -import asyncio -from datetime import datetime - -bp = Blueprint("money", url_prefix="/money") - -@bp.post("/pos") -async def do_transaction(request): - - message = '' - tx_id = request.app.ctx.money.execute('INSERT INTO tx(tag_id, amount, ts) VALUES (?,?,?) RETURNING id', (request.form.get('nfc_id'), request.form.get('total'), time())).fetchone()[0] - - for item, qty in request.form.items(): - if not item.startswith('itm_'): continue - if qty[0] == '0': continue - request.app.ctx.money.execute('INSERT INTO tx_items(tx_id, item_id, qty) VALUES (?,?,?)', (tx_id, item[4:], qty[0])) - - request.app.ctx.money.commit() - return await show_transactions(request, message='Transazione eseguita con successo!') - -@bp.get("/pos") -async def show_transactions(request, message=None): - tpl = request.app.ctx.tpl.get_template('pos.html') - items = request.app.ctx.money.execute('SELECT * FROM item') - - tx_info = {} - last_tx = request.app.ctx.money.execute('SELECT * FROM tx WHERE amount < 0 ORDER BY ts DESC LIMIT 3').fetchall() - for tx in last_tx: - tx_info[tx['id']] = {'items': request.app.ctx.money.execute('SELECT * FROM tx_items JOIN item ON item_id = item.id AND tx_id = ?', (tx['id'],)).fetchall(), 'order': await request.app.ctx.om.get_order(nfc_id=tx['tag_id']), 'time': datetime.fromtimestamp(tx['ts'] or 0).strftime('%H:%M')} - - return response.html(tpl.render(items=items, message=message, last_tx=last_tx, tx_info=tx_info)) - -@bp.get("/poll_barcode") -async def give_barcode(request): - - request.app.ctx.nfc_reads[request.ip] = Future() - - try: - bcd = await asyncio.wait_for(request.app.ctx.nfc_reads[request.ip], 20) - except asyncio.TimeoutError: - if not request.ip in request.app.ctx.nfc_reads: - del request.app.ctx.nfc_reads[request.ip] - return response.json({'error': 'no_barcode'}) - - info = request.app.ctx.money.execute("SELECT count(*), coalesce(sum(amount), 0) FROM tx WHERE coalesce(is_canceled, 0) != 1 AND tag_id = ?", (bcd['id'],)).fetchone() - - order = await request.app.ctx.om.get_order(nfc_id=bcd['id']) - - desc = ("⚠ī¸" if not bcd['is_secure'] else '') + (f"👤 {order.code} {order.name}" if order else f"đŸĒ™ {bcd['id']}") + f" ¡ Transazioni: {info[0]}" - - return response.json({**bcd, 'txamt': info[0], 'balance': info[1], 'desc': desc}) diff --git a/nfc.py b/nfc.py deleted file mode 100644 index 4f98447..0000000 --- a/nfc.py +++ /dev/null @@ -1,72 +0,0 @@ -from sanic import Blueprint, exceptions, response -from random import choice -from ext import * -from config import headers, PROPIC_DEADLINE -from PIL import Image -from os.path import isfile -from os import unlink -from io import BytesIO -from hashlib import sha224 -from time import time -from urllib.parse import unquote -from base64 import b16decode -from asyncio import Future -import json -import re -import asyncio - -bp = Blueprint("nfc") - -@bp.post("/nfc/read") -async def handle_reading(request): - payload = request.json - if payload['count']: - request.app.ctx.nfc_counts.execute('INSERT INTO read_count(tag_id, ts, count) VALUES (?,?,?)', (payload['id'], int(time()), payload['count'])) - request.app.ctx.nfc_counts.commit() - - if payload['boopbox_id']: - await request.app.ctx.boopbox_queue.put(payload) - return response.text('ok') - - if not request.ip in request.app.ctx.nfc_reads: - return response.text('wasted read') - - try: - request.app.ctx.nfc_reads[request.ip].set_result(payload) - except asyncio.exceptions.InvalidStateError: - del request.app.ctx.nfc_reads[request.ip] - - return response.text('ok') - -@bp.get("/fz23/") -async def handle_nfc_tag(request, tag_id): - - tag = re.match('([bc])([0-9A-F]{14})x([0-9A-F]{6})', tag_id) - - # Store the read count - read_count = int.from_bytes(b16decode(tag.group(3))) - request.app.ctx.nfc_counts.execute('INSERT INTO read_count(tag_id, ts, count, is_web) VALUES (?,?,?,1)', (tag.group(2), int(time()), read_count)) - request.app.ctx.nfc_counts.commit() - - # If it's a coin, just show the coin template - if tag.group(1) == 'c': - return response.redirect(f"coin/{tag.group(2)}") - - # If it's a badge, look for the corresponding order - for o in request.app.ctx.om.cache.values(): - if o.nfc_id == tag.group(2): - return response.redirect(o.code) - - raise exceptions.NotFound("Unknown tag :(") - -@bp.get("/fz23/coin/") -async def show_coin(request, tag_id): - balance = request.app.ctx.money.execute('SELECT sum(amount) FROM tx WHERE tag_id = ?', (tag_id,)).fetchone()[0] - - tpl = request.app.ctx.tpl.get_template('coin.html') - return response.html(tpl.render(balance=balance)) - -@bp.get("/fz23/") -async def show_order(request, code): - return response.html(f"

Badge

{code}

") - diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..88f0cc1 --- /dev/null +++ b/readme.md @@ -0,0 +1,26 @@ +# Furizon Webint +Furizon Webint is a powerful control panel designed to complement Pretix, providing management of various aspects related to the attendance of participants at furry conventions. Originally developed for Furizon Beyond (2023), this application is currently undergoing a rehaul to become more versatile and adaptable for use in any convention. + +## How does it work? +The integration with Pretix is achieved by leveraging a simple nginx rule. When individuals place orders through Pretix, they usually receive a "magic" link that allows them to manage their order. Using a nginx rule, we redirect these requests to this backend. This process is seamless because the essential information needed for managing Pretix orders can still be accessed via a shorter URL without compromising any functionality. + +## Why not a pretix plugin? +Developing plugins for Pretix was far too tedious, and Pretix didn't have the flexibility needed for this panel. + +## What can it do? +- User badges management (allow attendees to upload pictures within the deadlines) +- Manage hotel rooms (attendees can create, join, delete rooms) +- Show a nosecount public page +- Data export +- Car pooling (let attendees post announcements and organize trips) +- Karaoke Queue management (apply to sing for the karaoke contest and manage the queue) +- Manage the events and present them via API for usage with he app +- Export an API to be used for the mobile app (no plans to open source that, sorry ☚ī¸) +- Check-in management + +## How to run it +1. Create a Python virtual environment (venv). +2. Install the required dependencies from the `requirements.txt` file. +3. Edit the `config.py` file with your specific data. You can use `config.example.py` as a template to guide you. +4. Set up an nginx rule to redirect requests for `/manage/` and `/[a-z0-9]+/[a-z0-9]+/order/[A-Z0-9]+/[a-z0-9]+/open/[a-z0-9]+/` to the Furizon Webint backend. +5. Run `app.py`. By default, the application will listen on `0.0.0.0:8188`. diff --git a/res/font/NexaRustExtras-Free.otf b/res/font/NexaRustExtras-Free.otf deleted file mode 100644 index c9f13b4..0000000 Binary files a/res/font/NexaRustExtras-Free.otf and /dev/null differ diff --git a/res/orig/FurizonBanner2023_BG.png b/res/orig/FurizonBanner2023_BG.png deleted file mode 100644 index 966a173..0000000 Binary files a/res/orig/FurizonBanner2023_BG.png and /dev/null differ diff --git a/res/orig/TIGER.png b/res/orig/TIGER.png deleted file mode 100644 index e3956b7..0000000 Binary files a/res/orig/TIGER.png and /dev/null differ diff --git a/res/orig/wolf.png b/res/orig/wolf.png deleted file mode 100644 index 7d2773f..0000000 Binary files a/res/orig/wolf.png and /dev/null differ diff --git a/res/snd/01.mp3 b/res/snd/01.mp3 deleted file mode 100644 index b26cf0b..0000000 Binary files a/res/snd/01.mp3 and /dev/null differ diff --git a/res/snd/02.mp3 b/res/snd/02.mp3 deleted file mode 100644 index d4db4da..0000000 Binary files a/res/snd/02.mp3 and /dev/null differ diff --git a/res/snd/03.mp3 b/res/snd/03.mp3 deleted file mode 100644 index b9b22f4..0000000 Binary files a/res/snd/03.mp3 and /dev/null differ diff --git a/res/snd/04.mp3 b/res/snd/04.mp3 deleted file mode 100644 index 925d60d..0000000 Binary files a/res/snd/04.mp3 and /dev/null differ diff --git a/res/snd/05.mp3 b/res/snd/05.mp3 deleted file mode 100644 index 59b83b8..0000000 Binary files a/res/snd/05.mp3 and /dev/null differ diff --git a/res/snd/06.mp3 b/res/snd/06.mp3 deleted file mode 100644 index 28464c5..0000000 Binary files a/res/snd/06.mp3 and /dev/null differ diff --git a/res/snd/07.mp3 b/res/snd/07.mp3 deleted file mode 100644 index 0fcd952..0000000 Binary files a/res/snd/07.mp3 and /dev/null differ diff --git a/res/snd/08.mp3 b/res/snd/08.mp3 deleted file mode 100644 index 904f3d5..0000000 Binary files a/res/snd/08.mp3 and /dev/null differ diff --git a/res/snd/09.mp3 b/res/snd/09.mp3 deleted file mode 100644 index 6c29a7b..0000000 Binary files a/res/snd/09.mp3 and /dev/null differ diff --git a/res/snd/10.mp3 b/res/snd/10.mp3 deleted file mode 100644 index 36288d3..0000000 Binary files a/res/snd/10.mp3 and /dev/null differ diff --git a/res/snd/11.mp3 b/res/snd/11.mp3 deleted file mode 100644 index 650d3a9..0000000 Binary files a/res/snd/11.mp3 and /dev/null differ diff --git a/res/snd/12.mp3 b/res/snd/12.mp3 deleted file mode 100644 index b5fb15e..0000000 Binary files a/res/snd/12.mp3 and /dev/null differ diff --git a/res/snd/13.mp3 b/res/snd/13.mp3 deleted file mode 100644 index cf4338d..0000000 Binary files a/res/snd/13.mp3 and /dev/null differ diff --git a/res/snd/14.mp3 b/res/snd/14.mp3 deleted file mode 100644 index 21468d8..0000000 Binary files a/res/snd/14.mp3 and /dev/null differ diff --git a/res/snd/15.mp3 b/res/snd/15.mp3 deleted file mode 100644 index 2bb0e41..0000000 Binary files a/res/snd/15.mp3 and /dev/null differ diff --git a/res/snd/16.mp3 b/res/snd/16.mp3 deleted file mode 100644 index a97c6fe..0000000 Binary files a/res/snd/16.mp3 and /dev/null differ diff --git a/res/snd/17.mp3 b/res/snd/17.mp3 deleted file mode 100644 index dde9052..0000000 Binary files a/res/snd/17.mp3 and /dev/null differ diff --git a/res/snd/18.mp3 b/res/snd/18.mp3 deleted file mode 100644 index ca97513..0000000 Binary files a/res/snd/18.mp3 and /dev/null differ diff --git a/res/snd/19.mp3 b/res/snd/19.mp3 deleted file mode 100644 index d3db922..0000000 Binary files a/res/snd/19.mp3 and /dev/null differ diff --git a/res/snd/20.mp3 b/res/snd/20.mp3 deleted file mode 100644 index 56e6e49..0000000 Binary files a/res/snd/20.mp3 and /dev/null differ diff --git a/res/snd/21.mp3 b/res/snd/21.mp3 deleted file mode 100644 index 0cdc661..0000000 Binary files a/res/snd/21.mp3 and /dev/null differ diff --git a/res/snd/22.mp3 b/res/snd/22.mp3 deleted file mode 100644 index 01b9224..0000000 Binary files a/res/snd/22.mp3 and /dev/null differ diff --git a/res/snd/23.mp3 b/res/snd/23.mp3 deleted file mode 100644 index f8f283e..0000000 Binary files a/res/snd/23.mp3 and /dev/null differ diff --git a/res/snd/24.mp3 b/res/snd/24.mp3 deleted file mode 100644 index 7770625..0000000 Binary files a/res/snd/24.mp3 and /dev/null differ diff --git a/res/snd/25.mp3 b/res/snd/25.mp3 deleted file mode 100644 index d564cf9..0000000 Binary files a/res/snd/25.mp3 and /dev/null differ diff --git a/res/snd/error.wav b/res/snd/error.wav deleted file mode 100644 index 16d679e..0000000 Binary files a/res/snd/error.wav and /dev/null differ diff --git a/res/snd/ratelimit.wav b/res/snd/ratelimit.wav deleted file mode 100644 index dc71ed6..0000000 Binary files a/res/snd/ratelimit.wav and /dev/null differ diff --git a/res/video/coin-intro.webm b/res/video/coin-intro.webm deleted file mode 100644 index db3e757..0000000 Binary files a/res/video/coin-intro.webm and /dev/null differ diff --git a/res/video/coin-loop.webm b/res/video/coin-loop.webm deleted file mode 100644 index fa5a381..0000000 Binary files a/res/video/coin-loop.webm and /dev/null differ diff --git a/res/video/coin.webp b/res/video/coin.webp deleted file mode 100644 index 1624f85..0000000 Binary files a/res/video/coin.webp and /dev/null differ diff --git a/room.py b/room.py index cf036c4..71c327f 100644 --- a/room.py +++ b/room.py @@ -279,8 +279,8 @@ async def confirm_room(request, order: Order, quotas: Quotas): thing = { 'order': order.code, 'addon_to': order.position_positionid, - 'item': 39, - 'variation': ([None, 16, 17, 18, 19, 20])[len(room_members)] + 'item': ITEM_IDS['room'], + 'variation': ROOM_MAP[len(room_members)] } async with httpx.AsyncClient() as client: @@ -294,8 +294,8 @@ async def confirm_room(request, order: Order, quotas: Quotas): thing = { 'order': rm.code, 'addon_to': rm.position_positionid, - 'item': 42, - 'variation': ([None, 21, 22, 23, 24, 25])[len(room_members)] + 'item': ITEM_IDS['room'], + 'variation': ROOM_MAP[len(room_members)] } res = await client.post(join(base_url, "orderpositions/"), headers=headers, json=thing) diff --git a/tpl/badge.html b/tpl/badge.html deleted file mode 100644 index 6e820f7..0000000 --- a/tpl/badge.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - {{order.name}} - - - - -
- -
-

{{order.name}}

-

Is an attendee of Furizon 2023 :D

-


- -

Stay cool~
Stay Furizon

-
- - diff --git a/tpl/boopbox.html b/tpl/boopbox.html deleted file mode 100644 index 5f5bd66..0000000 --- a/tpl/boopbox.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - -
- - - -
(touch the screen to continue )
- -
-
-

It looks like nothing is happening now~
_-¯¯-¯_
See you later :)

-
-

See more events with our app!

- -
- - - - - -
-

Useful Info

-

CALL SECURITY

-

Dial (+39) 375 732 4734

-
-

Reception Open

-

Every day 8:00~19:00

-
-

NFC issues?

-

Search for Foxo (Badge ID 3) or write to @unfoxo on Telegram

-
-

-
- -
Dealer's Den
-
- - - - diff --git a/tpl/coin.html b/tpl/coin.html deleted file mode 100644 index 20f752c..0000000 --- a/tpl/coin.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - You found a coin! - - - - -
- - {% if balance < 0 %} -

You found a
{{balance}}FZ
coin!

- {% else %} -

You found a coin!
(but sadly, it's already spent)

- {% endif %} -
- - - - diff --git a/tpl/manage.html b/tpl/manage.html deleted file mode 100644 index 8773d66..0000000 --- a/tpl/manage.html +++ /dev/null @@ -1,97 +0,0 @@ -{% extends "base.html" %} -{% block title %}Your Booking{% endblock %} -{% block main %} -
-
-

{{order.ans('fursona_name')}}'s booking

-
- {% if order.status() == 'pending' %} -

⚠ī¸ Your order is still pending. You will not be able to reserve a room. Check your payment status

- {% endif %} - {% if not order.ans('propic') %} -

⚠ī¸ You still haven't uploaded a profile pic. Upload one now

- {% endif %} - -
-

Room Status: - {% if order.ans('room_confirmed') %} - {{order.ans('room_confirmed')}} - room confirmed

-

Your room is confirmed! Enjoy the convention :)

- {% elif order.ans('room_members') %} - pending room -

You have a room, but it is not confirmed! Make sure all room owners have paid, then use the button below to reserve the room for two people.

- -
Your room
-
- {% set room = namespace(forbidden=false) %} - {% for person in roommate_orders %} -
- -

{{person.ans('fursona_name')}}

-

{{person.ans('staff_title') if person.ans('staff_title') else ''}}{{' ¡ Fursuiter' if person.ans('is_fursuiter') != 'No'}}

-

{{('UNPAID' if person.status() == 'pending')|safe}}

-
- {% if person.status() != 'paid' %} - {% set room.forbidden = True %} - {% endif %} - {% endfor %} -
- -
Available rooms
- - - - - - - - - - - - - - - - - - - - - -
Single room{{room_qty[1]}} available
Double room{{room_qty[2]}} available
Triple room{{room_qty[3]}} available
Quadruple room{{room_qty[4]}} available
Quintuple room{{room_qty[5]}} available
- - {% if room.forbidden %} -

Since at least one of your roommates still have a pending reservation, you will not be able to reserve a room for now.

- - {% else %} - - {% endif %} - {% else %} - no room -

You currently don't have any room. If you don't create or join one within 42 days, 10 hours, 32 minutes, 13 seconds, we will assign you to a random one.

-
-
- - -
-
- {% endif %} - - -
-
- - - -{% endblock %} diff --git a/tpl/nosecount.html b/tpl/nosecount.html index d46a0eb..55b64c2 100644 --- a/tpl/nosecount.html +++ b/tpl/nosecount.html @@ -29,46 +29,47 @@ {% endif %} {% endfor %} - -
-

Unconfirmed rooms

-

These unconfirmed rooms are still being organized and may be subject to change. These rooms may also have openings for additional roommates. If you are interested in sharing a room, you can use this page to find potential roommates

- {% for o in orders.values() %} - {% if o.code == o.room_id and not o.room_confirmed and len(o.room_members) > 1 %} -

{% if o.room_confirmed %}🔒{% endif %}{{o.room_name}}

-
- {% for m in o.room_members %} - {% if m in orders %} - {% with person = orders[m] %} -
-
- - -
-
{{person.ans('fursona_name')}}
-
- {% endwith %} - {% endif %} - {% endfor %} -
+ {% for o in orders.values() if (o.code == o.room_id and not o.room_confirmed and len(o.room_members) > 1) %} + {% if loop.first %} +
+

Unconfirmed rooms

+

These unconfirmed rooms are still being organized and may be subject to change. These rooms may also have openings for additional roommates. If you are interested in sharing a room, you can use this page to find potential roommates

{% endif %} +

{% if o.room_confirmed %}🔒{% endif %}{{o.room_name}}

+
+ {% for m in o.room_members %} + {% if m in orders %} + {% with person = orders[m] %} +
+
+ + +
+
{{person.ans('fursona_name')}}
+
+ {% endwith %} + {% endif %} + {% endfor %} +
+ {% if loop.last %}{% endif %} {% endfor %} - -
-

Roomless furs

-

These furs have not yet secured a room for the convention. If you see your name on this list, please make sure to secure a room before the deadline to avoid being placed in a random room. If you are looking for a roommate or have an open spot in your room, you can use this page to find and connect with other furries who are also looking for housing 🎲

-
- {% for person in orders.values() %} - {% if (not person.room_id or len(person.room_members) == 1) and (not person.room_confirmed) %} -
-
- - -
-
{{person.ans('fursona_name')}}
+ + {% for person in orders.values() if (not person.room_id or len(person.room_members) == 1) and (not person.room_confirmed)%} + {% if loop.first %} +
+

Roomless furs

+

These furs have not yet secured a room for the convention. If you see your name on this list, please make sure to secure a room before the deadline to avoid being placed in a random room. If you are looking for a roommate or have an open spot in your room, you can use this page to find and connect with other furries who are also looking for housing 🎲

+
+ {% endif %} +
+
+ +
- {% endif %} +
{{person.ans('fursona_name')}}
+
+ {% if loop.last %}
{% endif %} {% endfor %} -
+ {% endblock %} diff --git a/tpl/pos.html b/tpl/pos.html deleted file mode 100644 index 1df431b..0000000 --- a/tpl/pos.html +++ /dev/null @@ -1,294 +0,0 @@ - - - - Fz vending - - - -
{{message or 'Scannerizza un NFC...'}}
-
- - -
- - {% for item in items %} - 0 %}class="secret"{% endif %}> - - - - - {% endfor %} -
{{item.name}}{{item.price}}FZ - - - -
-
- -
- - -
-
- - -