furizon_webint/ext.py

346 lines
12 KiB
Python
Raw Normal View History

2022-12-18 16:40:39 +00:00
from dataclasses import dataclass
from sanic import Request, exceptions
import httpx
import re
2023-12-30 10:27:42 +00:00
from utils import *
2022-12-18 16:40:39 +00:00
from config import *
from os.path import join
import json
2023-01-17 21:25:35 +00:00
from time import time
2022-12-18 16:40:39 +00:00
@dataclass
class Order:
def __init__(self, data):
2023-01-17 21:25:35 +00:00
self.time = time()
2022-12-18 16:40:39 +00:00
self.data = data
self.status = {'n': 'pending', 'p': 'paid', 'e': 'expired', 'c': 'canceled'}[self.data['status']]
2024-01-13 15:59:24 +00:00
self.secret = data['secret']
if not len(self.data['positions']):
self.status = 'canceled'
2022-12-18 16:40:39 +00:00
self.code = data['code']
2023-01-17 21:25:35 +00:00
self.pending_update = False
2023-05-11 22:18:49 +00:00
self.email = data['email']
self.has_card = False
self.sponsorship = None
2023-01-17 21:25:35 +00:00
self.has_early = False
self.has_late = False
self.first_name = None
self.last_name = None
self.country = 'xx'
self.address = None
self.checked_in = False
2023-12-30 10:27:42 +00:00
self.room_type = None
self.daily = False
self.dailyDays = []
self.room_person_no = 0
self.answers = []
2022-12-18 16:40:39 +00:00
idata = data['invoice_address']
if idata:
self.address = f"{idata['street']} - {idata['zipcode']} {idata['city']} - {idata['country']}"
self.country = idata['country']
2022-12-18 16:40:39 +00:00
for p in self.data['positions']:
if p['item'] in CATEGORIES_LIST_MAP['tickets']:
self.position_id = p['id']
self.position_positionid = p['positionid']
self.position_positiontypeid = p['item']
self.answers = p['answers']
2023-12-30 10:27:42 +00:00
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'])
2023-12-30 10:27:42 +00:00
if p['item'] in CATEGORIES_LIST_MAP['dailys']:
2023-12-30 10:27:42 +00:00
self.daily = True
self.dailyDays.append(CATEGORIES_LIST_MAP['dailys'].index(p['item']))
2022-12-18 16:40:39 +00:00
if p['item'] in CATEGORIES_LIST_MAP['memberships']:
self.has_card = True
if p['item'] == ITEMS_ID_MAP['sponsorship_item']:
sponsorshipType = key_from_value(ITEM_VARIATIONS_MAP['sponsorship_item'], p['variation'])
self.sponsorship = sponsorshipType[0].replace ('sponsorship_item_', '') if len(sponsorshipType) > 0 else None
2023-01-17 21:25:35 +00:00
if p['attendee_name']:
self.first_name = p['attendee_name_parts']['given_name']
self.last_name = p['attendee_name_parts']['family_name']
if p['item'] == ITEMS_ID_MAP['early_arrival_admission']:
2023-01-17 21:25:35 +00:00
self.has_early = True
if p['item'] == ITEMS_ID_MAP['late_departure_admission']:
2023-01-17 21:25:35 +00:00
self.has_late = True
2023-12-30 10:27:42 +00:00
if p['item'] == ITEMS_ID_MAP['bed_in_room']:
roomTypeLst = key_from_value(ITEM_VARIATIONS_MAP['bed_in_room'], p['variation'])
roomTypeId = roomTypeLst[0] if len(roomTypeLst) > 0 else None
2023-12-30 10:27:42 +00:00
self.bed_in_room = p['variation']
self.room_person_no = ROOM_CAPACITY_MAP[roomTypeId] if roomTypeId in ROOM_CAPACITY_MAP else None
2023-01-17 21:25:35 +00:00
self.total = float(data['total'])
self.fees = 0
self.refunds = 0
2023-01-17 21:25:35 +00:00
for fee in data['fees']:
self.fees += float(fee['value'])
for ref in data['refunds']:
self.refunds += float(ref['amount'])
2023-01-17 21:25:35 +00:00
answers = ['payment_provider', 'shirt_size', 'birth_date', 'fursona_name', 'room_confirmed', 'room_id']
self.payment_provider = data['payment_provider']
2023-05-23 19:02:02 +00:00
self.comment = data['comment']
self.phone = data['phone']
2024-01-19 00:15:20 +00:00
self.room_issues = []
self.loadAns()
def loadAns(self):
self.shirt_size = self.ans('shirt_size')
self.is_artist = True if self.ans('is_artist') != 'No' else False
self.is_fursuiter = True if self.ans('is_fursuiter') != 'No' else False
self.is_allergic = True if self.ans('is_allergic') != 'No' else False
self.notes = self.ans('notes')
self.badge_id = int(self.ans('badge_id')) if self.ans('badge_id') else None
self.propic_locked = self.ans('propic_locked')
self.propic_fursuiter = self.ans('propic_fursuiter')
self.propic = self.ans('propic')
self.carpooling_message = json.loads(self.ans('carpooling_message')) if self.ans('carpooling_message') else {}
2023-05-23 19:02:02 +00:00
self.karaoke_songs = json.loads(self.ans('karaoke_songs')) if self.ans('karaoke_songs') else {}
self.birth_date = self.ans('birth_date')
self.birth_location = self.ans('birth_location')
2022-12-18 16:40:39 +00:00
self.name = self.ans('fursona_name')
self.room_id = self.ans('room_id')
self.room_confirmed = self.ans('room_confirmed')
self.room_name = self.ans('room_name')
2022-12-18 16:40:39 +00:00
self.pending_room = self.ans('pending_room')
self.pending_roommates = self.ans('pending_roommates').split(',') if self.ans('pending_roommates') else []
self.room_members = self.ans('room_members').split(',') if self.ans('room_members') else []
2024-01-13 13:58:06 +00:00
self.room_owner = (self.code is not None and self.room_id is not None and self.code.strip() == self.room_id.strip())
2022-12-18 16:40:39 +00:00
self.room_secret = self.ans('room_secret')
2023-05-11 22:18:49 +00:00
self.app_token = self.ans('app_token')
2023-05-23 19:02:02 +00:00
self.nfc_id = self.ans('nfc_id')
self.can_scan_nfc = True if self.ans('can_scan_nfc') != 'No' else False
self.actual_room = self.ans('actual_room')
self.staff_role = self.ans('staff_role')
self.telegram_username = self.ans('telegram_username').strip('@') if self.ans('telegram_username') else None
2022-12-18 16:40:39 +00:00
def __getitem__(self, var):
return self.data[var]
2024-01-19 00:15:20 +00:00
def set_room_errors (self, to_set):
for s in to_set: self.room_issues.append (s)
2022-12-18 16:40:39 +00:00
def ans(self, name):
for p in self.data['positions']:
for a in p['answers']:
if a.get('question_identifier', None) == name:
if a['answer'] in ['True', 'False']:
return bool(a['answer'] == 'True')
return a['answer']
2022-12-18 16:40:39 +00:00
return None
def isBadgeValid (self):
return self.ans('propic') and (not self.is_fursuiter or self.ans('propic_fursuiter'))
def isAdmin (self):
return self.code in ADMINS or self.staff_role in ADMINS_PRETIX_ROLE_NAMES
2022-12-18 16:40:39 +00:00
2023-12-30 10:27:42 +00:00
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)
self.loadAns()
2023-12-30 10:27:42 +00:00
2022-12-18 16:40:39 +00:00
async def edit_answer(self, name, new_answer):
found = False
2023-01-17 21:25:35 +00:00
self.pending_update = True
2022-12-18 16:40:39 +00:00
for key in range(len(self.answers)):
if self.answers[key].get('question_identifier', None) == name:
2022-12-18 16:40:39 +00:00
if new_answer != None:
print('EXISTING ANSWER UPDATE', name, '=>', new_answer)
self.answers[key]['answer'] = new_answer
found = True
else:
print('DEL ANSWER', name, '=>', new_answer)
del self.answers[key]
break
if (not found) and (new_answer is not None):
async with httpx.AsyncClient() as client:
2023-12-30 10:27:42 +00:00
res = await client.get(join(base_url_event, 'questions/'), headers=headers)
2022-12-18 16:40:39 +00:00
res = res.json()
for r in res['results']:
if r['identifier'] != name: continue
print('ANSWER UPDATE', name, '=>', new_answer)
self.answers.append({
'question': r['id'],
'answer': new_answer,
'options': r['options']
})
self.loadAns()
2022-12-18 16:40:39 +00:00
async def send_answers(self):
async with httpx.AsyncClient() as client:
2023-05-23 19:02:02 +00:00
print("POSITION ID IS", self.position_id)
for i, ans in enumerate(self.answers):
if TYPE_OF_QUESTIONS[ans['question']] == QUESTION_TYPES["multiple_choice_from_list"]: # if multiple choice
identifier = ans['question_identifier']
if self.ans(identifier) == "": #if empty answer
await self.edit_answer(identifier, None)
# Fix for karaoke fields
#if ans['question'] == 40:
# del self.answers[i]['options']
# del self.answers[i]['option_identifiers']
2023-12-30 10:27:42 +00:00
res = await client.patch(join(base_url_event, f'orderpositions/{self.position_id}/'), headers=headers, json={'answers': self.answers})
2023-05-23 19:02:02 +00:00
if res.status_code != 200:
for ans, err in zip(self.answers, res.json()['answers']):
if err:
print('ERROR ON', ans, err)
raise exceptions.ServerError('There has been an error while updating this answers.')
2023-12-30 10:27:42 +00:00
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"
2023-01-17 21:25:35 +00:00
self.pending_update = False
self.time = -1
self.loadAns()
2024-01-19 00:15:20 +00:00
2022-12-18 16:40:39 +00:00
@dataclass
class Quotas:
def __init__(self, data):
self.data = data
def get_left(self, capacity):
for quota in self.data['results']:
if quota['id'] == ROOM_QUOTA_ID[capacity]:
return quota['available_number']
return 0
2022-12-18 16:40:39 +00:00
async def get_quotas(request: Request=None):
async with httpx.AsyncClient() as client:
2023-12-30 10:27:42 +00:00
res = await client.get(join(base_url_event, 'quotas/?order=id&with_availability=true'), headers=headers)
2022-12-18 16:40:39 +00:00
res = res.json()
return Quotas(res)
2023-01-17 21:25:35 +00:00
async def get_order(request: Request=None):
await request.receive_body()
return await request.app.ctx.om.get_order(request=request)
2023-01-17 21:25:35 +00:00
class OrderManager:
def __init__(self):
2023-12-30 10:27:42 +00:00
self.lastCacheUpdate = 0
self.empty()
def empty(self):
2023-01-17 21:25:35 +00:00
self.cache = {}
self.order_list = []
2023-12-30 10:27:42 +00:00
async def updateCache(self):
t = time()
if(t - self.lastCacheUpdate > CACHE_EXPIRE_TIME):
2024-01-19 00:15:20 +00:00
print("[TIME] Re-filling cache!")
2023-12-30 10:27:42 +00:00
await self.fill_cache()
2024-01-19 00:15:20 +00:00
self.lastCacheUpdate = t
2023-12-30 10:27:42 +00:00
2023-01-17 21:25:35 +00:00
def add_cache(self, order):
self.cache[order.code] = order
if not order.code in self.order_list:
self.order_list.append(order.code)
def remove_cache(self, code):
if code in self.cache:
del self.cache[code]
self.order_list.remove(code)
async def fill_cache(self):
await load_items()
await load_questions()
2023-12-30 10:27:42 +00:00
self.empty()
p = 0
2022-12-18 16:40:39 +00:00
async with httpx.AsyncClient() as client:
while 1:
p += 1
2023-12-30 10:27:42 +00:00
res = await client.get(join(base_url_event, f"orders/?page={p}"), headers=headers)
2022-12-18 16:40:39 +00:00
if res.status_code == 404: break
data = res.json()
for o in data['results']:
o = Order(o)
if o.status in ['canceled', 'expired']:
self.remove_cache(o.code)
else:
self.add_cache(Order(o))
2024-01-19 00:15:20 +00:00
self.lastCacheUpdate = time()
for o in self.cache.values():
if o.code == o.room_id:
print(o.room_name)
await validate_room(None, o, self)
async def get_order(self, request=None, code=None, secret=None, nfc_id=None, cached=False):
2023-01-17 21:25:35 +00:00
# if it's a nfc id, just retorn it
if nfc_id:
for order in self.cache.values():
if order.nfc_id == nfc_id:
return order
2023-12-30 10:27:42 +00:00
await self.updateCache()
2023-01-17 21:25:35 +00:00
# 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:
2023-01-17 21:25:35 +00:00
return self.cache[code]
# If it's a request, ignore all the other parameters and just get the order of the requestor
if request:
code = request.cookies.get("foxo_code")
secret = request.cookies.get("foxo_secret")
2022-12-18 16:40:39 +00:00
2023-01-17 21:25:35 +00:00
if re.match('^[A-Z0-9]{5}$', code or '') and (secret is None or re.match('^[a-z0-9]{16,}$', secret)):
print('Fetching', code, 'with secret', secret)
async with httpx.AsyncClient() as client:
2023-12-30 10:27:42 +00:00
res = await client.get(join(base_url_event, f"orders/{code}/"), headers=headers)
2023-01-17 21:25:35 +00:00
if res.status_code != 200:
if request:
raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.")
else:
self.remove_cache(code)
return None
2023-01-17 21:25:35 +00:00
res = res.json()
order = Order(res)
if order.status in ['canceled', 'expired']:
2023-05-08 20:23:27 +00:00
self.remove_cache(order.code)
2023-01-17 21:25:35 +00:00
if request:
raise exceptions.Forbidden(f"Your order has been deleted. Contact support with your order identifier ({res['code']}) for further info.")
else:
self.add_cache(order)
2023-01-17 21:25:35 +00:00
if request and secret != res['secret']:
2022-12-18 16:40:39 +00:00
raise exceptions.Forbidden("Your session has expired due to a token change. Please check your E-Mail for an updated link!")
2023-01-17 21:25:35 +00:00
return order