diff --git a/.gitignore b/.gitignore
index d8d1bb8..17840cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -161,3 +161,5 @@ res/propic/propic_*
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
config.py
+furizon_webinit_riverside2023.tar.gz
+diomerdas
diff --git a/README.md b/README.md
index e69de29..88f0cc1 100644
--- a/README.md
+++ 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/app.py b/app.py
index 88e663a..fa2a1b1 100644
--- a/app.py
+++ b/app.py
@@ -10,6 +10,7 @@ from os.path import join
from ext import *
from config import *
from aztec_code_generator import AztecCode
+from propic import resetDefaultPropic
from io import BytesIO
from asyncio import Queue
import sqlite3
@@ -32,7 +33,7 @@ from carpooling import bp as carpooling_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.exception(exceptions.SanicException)
async def clear_session(request, exception):
tpl = app.ctx.tpl.get_template('error.html')
@@ -50,7 +51,7 @@ async def main_start(*_):
app.ctx.om = OrderManager()
if FILL_CACHE:
log.info("Filling cache!")
- await app.ctx.om.fill_cache()
+ await app.ctx.om.updateCache()
log.info("Cache fill done!")
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(PROPIC_DEADLINE=PROPIC_DEADLINE)
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(len=len)
@@ -80,7 +82,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)
+ res = await client.get(join(base_url_event, 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.")
@@ -102,6 +104,11 @@ async def welcome(request, order: Order, quota: Quotas):
if not order:
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 = []
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.")
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:
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")
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__":
app.run(host="0.0.0.0", port=8188, dev=DEV_MODE)
diff --git a/checkin.py b/checkin.py
index 03904d1..a51e14f 100644
--- a/checkin.py
+++ b/checkin.py
@@ -2,7 +2,7 @@ from sanic.response import html, redirect, text
from sanic import Blueprint, exceptions, response
from random import choice
from ext import *
-from config import headers, base_url
+from config import headers, base_url_event
from PIL import Image
from os.path import isfile
from os import unlink
@@ -64,7 +64,7 @@ async def do_checkin(request):
if not order.checked_in:
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')
return html(tpl.render(order=order, room_owner=room_owner, roommates=roommates))
diff --git a/config.example.py b/config.example.py
index de039d9..98eb2fc 100644
--- a/config.example.py
+++ b/config.example.py
@@ -1,32 +1,53 @@
+API_TOKEN = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
ORGANIZER = 'furizon'
-EVENT_NAME = 'river-side-2023'
-API_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxx'
-HOSTNAME = 'your-pretix-hostname'
+EVENT_NAME = 'overlord'
+HOSTNAME = 'reg.furizon.net'
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
+CACHE_EXPIRE_TIME = 60 * 60 * 4
DEV_MODE = True
ITEM_IDS = {
- 'ticket': [90,],
- 'membership_card': [91,],
- 'sponsorship': [], # first one = normal, second = super
- 'early_arrival': [],
- 'late_departure': [],
- 'room': 98
+ 'ticket': [126, 127, 155],
+ 'membership_card': [128,],
+ 'sponsorship': [55, 56], # first one = normal, second = super
+ 'early_arrival': [133],
+ 'late_departure': [134],
+ '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.
+# Map variationId -> numberOfPeopleInRoom
ROOM_MAP = {
- 1: 16,
- 2: 17,
- 3: 18,
- 4: 19,
- 5: 20
+ # SACRO CUORE
+ 83: 1,
+ 67: 2,
+ 68: 3,
+ 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.
@@ -36,6 +57,6 @@ TG_CHAT_ID = -1234567
# These order codes have additional functions.
ADMINS = ['XXXXX', 'YYYYY']
-SMTP_HOST = 'your-smtp-host.com'
-SMTP_USER = 'username'
-SMTP_PASSWORD = 'password'
+SMTP_HOST = 'host'
+SMTP_USER = 'user'
+SMTP_PASSWORD = 'pw'
diff --git a/data/boop.db b/data/boop.db
index 5bf5436..4ef87e9 100644
Binary files a/data/boop.db and b/data/boop.db differ
diff --git a/data/event.db b/data/event.db
index 2728c20..9cdb2d5 100644
Binary files a/data/event.db and b/data/event.db differ
diff --git a/ext.py b/ext.py
index e5abb83..af339e8 100644
--- a/ext.py
+++ b/ext.py
@@ -2,6 +2,7 @@ from dataclasses import dataclass
from sanic import Request, exceptions
import httpx
import re
+from utils import *
from config import *
from os.path import join
import json
@@ -32,6 +33,11 @@ class Order:
self.country = 'xx'
self.address = None
self.checked_in = False
+ self.room_type = None
+ self.daily = False
+ self.dailyDays = []
+ self.room_person_no = 0
+ self.answers = []
idata = data['invoice_address']
if idata:
@@ -39,13 +45,22 @@ class Order:
self.country = idata['country']
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_positionid = p['positionid']
self.position_positiontypeid = p['item']
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.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']:
self.has_card = True
@@ -62,6 +77,10 @@ class Order:
if p['item'] == ITEM_IDS['late_departure']:
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.fees = 0
@@ -117,6 +136,18 @@ class Order:
return a['answer']
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):
found = False
self.pending_update = True
@@ -135,7 +166,7 @@ class Order:
if (not found) and (new_answer is not None):
async with httpx.AsyncClient() as client:
- res = await client.get(join(base_url, 'questions/'), headers=headers)
+ res = await client.get(join(base_url_event, 'questions/'), headers=headers)
res = res.json()
for r in res['results']:
@@ -158,7 +189,7 @@ class Order:
del self.answers[i]['options']
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:
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.')
+ 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.time = -1
@@ -183,7 +218,7 @@ class Quotas:
async def get_quotas(request: Request=None):
async with httpx.AsyncClient() as client:
- res = await client.get(join(base_url, 'quotas/?order=id&with_availability=true'), headers=headers)
+ res = await client.get(join(base_url_event, 'quotas/?order=id&with_availability=true'), headers=headers)
res = res.json()
return Quotas(res)
@@ -194,9 +229,20 @@ async def get_order(request: Request=None):
class OrderManager:
def __init__(self):
+ self.lastCacheUpdate = 0
+ self.empty()
+
+ def empty(self):
self.cache = {}
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):
self.cache[order.code] = order
if not order.code in self.order_list:
@@ -208,12 +254,14 @@ class OrderManager:
self.order_list.remove(code)
async def fill_cache(self):
+ await loadQuestions()
+ self.empty()
p = 0
async with httpx.AsyncClient() as client:
while 1:
p += 1
- res = await client.get(join(base_url, f"orders/?page={p}"), headers=headers)
+ res = await client.get(join(base_url_event, f"orders/?page={p}"), headers=headers)
if res.status_code == 404: break
@@ -233,6 +281,7 @@ class OrderManager:
if order.nfc_id == nfc_id:
return order
+ await self.updateCache()
# If a cached order is needed, just get it if available
if code and cached and code in self.cache and time()-self.cache[code].time < 3600:
return self.cache[code]
@@ -246,7 +295,7 @@ class OrderManager:
print('Fetching', code, 'with secret', secret)
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 request:
raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.")
diff --git a/propic.py b/propic.py
index 25852f1..7eef9b4 100644
--- a/propic.py
+++ b/propic.py
@@ -9,6 +9,15 @@ from time import time
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")
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!")
@@ -21,8 +30,10 @@ async def upload_propic(request, order: Order):
if request.form.get('submit') == 'Delete main image':
await order.edit_answer('propic', None)
+ await resetDefaultPropic(request, order, False, sendAnswer=False)
elif request.form.get('submit') == 'Delete fursuit image':
await order.edit_answer('propic_fursuiter', None)
+ await resetDefaultPropic(request, order, True, sendAnswer=False)
else:
for fn, body in request.files.items():
if fn not in ['propic', 'propic_fursuiter']:
@@ -49,8 +60,17 @@ async def upload_propic(request, order: Order):
img = img.convert('RGB')
img.thumbnail((512,512))
- img.save(f"res/propic/{fn}_{order.code}_{h}.jpg")
- except:
+ imgBytes = BytesIO()
+ 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.")
else:
await order.edit_answer(fn, f"{fn}_{order.code}_{h}.jpg")
diff --git a/reg.furizon.net/BannertVertical.png b/reg.furizon.net/BannertVertical.png
new file mode 100644
index 0000000..ba187e8
Binary files /dev/null and b/reg.furizon.net/BannertVertical.png differ
diff --git a/reg.furizon.net/bg.jpg b/reg.furizon.net/bg.jpg
new file mode 100644
index 0000000..4b968c9
Binary files /dev/null and b/reg.furizon.net/bg.jpg differ
diff --git a/reg.furizon.net/bgVert.jpg b/reg.furizon.net/bgVert.jpg
new file mode 100644
index 0000000..edeb9db
Binary files /dev/null and b/reg.furizon.net/bgVert.jpg differ
diff --git a/reg.furizon.net/fz23.html b/reg.furizon.net/fz23.html
new file mode 100644
index 0000000..9f9865d
--- /dev/null
+++ b/reg.furizon.net/fz23.html
@@ -0,0 +1,21 @@
+
+
+ Thanks!
+
+
+
+
+
Thanks for participating in Furizon ENABLE_JAVASCRIPT!
+
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!
+
When will it be back online?
+
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!
+
+
+
+
+
diff --git a/reg.furizon.net/reg.furizon.net.zip b/reg.furizon.net/reg.furizon.net.zip
new file mode 100644
index 0000000..b7b512e
Binary files /dev/null and b/reg.furizon.net/reg.furizon.net.zip differ
diff --git a/reg.furizon.net/riverside.jpg b/reg.furizon.net/riverside.jpg
new file mode 100644
index 0000000..e9cb261
Binary files /dev/null and b/reg.furizon.net/riverside.jpg differ
diff --git a/res/boopbox.js b/res/boopbox.js
index 055110f..dee60d8 100644
--- a/res/boopbox.js
+++ b/res/boopbox.js
@@ -94,13 +94,13 @@ function clock() {
currentTime = new Date();
let ts = currentTime.toString();
ts = ts.replace("GMT+0200 (Central European Summer Time)", "");
- ts = ts.replace("2023", " ");
+ ts = ts.replace("2024", " ");
document.getElementById("clock").innerHTML = ts;
}, 1000);
}
function fastForward() {
- currentTime = new Date("2023-05-29T18:00Z");
+ currentTime = new Date("2024-06-03T18:00Z");
setInterval(() => {
updateDivs(cachedData);
currentTime.setMinutes(currentTime.getMinutes()+1);
diff --git a/res/propic/default.png b/res/propic/default.png
index 5be81dc..5e6415a 100644
Binary files a/res/propic/default.png and b/res/propic/default.png differ
diff --git a/room.py b/room.py
index 32c9a63..fd5760f 100644
--- a/room.py
+++ b/room.py
@@ -18,6 +18,9 @@ async def room_create_post(request, order: Order):
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."
+
+ if order.daily:
+ raise exceptions.BadRequest("You cannot create a room if you have a daily ticket!")
if not error:
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 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')
return html(tpl.render(order=order))
@@ -70,6 +76,9 @@ async def join_room(request, order: Order):
if order.room_id:
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()
room_secret = request.form.get('room_secret').strip()
@@ -87,6 +96,9 @@ async def join_room(request, order: Order):
if room_owner.room_confirmed:
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):
#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:
raise exceptions.BadRequest("You are not allowed to confirm rooms of others.")
- if quotas.get_left(len(order.room_members)) == 0:
- raise exceptions.BadRequest("There are no more rooms of this size to reserve.")
+ # This is not needed anymore you buy tickets already
+ #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 = []
for m in order.room_members:
if m == order.code:
@@ -267,8 +281,18 @@ async def confirm_room(request, order: Order, quotas: Quotas):
if res.status != '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)
+
+
+ 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:
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_room', None)
- thing = {
- 'order': order.code,
- 'addon_to': order.position_positionid,
- '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)
-
- 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) '''
+ # This should now be useless because in the ticket there already is the ticket/room type
+ # thing = {
+ # 'order': order.code,
+ # 'addon_to': order.position_positionid,
+ # 'item': ITEM_IDS['room'],
+ # 'variation': ROOM_MAP[len(room_members)]
+ # }
+ #
+ # 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")
for rm in room_members:
await rm.send_answers()
diff --git a/stats.py b/stats.py
index 2025490..a97ef9b 100644
--- a/stats.py
+++ b/stats.py
@@ -6,7 +6,16 @@ bp = Blueprint("stats", url_prefix="/manage")
@bp.route("/nosecount")
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']}
tpl = request.app.ctx.tpl.get_template('nosecount.html')
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))
\ No newline at end of file
diff --git a/tpl/base.html b/tpl/base.html
index 66da42b..bc5dc0f 100644
--- a/tpl/base.html
+++ b/tpl/base.html
@@ -86,9 +86,11 @@
Your Booking
{% endif %}
Nose Count
+ Fursuit Count
{% if order %}
CarpoolingLogout
+ Logged in as {{order.ans('fursona_name')}}
{% endif %}
diff --git a/tpl/blocks/badge.html b/tpl/blocks/badge.html
index e7f8f7b..ab1bbd1 100644
--- a/tpl/blocks/badge.html
+++ b/tpl/blocks/badge.html
@@ -5,7 +5,7 @@
{% if order.propic_locked %}
⚠️ You have been limited from further editing your profile pic.
{% 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')) %}
⚠️ One or more badge pictures are missing! This will cause you badge to be empty, so make sure to upload something before the deadline!