Furizon overlord - working
This commit is contained in:
parent
c735144dca
commit
160d186aeb
|
@ -161,3 +161,5 @@ res/propic/propic_*
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
config.py
|
config.py
|
||||||
|
furizon_webinit_riverside2023.tar.gz
|
||||||
|
diomerdas
|
||||||
|
|
26
README.md
26
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`.
|
17
app.py
17
app.py
|
@ -10,6 +10,7 @@ from os.path import join
|
||||||
from ext import *
|
from ext import *
|
||||||
from config import *
|
from config import *
|
||||||
from aztec_code_generator import AztecCode
|
from aztec_code_generator import AztecCode
|
||||||
|
from propic import resetDefaultPropic
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from asyncio import Queue
|
from asyncio import Queue
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
@ -32,7 +33,7 @@ from carpooling import bp as carpooling_bp
|
||||||
from checkin import bp as checkin_bp
|
from checkin import bp as checkin_bp
|
||||||
|
|
||||||
app.blueprint([room_bp, karaoke_bp, propic_bp, export_bp, stats_bp, api_bp, carpooling_bp, checkin_bp])
|
app.blueprint([room_bp, karaoke_bp, propic_bp, export_bp, stats_bp, api_bp, carpooling_bp, checkin_bp])
|
||||||
|
|
||||||
@app.exception(exceptions.SanicException)
|
@app.exception(exceptions.SanicException)
|
||||||
async def clear_session(request, exception):
|
async def clear_session(request, exception):
|
||||||
tpl = app.ctx.tpl.get_template('error.html')
|
tpl = app.ctx.tpl.get_template('error.html')
|
||||||
|
@ -50,7 +51,7 @@ async def main_start(*_):
|
||||||
app.ctx.om = OrderManager()
|
app.ctx.om = OrderManager()
|
||||||
if FILL_CACHE:
|
if FILL_CACHE:
|
||||||
log.info("Filling cache!")
|
log.info("Filling cache!")
|
||||||
await app.ctx.om.fill_cache()
|
await app.ctx.om.updateCache()
|
||||||
log.info("Cache fill done!")
|
log.info("Cache fill done!")
|
||||||
|
|
||||||
app.ctx.nfc_counts = sqlite3.connect('data/nfc_counts.db')
|
app.ctx.nfc_counts = sqlite3.connect('data/nfc_counts.db')
|
||||||
|
@ -61,6 +62,7 @@ async def main_start(*_):
|
||||||
app.ctx.tpl.globals.update(time=time)
|
app.ctx.tpl.globals.update(time=time)
|
||||||
app.ctx.tpl.globals.update(PROPIC_DEADLINE=PROPIC_DEADLINE)
|
app.ctx.tpl.globals.update(PROPIC_DEADLINE=PROPIC_DEADLINE)
|
||||||
app.ctx.tpl.globals.update(ITEM_IDS=ITEM_IDS)
|
app.ctx.tpl.globals.update(ITEM_IDS=ITEM_IDS)
|
||||||
|
app.ctx.tpl.globals.update(ROOM_TYPE_NAMES=ROOM_TYPE_NAMES)
|
||||||
app.ctx.tpl.globals.update(int=int)
|
app.ctx.tpl.globals.update(int=int)
|
||||||
app.ctx.tpl.globals.update(len=len)
|
app.ctx.tpl.globals.update(len=len)
|
||||||
|
|
||||||
|
@ -80,7 +82,7 @@ async def redirect_explore(request, code, secret, order: Order, secret2=None):
|
||||||
|
|
||||||
if not order:
|
if not order:
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
res = await client.get(join(base_url, f"orders/{code}/"), headers=headers)
|
res = await client.get(join(base_url_event, f"orders/{code}/"), headers=headers)
|
||||||
print(res.json())
|
print(res.json())
|
||||||
if res.status_code != 200:
|
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.")
|
raise exceptions.NotFound("This order code does not exist. Check that your order wasn't deleted, or the link is correct.")
|
||||||
|
@ -102,6 +104,11 @@ async def welcome(request, order: Order, quota: Quotas):
|
||||||
|
|
||||||
if not order:
|
if not order:
|
||||||
raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
||||||
|
|
||||||
|
if order.ans("propic_file") is None:
|
||||||
|
await resetDefaultPropic(request, order, False)
|
||||||
|
if order.ans("propic_fursuiter_file") is None:
|
||||||
|
await resetDefaultPropic(request, order, True)
|
||||||
|
|
||||||
pending_roommates = []
|
pending_roommates = []
|
||||||
if order.pending_roommates:
|
if order.pending_roommates:
|
||||||
|
@ -139,7 +146,7 @@ async def download_ticket(request, order: Order):
|
||||||
raise exceptions.Forbidden("You are not allowed to download this ticket.")
|
raise exceptions.Forbidden("You are not allowed to download this ticket.")
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
res = await client.get(join(base_url, f"orders/{order.code}/download/pdf/"), headers=headers)
|
res = await client.get(join(base_url_event, f"orders/{order.code}/download/pdf/"), headers=headers)
|
||||||
|
|
||||||
if res.status_code == 409:
|
if res.status_code == 409:
|
||||||
raise exceptions.SanicException("Your ticket is still being generated. Please try again later!", status_code=res.status_code)
|
raise exceptions.SanicException("Your ticket is still being generated. Please try again later!", status_code=res.status_code)
|
||||||
|
@ -150,7 +157,7 @@ async def download_ticket(request, order: Order):
|
||||||
|
|
||||||
@app.route("/manage/logout")
|
@app.route("/manage/logout")
|
||||||
async def logour(request):
|
async def logour(request):
|
||||||
raise exceptions.Forbidden("You have been logged out.", status_code=403)
|
raise exceptions.Forbidden("You have been logged out.")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=8188, dev=DEV_MODE)
|
app.run(host="0.0.0.0", port=8188, dev=DEV_MODE)
|
||||||
|
|
|
@ -2,7 +2,7 @@ from sanic.response import html, redirect, text
|
||||||
from sanic import Blueprint, exceptions, response
|
from sanic import Blueprint, exceptions, response
|
||||||
from random import choice
|
from random import choice
|
||||||
from ext import *
|
from ext import *
|
||||||
from config import headers, base_url
|
from config import headers, base_url_event
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from os.path import isfile
|
from os.path import isfile
|
||||||
from os import unlink
|
from os import unlink
|
||||||
|
@ -64,7 +64,7 @@ async def do_checkin(request):
|
||||||
|
|
||||||
if not order.checked_in:
|
if not order.checked_in:
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
res = await client.post(base_url.replace('events/beyond/', 'checkinrpc/redeem/'), json={'secret': order.barcode, 'source_type': 'barcode', 'type': 'entry', 'lists': [3,]}, headers=headers)
|
res = await client.post(base_url_event.replace(f'events/{EVENT_NAME}/', 'checkinrpc/redeem/'), json={'secret': order.barcode, 'source_type': 'barcode', 'type': 'entry', 'lists': [3,]}, headers=headers)
|
||||||
|
|
||||||
tpl = request.app.ctx.tpl.get_template('checkin_3.html')
|
tpl = request.app.ctx.tpl.get_template('checkin_3.html')
|
||||||
return html(tpl.render(order=order, room_owner=room_owner, roommates=roommates))
|
return html(tpl.render(order=order, room_owner=room_owner, roommates=roommates))
|
||||||
|
|
|
@ -1,32 +1,53 @@
|
||||||
|
API_TOKEN = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
|
||||||
ORGANIZER = 'furizon'
|
ORGANIZER = 'furizon'
|
||||||
EVENT_NAME = 'river-side-2023'
|
EVENT_NAME = 'overlord'
|
||||||
API_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxx'
|
HOSTNAME = 'reg.furizon.net'
|
||||||
HOSTNAME = 'your-pretix-hostname'
|
|
||||||
|
|
||||||
headers = {'Host': HOSTNAME, 'Authorization': f'Token {API_TOKEN}'}
|
headers = {'Host': HOSTNAME, 'Authorization': f'Token {API_TOKEN}'}
|
||||||
base_url = f"https://{HOSTNAME}/api/v1/organizers/{ORGANIZER}/events/{EVENT_NAME}/"
|
base_url = "http://urlllllllllllllllllllll/api/v1/"
|
||||||
|
base_url_event = f"{base_url}organizers/{ORGANIZER}/events/{EVENT_NAME}/"
|
||||||
|
|
||||||
PROPIC_DEADLINE = 1683575684
|
PROPIC_DEADLINE = 9999999999
|
||||||
FILL_CACHE = True
|
FILL_CACHE = True
|
||||||
|
CACHE_EXPIRE_TIME = 60 * 60 * 4
|
||||||
|
|
||||||
DEV_MODE = True
|
DEV_MODE = True
|
||||||
|
|
||||||
ITEM_IDS = {
|
ITEM_IDS = {
|
||||||
'ticket': [90,],
|
'ticket': [126, 127, 155],
|
||||||
'membership_card': [91,],
|
'membership_card': [128,],
|
||||||
'sponsorship': [], # first one = normal, second = super
|
'sponsorship': [55, 56], # first one = normal, second = super
|
||||||
'early_arrival': [],
|
'early_arrival': [133],
|
||||||
'late_departure': [],
|
'late_departure': [134],
|
||||||
'room': 98
|
'room': 135,
|
||||||
|
'bed_in_room': 153,
|
||||||
|
'daily': 162,
|
||||||
|
'daily_addons': [163, 164, 165, 166] #This should be in date order. If there are holes in the daily-span, insert an unexisting id
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create a bunch of "room" items which will get added to the order once somebody gets a room.
|
# Create a bunch of "room" items which will get added to the order once somebody gets a room.
|
||||||
|
# Map variationId -> numberOfPeopleInRoom
|
||||||
ROOM_MAP = {
|
ROOM_MAP = {
|
||||||
1: 16,
|
# SACRO CUORE
|
||||||
2: 17,
|
83: 1,
|
||||||
3: 18,
|
67: 2,
|
||||||
4: 19,
|
68: 3,
|
||||||
5: 20
|
69: 4,
|
||||||
|
70: 5,
|
||||||
|
|
||||||
|
# OVERFLOW 1
|
||||||
|
75: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
ROOM_TYPE_NAMES = {
|
||||||
|
83: "Park Hotel Sacro Cuore (main hotel) - Single",
|
||||||
|
67: "Park Hotel Sacro Cuore (main hotel) - Double",
|
||||||
|
68: "Park Hotel Sacro Cuore (main hotel) - Triple",
|
||||||
|
69: "Park Hotel Sacro Cuore (main hotel) - Quadruple",
|
||||||
|
70: "Park Hotel Sacro Cuore (main hotel) - Quintuple",
|
||||||
|
|
||||||
|
# OVERFLOW 1
|
||||||
|
75: "Hotel San Valier (overflow hotel) - Double"
|
||||||
}
|
}
|
||||||
|
|
||||||
# This is used for feedback sending inside of the app. Feedbacks will be sent to the specified chat using the bot api id.
|
# This is used for feedback sending inside of the app. Feedbacks will be sent to the specified chat using the bot api id.
|
||||||
|
@ -36,6 +57,6 @@ TG_CHAT_ID = -1234567
|
||||||
# These order codes have additional functions.
|
# These order codes have additional functions.
|
||||||
ADMINS = ['XXXXX', 'YYYYY']
|
ADMINS = ['XXXXX', 'YYYYY']
|
||||||
|
|
||||||
SMTP_HOST = 'your-smtp-host.com'
|
SMTP_HOST = 'host'
|
||||||
SMTP_USER = 'username'
|
SMTP_USER = 'user'
|
||||||
SMTP_PASSWORD = 'password'
|
SMTP_PASSWORD = 'pw'
|
||||||
|
|
BIN
data/boop.db
BIN
data/boop.db
Binary file not shown.
BIN
data/event.db
BIN
data/event.db
Binary file not shown.
61
ext.py
61
ext.py
|
@ -2,6 +2,7 @@ from dataclasses import dataclass
|
||||||
from sanic import Request, exceptions
|
from sanic import Request, exceptions
|
||||||
import httpx
|
import httpx
|
||||||
import re
|
import re
|
||||||
|
from utils import *
|
||||||
from config import *
|
from config import *
|
||||||
from os.path import join
|
from os.path import join
|
||||||
import json
|
import json
|
||||||
|
@ -32,6 +33,11 @@ class Order:
|
||||||
self.country = 'xx'
|
self.country = 'xx'
|
||||||
self.address = None
|
self.address = None
|
||||||
self.checked_in = False
|
self.checked_in = False
|
||||||
|
self.room_type = None
|
||||||
|
self.daily = False
|
||||||
|
self.dailyDays = []
|
||||||
|
self.room_person_no = 0
|
||||||
|
self.answers = []
|
||||||
|
|
||||||
idata = data['invoice_address']
|
idata = data['invoice_address']
|
||||||
if idata:
|
if idata:
|
||||||
|
@ -39,13 +45,22 @@ class Order:
|
||||||
self.country = idata['country']
|
self.country = idata['country']
|
||||||
|
|
||||||
for p in self.data['positions']:
|
for p in self.data['positions']:
|
||||||
if p['item'] in (ITEM_IDS['ticket'] + ITEM_IDS['daily']):
|
if p['item'] in (ITEM_IDS['ticket'] + [ITEM_IDS['daily']]):
|
||||||
self.position_id = p['id']
|
self.position_id = p['id']
|
||||||
self.position_positionid = p['positionid']
|
self.position_positionid = p['positionid']
|
||||||
self.position_positiontypeid = p['item']
|
self.position_positiontypeid = p['item']
|
||||||
self.answers = p['answers']
|
self.answers = p['answers']
|
||||||
|
for i, ans in enumerate(self.answers):
|
||||||
|
if(TYPE_OF_QUESTIONS[self.answers[i]['question']] == QUESTION_TYPES['file_upload']):
|
||||||
|
self.answers[i]['answer'] = "file:keep"
|
||||||
self.barcode = p['secret']
|
self.barcode = p['secret']
|
||||||
self.checked_in = bool(p['checkins'])
|
self.checked_in = bool(p['checkins'])
|
||||||
|
if p['item'] == ITEM_IDS['daily']:
|
||||||
|
self.daily = True
|
||||||
|
|
||||||
|
if p['item'] in ITEM_IDS['daily_addons']:
|
||||||
|
self.daily = True
|
||||||
|
self.dailyDays.append(ITEM_IDS['daily_addons'].index(p['item']))
|
||||||
|
|
||||||
if p['item'] in ITEM_IDS['membership_card']:
|
if p['item'] in ITEM_IDS['membership_card']:
|
||||||
self.has_card = True
|
self.has_card = True
|
||||||
|
@ -62,6 +77,10 @@ class Order:
|
||||||
|
|
||||||
if p['item'] == ITEM_IDS['late_departure']:
|
if p['item'] == ITEM_IDS['late_departure']:
|
||||||
self.has_late = True
|
self.has_late = True
|
||||||
|
|
||||||
|
if p['item'] == ITEM_IDS['bed_in_room']:
|
||||||
|
self.bed_in_room = p['variation']
|
||||||
|
self.room_person_no = ROOM_MAP[self.bed_in_room] if self.bed_in_room in ROOM_MAP else None
|
||||||
|
|
||||||
self.total = float(data['total'])
|
self.total = float(data['total'])
|
||||||
self.fees = 0
|
self.fees = 0
|
||||||
|
@ -117,6 +136,18 @@ class Order:
|
||||||
return a['answer']
|
return a['answer']
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
async def edit_answer_fileUpload(self, name, fileName, mimeType, data : bytes):
|
||||||
|
if(mimeType != None and data != None):
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
localHeaders = dict(headers)
|
||||||
|
localHeaders['Content-Type'] = mimeType
|
||||||
|
localHeaders['Content-Disposition'] = f'attachment; filename="{fileName}"'
|
||||||
|
res = await client.post(join(base_url, 'upload'), headers=localHeaders, content=data)
|
||||||
|
res = res.json()
|
||||||
|
await self.edit_answer(name, res['id'])
|
||||||
|
else:
|
||||||
|
await self.edit_answer(name, None)
|
||||||
|
|
||||||
async def edit_answer(self, name, new_answer):
|
async def edit_answer(self, name, new_answer):
|
||||||
found = False
|
found = False
|
||||||
self.pending_update = True
|
self.pending_update = True
|
||||||
|
@ -135,7 +166,7 @@ class Order:
|
||||||
if (not found) and (new_answer is not None):
|
if (not found) and (new_answer is not None):
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
res = await client.get(join(base_url, 'questions/'), headers=headers)
|
res = await client.get(join(base_url_event, 'questions/'), headers=headers)
|
||||||
res = res.json()
|
res = res.json()
|
||||||
|
|
||||||
for r in res['results']:
|
for r in res['results']:
|
||||||
|
@ -158,7 +189,7 @@ class Order:
|
||||||
del self.answers[i]['options']
|
del self.answers[i]['options']
|
||||||
del self.answers[i]['option_identifiers']
|
del self.answers[i]['option_identifiers']
|
||||||
|
|
||||||
res = await client.patch(join(base_url, f'orderpositions/{self.position_id}/'), headers=headers, json={'answers': self.answers})
|
res = await client.patch(join(base_url_event, f'orderpositions/{self.position_id}/'), headers=headers, json={'answers': self.answers})
|
||||||
|
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
for ans, err in zip(self.answers, res.json()['answers']):
|
for ans, err in zip(self.answers, res.json()['answers']):
|
||||||
|
@ -167,6 +198,10 @@ class Order:
|
||||||
|
|
||||||
raise exceptions.ServerError('There has been an error while updating this answers.')
|
raise exceptions.ServerError('There has been an error while updating this answers.')
|
||||||
|
|
||||||
|
for i, ans in enumerate(self.answers):
|
||||||
|
if(TYPE_OF_QUESTIONS[self.answers[i]['question']] == QUESTION_TYPES['file_upload']):
|
||||||
|
self.answers[i]['answer'] = "file:keep"
|
||||||
|
|
||||||
self.pending_update = False
|
self.pending_update = False
|
||||||
self.time = -1
|
self.time = -1
|
||||||
|
|
||||||
|
@ -183,7 +218,7 @@ class Quotas:
|
||||||
|
|
||||||
async def get_quotas(request: Request=None):
|
async def get_quotas(request: Request=None):
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
res = await client.get(join(base_url, 'quotas/?order=id&with_availability=true'), headers=headers)
|
res = await client.get(join(base_url_event, 'quotas/?order=id&with_availability=true'), headers=headers)
|
||||||
res = res.json()
|
res = res.json()
|
||||||
|
|
||||||
return Quotas(res)
|
return Quotas(res)
|
||||||
|
@ -194,9 +229,20 @@ async def get_order(request: Request=None):
|
||||||
|
|
||||||
class OrderManager:
|
class OrderManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.lastCacheUpdate = 0
|
||||||
|
self.empty()
|
||||||
|
|
||||||
|
def empty(self):
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
self.order_list = []
|
self.order_list = []
|
||||||
|
|
||||||
|
async def updateCache(self):
|
||||||
|
t = time()
|
||||||
|
if(t - self.lastCacheUpdate > CACHE_EXPIRE_TIME):
|
||||||
|
print("Re-filling cache!")
|
||||||
|
await self.fill_cache()
|
||||||
|
self.lastCacheUpdate = t
|
||||||
|
|
||||||
def add_cache(self, order):
|
def add_cache(self, order):
|
||||||
self.cache[order.code] = order
|
self.cache[order.code] = order
|
||||||
if not order.code in self.order_list:
|
if not order.code in self.order_list:
|
||||||
|
@ -208,12 +254,14 @@ class OrderManager:
|
||||||
self.order_list.remove(code)
|
self.order_list.remove(code)
|
||||||
|
|
||||||
async def fill_cache(self):
|
async def fill_cache(self):
|
||||||
|
await loadQuestions()
|
||||||
|
self.empty()
|
||||||
p = 0
|
p = 0
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
while 1:
|
while 1:
|
||||||
p += 1
|
p += 1
|
||||||
res = await client.get(join(base_url, f"orders/?page={p}"), headers=headers)
|
res = await client.get(join(base_url_event, f"orders/?page={p}"), headers=headers)
|
||||||
|
|
||||||
if res.status_code == 404: break
|
if res.status_code == 404: break
|
||||||
|
|
||||||
|
@ -233,6 +281,7 @@ class OrderManager:
|
||||||
if order.nfc_id == nfc_id:
|
if order.nfc_id == nfc_id:
|
||||||
return order
|
return order
|
||||||
|
|
||||||
|
await self.updateCache()
|
||||||
# If a cached order is needed, just get it if available
|
# 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:
|
if code and cached and code in self.cache and time()-self.cache[code].time < 3600:
|
||||||
return self.cache[code]
|
return self.cache[code]
|
||||||
|
@ -246,7 +295,7 @@ class OrderManager:
|
||||||
print('Fetching', code, 'with secret', secret)
|
print('Fetching', code, 'with secret', secret)
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
res = await client.get(join(base_url, f"orders/{code}/"), headers=headers)
|
res = await client.get(join(base_url_event, f"orders/{code}/"), headers=headers)
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
if request:
|
if request:
|
||||||
raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.")
|
raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.")
|
||||||
|
|
24
propic.py
24
propic.py
|
@ -9,6 +9,15 @@ from time import time
|
||||||
|
|
||||||
bp = Blueprint("propic", url_prefix="/manage/propic")
|
bp = Blueprint("propic", url_prefix="/manage/propic")
|
||||||
|
|
||||||
|
async def resetDefaultPropic(request, order: Order, isFursuiter, sendAnswer=True):
|
||||||
|
s = "_fursuiter" if isFursuiter else ""
|
||||||
|
print("Resetting default propic")
|
||||||
|
with open("res/propic/default.png", "rb") as f:
|
||||||
|
data = f.read()
|
||||||
|
await order.edit_answer_fileUpload(f'propic{s}_file', f'propic{s}_file_{order.code}_default.png', 'image/png', data)
|
||||||
|
if(sendAnswer):
|
||||||
|
await order.send_answers()
|
||||||
|
|
||||||
@bp.post("/upload")
|
@bp.post("/upload")
|
||||||
async def upload_propic(request, order: Order):
|
async def upload_propic(request, order: Order):
|
||||||
if not order: raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
if not order: raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
||||||
|
@ -21,8 +30,10 @@ async def upload_propic(request, order: Order):
|
||||||
|
|
||||||
if request.form.get('submit') == 'Delete main image':
|
if request.form.get('submit') == 'Delete main image':
|
||||||
await order.edit_answer('propic', None)
|
await order.edit_answer('propic', None)
|
||||||
|
await resetDefaultPropic(request, order, False, sendAnswer=False)
|
||||||
elif request.form.get('submit') == 'Delete fursuit image':
|
elif request.form.get('submit') == 'Delete fursuit image':
|
||||||
await order.edit_answer('propic_fursuiter', None)
|
await order.edit_answer('propic_fursuiter', None)
|
||||||
|
await resetDefaultPropic(request, order, True, sendAnswer=False)
|
||||||
else:
|
else:
|
||||||
for fn, body in request.files.items():
|
for fn, body in request.files.items():
|
||||||
if fn not in ['propic', 'propic_fursuiter']:
|
if fn not in ['propic', 'propic_fursuiter']:
|
||||||
|
@ -49,8 +60,17 @@ async def upload_propic(request, order: Order):
|
||||||
|
|
||||||
img = img.convert('RGB')
|
img = img.convert('RGB')
|
||||||
img.thumbnail((512,512))
|
img.thumbnail((512,512))
|
||||||
img.save(f"res/propic/{fn}_{order.code}_{h}.jpg")
|
imgBytes = BytesIO()
|
||||||
except:
|
img.save(imgBytes, format='jpeg')
|
||||||
|
imgBytes = imgBytes.getvalue()
|
||||||
|
|
||||||
|
with open(f"res/propic/{fn}_{order.code}_{h}.jpg", "wb") as f:
|
||||||
|
f.write(imgBytes)
|
||||||
|
|
||||||
|
await order.edit_answer_fileUpload(f'{fn}_file', f'{fn}_file_{order.code}_{h}.jpg', 'image/jpeg', imgBytes)
|
||||||
|
except Exception:
|
||||||
|
import traceback
|
||||||
|
print(traceback.format_exc())
|
||||||
raise exceptions.BadRequest("The image you uploaded is not valid.")
|
raise exceptions.BadRequest("The image you uploaded is not valid.")
|
||||||
else:
|
else:
|
||||||
await order.edit_answer(fn, f"{fn}_{order.code}_{h}.jpg")
|
await order.edit_answer(fn, f"{fn}_{order.code}_{h}.jpg")
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 78 MiB |
Binary file not shown.
After Width: | Height: | Size: 292 KiB |
Binary file not shown.
After Width: | Height: | Size: 208 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Thanks!</title>
|
||||||
|
<style>
|
||||||
|
body {font-family: sans-serif;color:#eee;background:#222;}
|
||||||
|
main {margin: 4em auto;max-width:50em;line-height:2em;}
|
||||||
|
h1, h2 {color:#e90;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h1 id="a">Thanks for participating in Furizon ENABLE_JAVASCRIPT!</h1>
|
||||||
|
<p>We just came back home and we need some time to regain energy and get back to normal! The registration system is currently offline, and we are working on making it better!</p>
|
||||||
|
<h2>When will it be back online?</h2>
|
||||||
|
<p>The registration system was hosted in a server in the convention network itself. After the end of the convention, the network was disassembled. We will soon setup the server again!</p>
|
||||||
|
<h2>Can i contact you?</h2>
|
||||||
|
<p>Yes. Just write to info@furizon.net</p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
<script>document.getElementById("a").innerText = "Thanks for participating in Furizon " + (new Date().getFullYear()) + "!"</script>
|
||||||
|
</html>
|
|
@ -0,0 +1,162 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Countdown to Furizon</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||||
|
<meta name="viewport" content="width=500rem" />
|
||||||
|
<link href="https://fonts.bunny.net/css?family=pt-serif-caption:400" rel="stylesheet" />
|
||||||
|
<meta property="og:title" content="Furizon Overlord · Booking">
|
||||||
|
<meta property="og:description" content="Acquista i biglietti per Furizon Overlord, la convention furry Italiana.">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
*{margin:0;border:0;padding:0;}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'PT Serif Caption';
|
||||||
|
background: url('bg.jpg') center center no-repeat;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
background-color:#111;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: calc(100vh * 1.406)) { /*1.406 is width/height of the image*/
|
||||||
|
.containedBg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url('bg.jpg') center center no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: calc(100vh * 1.406)) and (min-width: calc(100vh * 0.706)) {
|
||||||
|
.containedBg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url('bg.jpg') right center no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: calc(100vh * 0.706)) {
|
||||||
|
.containedBg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url('bgVert.jpg') center center no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
img {
|
||||||
|
display:block;
|
||||||
|
height: 6rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 1em 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #fff;
|
||||||
|
width:100%;
|
||||||
|
box-sizing:border-box;
|
||||||
|
background:rgba(0,0,0,0.5);
|
||||||
|
text-align:center;
|
||||||
|
position:absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
color: #FCB53F;
|
||||||
|
font-size: 0.5em;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main a {
|
||||||
|
font-size:1.6em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
color: #FCB53F;
|
||||||
|
background:#000;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-decoration:none;
|
||||||
|
padding: 0.3em 0.6em;
|
||||||
|
display:inline-block;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
main a:hover {
|
||||||
|
background:#FCB53F;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#timer {display:block;max-width:30em;margin: 0 auto;}
|
||||||
|
#clock {display:block;}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<!-- Matomo -->
|
||||||
|
<script>
|
||||||
|
var _paq = window._paq = window._paq || [];
|
||||||
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
var u="https://y.foxo.me/";
|
||||||
|
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||||
|
_paq.push(['setSiteId', '2']);
|
||||||
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<!-- End Matomo Code -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="containedBg"></div>
|
||||||
|
<main>
|
||||||
|
<div id="timer">
|
||||||
|
<img src="https://furizon.net/wp-content/uploads/2022/08/Logo_base_no_sfondoo_Furizon_1-1.png" />
|
||||||
|
<h2>Overlord Registration</h2>
|
||||||
|
<span id="clock">Enjoy furizon~</span>
|
||||||
|
<a id="button" href="https://reg.furizon.net/furizon/overlord/" style="display:none">Book now!</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
// Set the date we're counting down to
|
||||||
|
var countDownDate = 1705143600 * 1000;
|
||||||
|
|
||||||
|
var now = new Date().getTime();
|
||||||
|
if(now <= countDownDate) {
|
||||||
|
// Update the count down every 1 second
|
||||||
|
var x = setInterval(function() {
|
||||||
|
|
||||||
|
// Get today's date and time
|
||||||
|
var now = new Date().getTime();
|
||||||
|
|
||||||
|
// Find the distance between now and the count down date
|
||||||
|
var distance = countDownDate - now;
|
||||||
|
|
||||||
|
// Time calculations for days, hours, minutes and seconds
|
||||||
|
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
|
||||||
|
var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||||
|
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
var seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
||||||
|
|
||||||
|
// Display the result in the element with id="timer"
|
||||||
|
document.getElementById("clock").innerHTML = days + "<em>d</em> " + hours + "<em>h</em> "
|
||||||
|
+ minutes + "<em>m</em> " + seconds + "<em>s</em>";
|
||||||
|
|
||||||
|
// If the count down is finished, write some text
|
||||||
|
if (distance < 0) {
|
||||||
|
clearInterval(x);
|
||||||
|
document.getElementById("button").style.display = 'block';
|
||||||
|
document.getElementById("clock").style.display = 'none';
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
document.getElementById("button").style.display = 'block';
|
||||||
|
document.getElementById("clock").style.display = 'none';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Countdown to Furizon</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||||
|
<meta name="viewport" content="width=500rem" />
|
||||||
|
<link href="https://fonts.bunny.net/css?family=pt-serif-caption:400" rel="stylesheet" />
|
||||||
|
<meta property="og:title" content="Furizon Beyond · Booking">
|
||||||
|
<meta property="og:description" content="Acquista i biglietti per Furizon Beyond, la convention furry Italiana.">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
*{margin:0;border:0;padding:0;}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'PT Serif Caption';
|
||||||
|
background-image: url('https://furizon.net/wp-content/uploads/2022/12/Header-banner.jpg?id=1635');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right;
|
||||||
|
background-size: cover;
|
||||||
|
background-color:#111;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
display:block;
|
||||||
|
height: 6rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 1em 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #fff;
|
||||||
|
width:100%;
|
||||||
|
box-sizing:border-box;
|
||||||
|
background:rgba(0,0,0,0.5);
|
||||||
|
text-align:center;
|
||||||
|
position:absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
color: #FCB53F;
|
||||||
|
font-size: 0.5em;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main a {
|
||||||
|
font-size:1.6em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
color: #FCB53F;
|
||||||
|
background:#000;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-decoration:none;
|
||||||
|
padding: 0.3em 0.6em;
|
||||||
|
display:inline-block;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
main a:hover {
|
||||||
|
background:#FCB53F;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#timer {display:block;max-width:30em;margin: 0 auto;}
|
||||||
|
#clock {display:block;}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<!-- Matomo -->
|
||||||
|
<script>
|
||||||
|
var _paq = window._paq = window._paq || [];
|
||||||
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
var u="https://y.foxo.me/";
|
||||||
|
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||||
|
_paq.push(['setSiteId', '2']);
|
||||||
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<!-- End Matomo Code -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div id="timer">
|
||||||
|
<img src="https://furizon.net/wp-content/uploads/2022/08/Logo_base_no_sfondoo_Furizon_1-1.png" />
|
||||||
|
<h2>Thank you to everybody who registered for Furizon 2023!</h2>
|
||||||
|
<p>Too late to register? You can still sign up to the waiting list! We will contact you as soon as a spot is available for you~</p>
|
||||||
|
<!--<span id="clock">Enjoy furizon~</span>-->
|
||||||
|
<a id="button" href="https://reg.furizon.net/furizon/beyond/waitinglist?item=38">Join waiting list</a>
|
||||||
|
<a id="button" href="https://reg.furizon.net/furizon/beyond/redeem">Redeem code</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<!--<script>
|
||||||
|
// Set the date we're counting down to
|
||||||
|
var countDownDate = 1685455200 * 1000;
|
||||||
|
|
||||||
|
var now = new Date().getTime();
|
||||||
|
if(now <= countDownDate) {
|
||||||
|
// Update the count down every 1 second
|
||||||
|
var x = setInterval(function() {
|
||||||
|
|
||||||
|
// Get today's date and time
|
||||||
|
var now = new Date().getTime();
|
||||||
|
|
||||||
|
// Find the distance between now and the count down date
|
||||||
|
var distance = countDownDate - now;
|
||||||
|
|
||||||
|
// Time calculations for days, hours, minutes and seconds
|
||||||
|
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
|
||||||
|
var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||||
|
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
var seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
||||||
|
|
||||||
|
// Display the result in the element with id="timer"
|
||||||
|
document.getElementById("clock").innerHTML = days + "<em>d</em> " + hours + "<em>h</em> "
|
||||||
|
+ minutes + "<em>m</em> " + seconds + "<em>s</em>";
|
||||||
|
|
||||||
|
// If the count down is finished, write some text
|
||||||
|
if (distance < 0) {
|
||||||
|
clearInterval(x);
|
||||||
|
document.getElementById("clock").style.display = 'none';
|
||||||
|
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
</script>-->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 280 KiB |
|
@ -94,13 +94,13 @@ function clock() {
|
||||||
currentTime = new Date();
|
currentTime = new Date();
|
||||||
let ts = currentTime.toString();
|
let ts = currentTime.toString();
|
||||||
ts = ts.replace("GMT+0200 (Central European Summer Time)", "");
|
ts = ts.replace("GMT+0200 (Central European Summer Time)", "");
|
||||||
ts = ts.replace("2023", "<br />");
|
ts = ts.replace("2024", "<br />");
|
||||||
document.getElementById("clock").innerHTML = ts;
|
document.getElementById("clock").innerHTML = ts;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fastForward() {
|
function fastForward() {
|
||||||
currentTime = new Date("2023-05-29T18:00Z");
|
currentTime = new Date("2024-06-03T18:00Z");
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
updateDivs(cachedData);
|
updateDivs(cachedData);
|
||||||
currentTime.setMinutes(currentTime.getMinutes()+1);
|
currentTime.setMinutes(currentTime.getMinutes()+1);
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 8.1 KiB |
63
room.py
63
room.py
|
@ -18,6 +18,9 @@ async def room_create_post(request, order: Order):
|
||||||
|
|
||||||
if order.room_id:
|
if order.room_id:
|
||||||
error = "You are already in another room. You need to delete (if it's yours) or leave it before creating another."
|
error = "You are already in another room. You need to delete (if it's yours) or leave it before creating another."
|
||||||
|
|
||||||
|
if order.daily:
|
||||||
|
raise exceptions.BadRequest("You cannot create a room if you have a daily ticket!")
|
||||||
|
|
||||||
if not error:
|
if not error:
|
||||||
await order.edit_answer('room_name', name)
|
await order.edit_answer('room_name', name)
|
||||||
|
@ -35,6 +38,9 @@ async def room_create(request, order: Order):
|
||||||
|
|
||||||
if not order: raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
if not order: raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
||||||
|
|
||||||
|
if order.daily:
|
||||||
|
raise exceptions.BadRequest("You cannot create a room if you have a daily ticket!")
|
||||||
|
|
||||||
tpl = request.app.ctx.tpl.get_template('create_room.html')
|
tpl = request.app.ctx.tpl.get_template('create_room.html')
|
||||||
return html(tpl.render(order=order))
|
return html(tpl.render(order=order))
|
||||||
|
|
||||||
|
@ -70,6 +76,9 @@ async def join_room(request, order: Order):
|
||||||
|
|
||||||
if order.room_id:
|
if order.room_id:
|
||||||
raise exceptions.BadRequest("You are in another room already. Why would you join another?")
|
raise exceptions.BadRequest("You are in another room already. Why would you join another?")
|
||||||
|
|
||||||
|
if order.daily:
|
||||||
|
raise exceptions.BadRequest("You cannot join a room if you have a daily ticket!")
|
||||||
|
|
||||||
code = request.form.get('code').strip()
|
code = request.form.get('code').strip()
|
||||||
room_secret = request.form.get('room_secret').strip()
|
room_secret = request.form.get('room_secret').strip()
|
||||||
|
@ -87,6 +96,9 @@ async def join_room(request, order: Order):
|
||||||
|
|
||||||
if room_owner.room_confirmed:
|
if room_owner.room_confirmed:
|
||||||
raise exceptions.BadRequest("The room you're trying to join has been confirmed already")
|
raise exceptions.BadRequest("The room you're trying to join has been confirmed already")
|
||||||
|
|
||||||
|
if room_owner.bed_in_room != order.bed_in_room:
|
||||||
|
raise exceptions.BadRequest("This room's ticket is of a different type than yours!")
|
||||||
|
|
||||||
#if room_owner.pending_roommates and (order.code in room_owner.pending_roommates):
|
#if room_owner.pending_roommates and (order.code in room_owner.pending_roommates):
|
||||||
#raise exceptions.BadRequest("What? You should never reach this check, but whatever...")
|
#raise exceptions.BadRequest("What? You should never reach this check, but whatever...")
|
||||||
|
@ -252,9 +264,11 @@ async def confirm_room(request, order: Order, quotas: Quotas):
|
||||||
if order.room_id != order.code:
|
if order.room_id != order.code:
|
||||||
raise exceptions.BadRequest("You are not allowed to confirm rooms of others.")
|
raise exceptions.BadRequest("You are not allowed to confirm rooms of others.")
|
||||||
|
|
||||||
if quotas.get_left(len(order.room_members)) == 0:
|
# This is not needed anymore you buy tickets already
|
||||||
raise exceptions.BadRequest("There are no more rooms of this size to reserve.")
|
#if quotas.get_left(len(order.room_members)) == 0:
|
||||||
|
# raise exceptions.BadRequest("There are no more rooms of this size to reserve.")
|
||||||
|
|
||||||
|
bed_in_room = order.bed_in_room # Variation id of the ticket for that kind of room
|
||||||
room_members = []
|
room_members = []
|
||||||
for m in order.room_members:
|
for m in order.room_members:
|
||||||
if m == order.code:
|
if m == order.code:
|
||||||
|
@ -267,8 +281,18 @@ async def confirm_room(request, order: Order, quotas: Quotas):
|
||||||
|
|
||||||
if res.status != 'paid':
|
if res.status != 'paid':
|
||||||
raise exceptions.BadRequest("Somebody hasn't paid.")
|
raise exceptions.BadRequest("Somebody hasn't paid.")
|
||||||
|
|
||||||
|
if res.bed_in_room != bed_in_room:
|
||||||
|
raise exceptions.BadRequest("Somebody has a ticket for a different type of room!")
|
||||||
|
|
||||||
|
if res.daily:
|
||||||
|
raise exceptions.BadRequest("Somebody in your room has a daily ticket!")
|
||||||
|
|
||||||
room_members.append(res)
|
room_members.append(res)
|
||||||
|
|
||||||
|
|
||||||
|
if len(room_members) != order.room_person_no and order.room_person_no != None:
|
||||||
|
raise exceptions.BadRequest("The number of people in your room mismatches your type of ticket!")
|
||||||
|
|
||||||
for rm in room_members:
|
for rm in room_members:
|
||||||
await rm.edit_answer('room_id', order.code)
|
await rm.edit_answer('room_id', order.code)
|
||||||
|
@ -276,28 +300,19 @@ async def confirm_room(request, order: Order, quotas: Quotas):
|
||||||
await rm.edit_answer('pending_roommates', None)
|
await rm.edit_answer('pending_roommates', None)
|
||||||
await rm.edit_answer('pending_room', None)
|
await rm.edit_answer('pending_room', None)
|
||||||
|
|
||||||
thing = {
|
# This should now be useless because in the ticket there already is the ticket/room type
|
||||||
'order': order.code,
|
# thing = {
|
||||||
'addon_to': order.position_positionid,
|
# 'order': order.code,
|
||||||
'item': ITEM_IDS['room'],
|
# 'addon_to': order.position_positionid,
|
||||||
'variation': ROOM_MAP[len(room_members)]
|
# 'item': ITEM_IDS['room'],
|
||||||
}
|
# 'variation': ROOM_MAP[len(room_members)]
|
||||||
|
# }
|
||||||
async with httpx.AsyncClient() as client:
|
#
|
||||||
res = await client.post(join(base_url, "orderpositions/"), headers=headers, json=thing)
|
# async with httpx.AsyncClient() as client:
|
||||||
|
# res = await client.post(join(base_url_event, "orderpositions/"), headers=headers, json=thing)
|
||||||
if res.status_code != 201:
|
#
|
||||||
raise exceptions.BadRequest("Something has gone wrong! Please contact support immediately")
|
# if res.status_code != 201:
|
||||||
|
# raise exceptions.BadRequest("Something has gone wrong! Please contact support immediately")
|
||||||
'''for rm in room_members:
|
|
||||||
if rm.code == order.code: continue
|
|
||||||
thing = {
|
|
||||||
'order': rm.code,
|
|
||||||
'addon_to': rm.position_positionid,
|
|
||||||
'item': ITEM_IDS['room'],
|
|
||||||
'variation': ROOM_MAP[len(room_members)]
|
|
||||||
}
|
|
||||||
res = await client.post(join(base_url, "orderpositions/"), headers=headers, json=thing) '''
|
|
||||||
|
|
||||||
for rm in room_members:
|
for rm in room_members:
|
||||||
await rm.send_answers()
|
await rm.send_answers()
|
||||||
|
|
9
stats.py
9
stats.py
|
@ -6,7 +6,16 @@ bp = Blueprint("stats", url_prefix="/manage")
|
||||||
|
|
||||||
@bp.route("/nosecount")
|
@bp.route("/nosecount")
|
||||||
async def nose_count(request, order: Order):
|
async def nose_count(request, order: Order):
|
||||||
|
await request.app.ctx.om.updateCache()
|
||||||
orders = {key:value for key,value in sorted(request.app.ctx.om.cache.items(), key=lambda x: len(x[1].room_members), reverse=True) if value.status not in ['c', 'e']}
|
orders = {key:value for key,value in sorted(request.app.ctx.om.cache.items(), key=lambda x: len(x[1].room_members), reverse=True) if value.status not in ['c', 'e']}
|
||||||
|
|
||||||
tpl = request.app.ctx.tpl.get_template('nosecount.html')
|
tpl = request.app.ctx.tpl.get_template('nosecount.html')
|
||||||
return html(tpl.render(orders=orders, order=order))
|
return html(tpl.render(orders=orders, order=order))
|
||||||
|
|
||||||
|
@bp.route("/fursuitcount")
|
||||||
|
async def fursuit_count(request, order: Order):
|
||||||
|
await request.app.ctx.om.updateCache()
|
||||||
|
orders = {key:value for key,value in sorted(request.app.ctx.om.cache.items(), key=lambda x: len(x[1].room_members), reverse=True) if value.status not in ['c', 'e']}
|
||||||
|
|
||||||
|
tpl = request.app.ctx.tpl.get_template('fursuitcount.html')
|
||||||
|
return html(tpl.render(orders=orders, order=order))
|
|
@ -86,9 +86,11 @@
|
||||||
<a href="/manage/welcome">Your Booking</a>
|
<a href="/manage/welcome">Your Booking</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="/manage/nosecount">Nose Count</a>
|
<a href="/manage/nosecount">Nose Count</a>
|
||||||
|
<a href="/manage/fursuitcount">Fursuit Count</a>
|
||||||
{% if order %}
|
{% if order %}
|
||||||
<a href="/manage/carpooling">Carpooling</a>
|
<a href="/manage/carpooling">Carpooling</a>
|
||||||
<a style="float:right;" href="/manage/logout">Logout</a>
|
<a style="float:right;" href="/manage/logout">Logout</a>
|
||||||
|
<a style="float:right;color: #b1b1b1;">Logged in as <i>{{order.ans('fursona_name')}}</i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br clear="both" />
|
<br clear="both" />
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{% if order.propic_locked %}
|
{% if order.propic_locked %}
|
||||||
<p class="notice">⚠️ You have been limited from further editing your profile pic.</p>
|
<p class="notice">⚠️ You have been limited from further editing your profile pic.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if (not order.ans('propic')) or (order.ans('is_fursuiter') != 'No' and not order.ans('propic_fursuiter')) %}
|
{% if not order.ans('propic') or (order.is_fursuiter and not order.ans('propic_fursuiter')) %}
|
||||||
<p class="notice">⚠️ One or more badge pictures are missing! This will cause you badge to be empty, so make sure to upload something before the deadline!</p>
|
<p class="notice">⚠️ One or more badge pictures are missing! This will cause you badge to be empty, so make sure to upload something before the deadline!</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="POST" enctype="multipart/form-data" action="/manage/propic/upload">
|
<form method="POST" enctype="multipart/form-data" action="/manage/propic/upload">
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>Normal Badge</p>
|
<p>Normal Badge</p>
|
||||||
</div>
|
</div>
|
||||||
{% if order.ans('is_fursuiter') != 'No' %}
|
{% if order.is_fursuiter %}
|
||||||
<div>
|
<div>
|
||||||
{% if not order.ans('propic_fursuiter') %}
|
{% if not order.ans('propic_fursuiter') %}
|
||||||
<input type="file" value="" accept="image/jpeg,image/png" name="propic_fursuiter" />
|
<input type="file" value="" accept="image/jpeg,image/png" name="propic_fursuiter" />
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
{% if order.ans('propic_fursuiter') %}
|
{% if order.ans('propic_fursuiter') %}
|
||||||
<input type="submit" name="submit" value="Delete fursuit image" {{'disabled' if time() > PROPIC_DEADLINE else ''}} />
|
<input type="submit" name="submit" value="Delete fursuit image" {{'disabled' if time() > PROPIC_DEADLINE else ''}} />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if (not order.ans('propic')) or (order.ans('is_fursuiter') != 'No' and not order.ans('propic_fursuiter')) %}
|
{% if not order.ans('propic') or (order.is_fursuiter and not order.ans('propic_fursuiter')) %}
|
||||||
<input type="submit" name="submit" value="Upload" />
|
<input type="submit" name="submit" value="Upload" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
{% if order.status == 'paid' and order.room_confirmed %}
|
{% if order.status == 'paid' and order.room_confirmed %}
|
||||||
<p style="text-align:right;"><a href="/manage/download_ticket?name=BEYOND-{{order.code}}.pdf" role="button">Download ticket</a></p>
|
<p style="text-align:right;"><a href="/manage/download_ticket?name=OVERLORD-{{order.code}}.pdf" role="button">Download ticket</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if order.status != 'paid' %}
|
{% if order.status != 'paid' %}
|
||||||
<a href="{{order.url}}"><button>Payment area</button></a>
|
<a href="{{order.url}}"><button>Payment area</button></a>
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<details id="room">
|
<details id="room">
|
||||||
<summary role="button"><img src="/res/icons/bedroom.svg" class="icon"/> Accomodation & Roommates <span class="status">{% if not order.room_confirmed %}⚠️{% endif %}</span></summary>
|
<summary role="button"><img src="/res/icons/bedroom.svg" class="icon"/> Accomodation & Roommates <span class="status">{% if not order.room_confirmed %}⚠️{% endif %}</span></summary>
|
||||||
<h2>Your room {% if room_members %}- {{room_members[0].ans('room_name')}}{% endif %}</h2>
|
<h2 style="margin-bottom:0;">Your room {% if room_members %}- {{room_members[0].ans('room_name')}}{% endif %}</h2>
|
||||||
|
<p><b>Room's type:</b> {{ROOM_TYPE_NAMES[order.bed_in_room]}}.</p>
|
||||||
|
<p class="notice" style="background:#0881c0"><b>Note! </b> Only people with the same room type can be roommates. If you need help, you can check the support guide <a href="https://pornhub.com">clicking here</a>.</p>
|
||||||
|
|
||||||
{# Show alert if room owner has wrong people inside #}
|
{# Show alert if room owner has wrong people inside #}
|
||||||
|
|
||||||
{% if room_members and quota.get_left(len(room_members)) == 0 and (not order.room_confirmed) %}
|
{# {% if room_members and quota.get_left(len(room_members)) == 0 and (not order.room_confirmed) %} #}
|
||||||
<p class="notice">⚠️ Your room contains {{len(room_members)}} people inside, but sadly there are no more {{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}} rooms. You need to add or remove people until you reach the size of an available room if you want to confirm it.</p>
|
{# <p class="notice">⚠️ Your room contains {{len(room_members)}} people inside, but sadly there are no more {{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}} rooms. You need to add or remove people until you reach the size of an available room if you want to confirm it.</p> #}
|
||||||
{% endif %}
|
{# {% endif %} #}
|
||||||
|
|
||||||
{# Show alert if room was not confirmed #}
|
{# Show alert if room was not confirmed #}
|
||||||
{% if order.room_id and not order.room_confirmed %}
|
{% if order.room_id and not order.room_confirmed %}
|
||||||
|
@ -15,7 +17,8 @@
|
||||||
|
|
||||||
{# Show notice if the room is confirmed #}
|
{# Show notice if the room is confirmed #}
|
||||||
{% if order.room_confirmed %}
|
{% if order.room_confirmed %}
|
||||||
<p class="notice" style="background:#060">✅ Your <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room has been confirmed</p>
|
{# <p class="notice" style="background:#060">✅ Your <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room has been confirmed</p> #}
|
||||||
|
<p class="notice" style="background:#060">✅ Your <strong>{{[None,'single','double','triple','quadruple','quintuple'][order.room_person_no]}}</strong> room has been confirmed</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Show roommates if room is set #}
|
{# Show roommates if room is set #}
|
||||||
|
@ -30,7 +33,7 @@
|
||||||
</div>
|
</div>
|
||||||
<h3>{{person.ans('fursona_name')}}</h3>
|
<h3>{{person.ans('fursona_name')}}</h3>
|
||||||
{% if person.code == order.room_id %}<p><strong style="color:#c6f">ROOM OWNER</strong></p>{% endif %}
|
{% if person.code == order.room_id %}<p><strong style="color:#c6f">ROOM OWNER</strong></p>{% endif %}
|
||||||
<p>{{person.ans('staff_title') if person.ans('staff_title') else ''}} {{'Fursuiter' if person.ans('is_fursuiter') != 'No'}}</p>
|
<p>{{person.ans('staff_title') if person.ans('staff_title') else ''}} {{'Fursuiter' if person.is_fursuiter}}</p>
|
||||||
{% if person.status == 'pending' %}
|
{% if person.status == 'pending' %}
|
||||||
<p><strong style="color:red;">UNPAID</strong></p>
|
<p><strong style="color:red;">UNPAID</strong></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -42,7 +45,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if order.room_id == order.code and not order.room_confirmed and len(room_members) < 5 %}
|
{# {% if order.room_id == order.code and not order.room_confirmed and len(room_members) < 5%} #}
|
||||||
|
{% if order.room_id == order.code and not order.room_confirmed and len(room_members) < order.room_person_no %}
|
||||||
<div>
|
<div>
|
||||||
<a href="javascript:document.getElementById('modal-roominvite').setAttribute('open', 'true');">
|
<a href="javascript:document.getElementById('modal-roominvite').setAttribute('open', 'true');">
|
||||||
<div class="propic-container">
|
<div class="propic-container">
|
||||||
|
@ -79,7 +83,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if not order.room_confirmed %}
|
{% if not order.room_confirmed %}
|
||||||
<a role="button" {% if not room.forbidden and quota.get_left(len(room_members)) > 0 %}href="javascript:document.getElementById('modal-roomconfirm').setAttribute('open', 'true');"{% endif %}>Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room</a>
|
{# <a role="button" {% if not room.forbidden and quota.get_left(len(room_members)) > 0 %}href="javascript:document.getElementById('modal-roomconfirm').setAttribute('open', 'true');"{% endif %}>Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room</a> #}
|
||||||
|
<a role="button" {% if not room.forbidden and len(room_members) == order.room_person_no %}href="javascript:document.getElementById('modal-roomconfirm').setAttribute('open', 'true');"{% endif %}>Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][order.room_person_no]}}</strong> room</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if order.room_id and not order.room_confirmed %}
|
{% if order.room_id and not order.room_confirmed %}
|
||||||
|
@ -113,13 +118,13 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Room availability is always shown #}
|
{# Room availability is always shown #}
|
||||||
<h4>Room availability</h4>
|
{# <h4>Room availability</h4> #}
|
||||||
<table>
|
{# <table> #}
|
||||||
{% for q in quota.data['results'] if 'Room' in q['name'] %}
|
{# {% for q in quota.data['results'] if 'Room' in q['name'] %} #}
|
||||||
<tr {% if q['available_number'] == 0 %}style="text-decoration:line-through;"{% endif %}>
|
{# <tr {% if q['available_number'] == 0 %}style="text-decoration:line-through;"{% endif %}> #}
|
||||||
<td>{{q['name']}}</td>
|
{# <td>{{q['name']}}</td> #}
|
||||||
<td>{{q['available_number']}} left</td>
|
{# <td>{{q['available_number']}} left</td> #}
|
||||||
</tr>
|
{# </tr> #}
|
||||||
{% endfor %}
|
{# {% endfor %} #}
|
||||||
</table>
|
{# </table> #}
|
||||||
</details>
|
</details>
|
||||||
|
|
|
@ -29,16 +29,18 @@
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Room type</td>
|
<td>Room type</td>
|
||||||
<td><strong>{{[None,'Single','Double','Triple','Quadruple','Quintuple'][len(room_members)]}} Room</strong></td>
|
{# <td><strong>{{[None,'Single','Double','Triple','Quadruple','Quintuple'][len(room_members)]}} Room</strong></td> #}
|
||||||
</tr>
|
<td><strong>{{[None,'Single','Double','Triple','Quadruple','Quintuple'][order.room_person_no]}} Room</strong></td>
|
||||||
<tr>
|
|
||||||
<td>Rooms left of this type</td>
|
|
||||||
<td><strong>{{quota.get_left(len(room_members))}}</strong></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
{# <tr> #}
|
||||||
|
{# <td>Rooms left of this type</td> #}
|
||||||
|
{# <td><strong>{{quota.get_left(len(room_members))}}</strong></td> #}
|
||||||
|
{# </tr> #}
|
||||||
</table>
|
</table>
|
||||||
<footer>
|
<footer>
|
||||||
<a href="javascript:document.getElementById('modal-roomconfirm').removeAttribute('open')" role="button">Close</a>
|
<a href="javascript:document.getElementById('modal-roomconfirm').removeAttribute('open')" role="button">Close</a>
|
||||||
<a href="/manage/room/confirm" role="button">Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room</a>
|
{# <a href="/manage/room/confirm" role="button">Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room</a> #}
|
||||||
|
<a href="/manage/room/confirm" role="button">Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][order.room_person_no]}}</strong> room</a>
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Furizon 2023 Carpooling{% endblock %}
|
{% block title %}Furizon 2024 Carpooling{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<header>
|
<header>
|
||||||
|
@ -42,13 +42,13 @@
|
||||||
Day of departure
|
Day of departure
|
||||||
|
|
||||||
<select name="day_departure" id="day_departure">
|
<select name="day_departure" id="day_departure">
|
||||||
<option value="m29th" {{'selected' if order.carpooling_message.day_departure == 'm29th'}}>May 29th</option>
|
|
||||||
<option value="m30th" {{'selected' if order.carpooling_message.day_departure == 'm30th'}}>May 30th</option>
|
|
||||||
<option value="m31st" {{'selected' if order.carpooling_message.day_departure == 'm31st'}}>May 31st</option>
|
|
||||||
<option value="j1st" {{'selected' if order.carpooling_message.day_departure == 'j1st'}}>June 1st</option>
|
|
||||||
<option value="j2nd" {{'selected' if order.carpooling_message.day_departure == 'j2nd'}}>June 2nd</option>
|
|
||||||
<option value="j3rd" {{'selected' if order.carpooling_message.day_departure == 'j3rd'}}>June 3rd</option>
|
<option value="j3rd" {{'selected' if order.carpooling_message.day_departure == 'j3rd'}}>June 3rd</option>
|
||||||
<option value="j4th" {{'selected' if order.carpooling_message.day_departure == 'j4th'}}>June 4th</option>
|
<option value="j4th" {{'selected' if order.carpooling_message.day_departure == 'j4th'}}>June 4th</option>
|
||||||
|
<option value="j5th" {{'selected' if order.carpooling_message.day_departure == 'j5th'}}>June 5th</option>
|
||||||
|
<option value="j6th" {{'selected' if order.carpooling_message.day_departure == 'j6th'}}>June 6th</option>
|
||||||
|
<option value="j7th" {{'selected' if order.carpooling_message.day_departure == 'j7th'}}>June 7th</option>
|
||||||
|
<option value="j8th" {{'selected' if order.carpooling_message.day_departure == 'j8th'}}>June 8th</option>
|
||||||
|
<option value="j9th" {{'selected' if order.carpooling_message.day_departure == 'j9th'}}>June 9th</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<textarea id="message" name="message" style="height:10em;" placeholder="Write here your message" required>{{order.carpooling_message.message}}</textarea>
|
<textarea id="message" name="message" style="height:10em;" placeholder="Write here your message" required>{{order.carpooling_message.message}}</textarea>
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Furizon 2024 Fursuitcount{% endblock %}
|
||||||
|
{% block main %}
|
||||||
|
<main class="container">
|
||||||
|
<header>
|
||||||
|
<picture>
|
||||||
|
<source srcset="/res/furizon.png" media="(prefers-color-scheme:dark)">
|
||||||
|
<img src="/res/furizon-light.png" style="height:4rem;text-align:center;">
|
||||||
|
</picture>
|
||||||
|
</header>
|
||||||
|
<!--h2>Fursuit-count!</h2-->
|
||||||
|
<p>Welcome to the fursuit-count page! Here you can see all of the fursuits that you'll find at Furizon!</p>
|
||||||
|
{% for person in orders.values() if person.is_fursuiter%}
|
||||||
|
{% if loop.first %}
|
||||||
|
<div class="grid people" style="padding-bottom:1em;">
|
||||||
|
{% endif %}
|
||||||
|
<div style="margin-bottom: 1em;">
|
||||||
|
<div class="propic-container">
|
||||||
|
<img class="propic propic-{{person.sponsorship}}" src="/res/propic/{{person.ans('propic_fursuiter') or 'default.png'}}" />
|
||||||
|
<img class="propic-flag" src="/res/flags/{{person.country.lower()}}.svg" />
|
||||||
|
</div>
|
||||||
|
<h5>{{person.ans('fursona_name')}}</h5>
|
||||||
|
</div>
|
||||||
|
{% if loop.last %}</div>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Furizon 2023 Karaoke Admin{% endblock %}
|
{% block title %}Furizon 2024 Karaoke Admin{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<h1>Karaoke Admin</h1>
|
<h1>Karaoke Admin</h1>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Furizon 2023 Nosecount{% endblock %}
|
{% block title %}Furizon 2024 Nosecount{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<header>
|
<header>
|
||||||
|
@ -29,13 +29,14 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for o in orders.values() if (o.code == o.room_id and not o.room_confirmed and len(o.room_members) > 1) %}
|
{% for o in orders.values() if (o.code == o.room_id and not o.room_confirmed) %}
|
||||||
{% if loop.first %}
|
{% if loop.first %}
|
||||||
<hr />
|
<hr />
|
||||||
<h1>Unconfirmed rooms</h1>
|
<h1>Unconfirmed rooms</h1>
|
||||||
<p>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</p>
|
<p>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</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h3>{{o.room_name}}</h3>
|
<h3 style="display:inline-block">{{o.room_name}}</h3>
|
||||||
|
{% if o.room_person_no - len(o.room_members) > 0 %} <p style="display:inline-block"> - Remaining slots: {{o.room_person_no - len(o.room_members)}}</p> {% endif %}
|
||||||
<div class="grid people" style="padding-bottom:1em;">
|
<div class="grid people" style="padding-bottom:1em;">
|
||||||
{% for m in o.room_members %}
|
{% for m in o.room_members %}
|
||||||
{% if m in orders %}
|
{% if m in orders %}
|
||||||
|
@ -54,7 +55,7 @@
|
||||||
{% if loop.last %}</div>{% endif %}
|
{% if loop.last %}</div>{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% for person in orders.values() if (not person.room_id or len(person.room_members) == 1) and (not person.room_confirmed)%}
|
{% for person in orders.values() if (not person.room_id or len(person.room_members) == 1) and (not person.room_confirmed) and not person.daily %}
|
||||||
{% if loop.first %}
|
{% if loop.first %}
|
||||||
<hr />
|
<hr />
|
||||||
<h1>Roomless furs</h1>
|
<h1>Roomless furs</h1>
|
||||||
|
@ -69,7 +70,24 @@
|
||||||
<h5>{{person.ans('fursona_name')}}</h5>
|
<h5>{{person.ans('fursona_name')}}</h5>
|
||||||
</div>
|
</div>
|
||||||
{% if loop.last %}</div>{% endif %}
|
{% if loop.last %}</div>{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for person in orders.values() if person.daily %}
|
||||||
|
{% if loop.first %}
|
||||||
|
<hr />
|
||||||
|
<h1>Daily furs!</h1>
|
||||||
|
<p>These furs will not stay in our hotels, but may be there with us just a few days!</p>
|
||||||
|
<div class="grid people" style="padding-bottom:1em;">
|
||||||
|
{% endif %}
|
||||||
|
<div style="margin-bottom: 1em;">
|
||||||
|
<div class="propic-container">
|
||||||
|
<img class="propic propic-{{person.sponsorship}}" src="/res/propic/{{person.ans('propic') or 'default.png'}}" />
|
||||||
|
<img class="propic-flag" src="/res/flags/{{person.country.lower()}}.svg" />
|
||||||
|
</div>
|
||||||
|
<h5>{{person.ans('fursona_name')}}</h5>
|
||||||
|
</div>
|
||||||
|
{% if loop.last %}</div>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Furizon 2023 Nosecount{% endblock %}
|
{% block title %}Furizon 2024 Nosecount{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<header>
|
<header>
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
<img src="/res/furizon-light.png" style="height:4rem;text-align:center;">
|
<img src="/res/furizon-light.png" style="height:4rem;text-align:center;">
|
||||||
</picture>
|
</picture>
|
||||||
</header>
|
</header>
|
||||||
<p class="notice" style="margin:2em 0;">⚠️ This privacy policy is a courtesy explanation of <a href="https://furizon.net/wp-content/uploads/2023/01/Regolamento-Furizon-Beyond-en-GB.pdf">the one you signed up when registering to the con</a></p>
|
{# <p class="notice" style="margin:2em 0;">⚠️ This privacy policy is a courtesy explanation of <a href="https://furizon.net/wp-content/uploads/2023/01/Regolamento-Furizon-Beyond-en-GB.pdf">the one you signed up when registering to the con</a></p> #}
|
||||||
<h1>Privacy policy of this private area</h1>
|
<h1>Privacy policy of this private area</h1>
|
||||||
<p>We collect only the data that is needed by law and to make sure that your convention experience is up to your expectations. Keep reading to know why and how we collect this data.</p>
|
<p>We collect only the data that is needed by law and to make sure that your convention experience is up to your expectations. Keep reading to know why and how we collect this data.</p>
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<p>From here, you can easily manage all aspects of your booking, including composing your hotel room, designing your badge, and updating your payment information. Simply use the buttons below to navigate between the different sections.</p>
|
<p>From here, you can easily manage all aspects of your booking, including composing your hotel room, designing your badge, and updating your payment information. Simply use the buttons below to navigate between the different sections.</p>
|
||||||
<p>Buttons marked with ⚠️ require your attention</p>
|
<p>Buttons marked with ⚠️ require your attention</p>
|
||||||
|
|
||||||
<p>If you have any questions or issues while using this page, please don't hesitate to contact us for assistance. We look forward to seeing you at Furizon Riverside!</p>
|
<p>If you have any questions or issues while using this page, please don't hesitate to contact us for assistance. We look forward to seeing you at Furizon Overlord!</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="info">Useful information</h2>
|
<h2 id="info">Useful information</h2>
|
||||||
<table>
|
<table>
|
||||||
|
@ -24,13 +24,14 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>When{{' (convention)' if order.has_early or order.has_late else ''}}?</th>
|
<th>When{{' (convention)' if order.has_early or order.has_late else ''}}?</th>
|
||||||
<td>13 October → 15 October 2023</td></td>
|
{# This should be early/late excluded! #}
|
||||||
|
<td>4 June → 8 June 2024</td></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if order.has_early or order.has_late %}
|
{% if order.has_early or order.has_late %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>When (check-in)?</th>
|
<th>When (check-in)?</th>
|
||||||
<td>
|
<td>
|
||||||
{{('12' if order.has_early else '13')|safe}} October → {{('16' if order.has_late else '15')|safe}} October 2023
|
{{('3' if order.has_early else '4')|safe}} October → {{('9' if order.has_late else '8')|safe}} June 2024
|
||||||
{% if order.has_early %}
|
{% if order.has_early %}
|
||||||
<span class="tag">EARLY</span>
|
<span class="tag">EARLY</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -53,7 +54,7 @@
|
||||||
{% if order.status == 'paid' and order.room_confirmed %}
|
{% if order.status == 'paid' and order.room_confirmed %}
|
||||||
<br />
|
<br />
|
||||||
<img src="/res/icons/pdf.svg" class="icon" />
|
<img src="/res/icons/pdf.svg" class="icon" />
|
||||||
<a href="/manage/download_ticket?name=RIVERSIDE-{{order.code}}.pdf" target="_blank">Download ticket PDF</a>
|
<a href="/manage/download_ticket?name=OVERLORD-{{order.code}}.pdf" target="_blank">Download ticket PDF</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -61,7 +62,7 @@
|
||||||
|
|
||||||
<h2>Manage your booking</h2>
|
<h2>Manage your booking</h2>
|
||||||
{% include 'blocks/payment.html' %}
|
{% include 'blocks/payment.html' %}
|
||||||
{% if order.position_positiontypeid not in ITEM_IDS['daily'] %}
|
{% if not order.daily %}
|
||||||
{% include 'blocks/room.html' %}
|
{% include 'blocks/room.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'blocks/badge.html' %}
|
{% include 'blocks/badge.html' %}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
from os.path import join
|
||||||
|
from config import *
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
QUESTION_TYPES = { #https://docs.pretix.eu/en/latest/api/resources/questions.html
|
||||||
|
"number": "N",
|
||||||
|
"one_line_string": "S",
|
||||||
|
"multi_line_string": "T",
|
||||||
|
"boolean": "B",
|
||||||
|
"choice_from_list": "C",
|
||||||
|
"multiple_choice_from_list": "M",
|
||||||
|
"file_upload": "F",
|
||||||
|
"date": "D",
|
||||||
|
"time": "H",
|
||||||
|
"date_time": "W",
|
||||||
|
"country_code": "CC",
|
||||||
|
"telephone_number": "TEL"
|
||||||
|
}
|
||||||
|
TYPE_OF_QUESTIONS = {} # maps questionId -> type
|
||||||
|
|
||||||
|
|
||||||
|
async def loadQuestions():
|
||||||
|
global TYPE_OF_QUESTIONS
|
||||||
|
TYPE_OF_QUESTIONS.clear()
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
p = 0
|
||||||
|
while 1:
|
||||||
|
p += 1
|
||||||
|
res = await client.get(join(base_url_event, f"questions/?page={p}"), headers=headers)
|
||||||
|
|
||||||
|
if res.status_code == 404: break
|
||||||
|
|
||||||
|
data = res.json()
|
||||||
|
for q in data['results']:
|
||||||
|
TYPE_OF_QUESTIONS[q['id']] = q['type']
|
Loading…
Reference in New Issue