Added order caching

This commit is contained in:
Ed 2023-01-17 22:25:35 +01:00
parent 435beecb7c
commit 70f5aaa362
2 changed files with 114 additions and 46 deletions

42
app.py
View File

@ -19,8 +19,9 @@ app.ext.add_dependency(Quotas, get_quotas)
from room import bp as room_bp from room import bp as room_bp
from propic import bp as propic_bp from propic import bp as propic_bp
from export import bp as export_bp
app.blueprint([room_bp,propic_bp]) app.blueprint([room_bp,propic_bp,export_bp])
@app.exception(exceptions.SanicException) @app.exception(exceptions.SanicException)
async def clear_session(request, exception): async def clear_session(request, exception):
@ -35,12 +36,12 @@ async def clear_session(request, exception):
@app.before_server_start @app.before_server_start
async def main_start(*_): async def main_start(*_):
print(">>>>>> main_start <<<<<<") print(">>>>>> main_start <<<<<<")
app.ctx.om = OrderManager()
app.ctx.tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=True) app.ctx.tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=True)
app.ctx.tpl.globals.update(time=time) app.ctx.tpl.globals.update(time=time)
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)
app.ctx.order_cache = {}
@app.route("/manage/barcode/<code>") @app.route("/manage/barcode/<code>")
async def gen_barcode(request, code): async def gen_barcode(request, code):
@ -49,23 +50,24 @@ async def gen_barcode(request, code):
aa.save(img, format='PNG') aa.save(img, format='PNG')
return raw(img.getvalue(), content_type="image/png") return raw(img.getvalue(), content_type="image/png")
@app.route("/manage/cache")
async def cache_status(request):
return
@app.route("/manage/stats")
async def stats(request, order: Order):
with open('res/stats.json') as f:
stats = json.load(f)
tpl = app.ctx.tpl.get_template('stats.html')
return html(tpl.render(order=order, stats=stats))
@app.route("/manage/nosecount") @app.route("/manage/nosecount")
async def nose_count(request, order: Order): async def nose_count(request, order: Order):
p = 0 orders = {key:value for key,value in sorted(app.ctx.om.cache.items(), key=lambda x: len(x[1].room_members), reverse=True) if value.status not in ['c', 'e']}
orders = {}
async with httpx.AsyncClient() as client:
while 1:
p += 1
res = await client.get(join(base_url, f"orders/?include_canceled_positions=false&page={p}"), headers=headers)
if res.status_code == 404: break
data = res.json()
for o in data['results']:
orders[o['code']] = Order(o)
orders = {key:value for key,value in sorted(orders.items(), key=lambda x: len(x[1].room_members), reverse=True)}
tpl = app.ctx.tpl.get_template('nosecount.html') tpl = app.ctx.tpl.get_template('nosecount.html')
return html(tpl.render(orders=orders, order=order)) return html(tpl.render(orders=orders, order=order))
@ -99,12 +101,12 @@ async def welcome(request, order: Order, quota: Quotas):
if order.pending_roommates: if order.pending_roommates:
for pr in order.pending_roommates: for pr in order.pending_roommates:
if not pr: continue if not pr: continue
pending_roommates.append(await get_order(code=pr, insecure=True)) pending_roommates.append(await app.ctx.om.get_order(code=pr, cached=True))
room_members = [] room_members = []
if order.room_id: if order.room_id:
if order.room_id != order.code: if order.room_id != order.code:
room_owner = await get_order(code=order.room_id, insecure=True) room_owner = await app.ctx.om.get_order(code=order.room_id, cached=True)
else: else:
room_owner = order room_owner = order
@ -115,7 +117,7 @@ async def welcome(request, order: Order, quota: Quotas):
if member_id == order.code: if member_id == order.code:
room_members.append(order) room_members.append(order)
else: else:
room_members.append(await get_order(code=member_id, insecure=True)) room_members.append(await app.ctx.om.get_order(code=member_id, cached=True))
tpl = app.ctx.tpl.get_template('welcome.html') tpl = app.ctx.tpl.get_template('welcome.html')
return html(tpl.render(order=order, quota=quota, room_members=room_members, pending_roommates=pending_roommates)) return html(tpl.render(order=order, quota=quota, room_members=room_members, pending_roommates=pending_roommates))

118
ext.py
View File

@ -5,15 +5,22 @@ import re
from config import * from config import *
from os.path import join from os.path import join
import json import json
from time import time
@dataclass @dataclass
class Order: class Order:
def __init__(self, data): def __init__(self, data):
self.time = time
self.data = data self.data = data
self.status = {'n': 'pending', 'p': 'paid', 'e': 'expired', 'c': 'canceled'}[self.data['status']] self.status = {'n': 'pending', 'p': 'paid', 'e': 'expired', 'c': 'canceled'}[self.data['status']]
self.code = data['code'] self.code = data['code']
self.pending_update = False
self.has_card = False self.has_card = False
self.sponsorship = None self.sponsorship = None
self.has_early = False
self.has_late = False
for p in self.data['positions']: for p in self.data['positions']:
if p['item'] in [16, 38]: if p['item'] in [16, 38]:
@ -30,9 +37,26 @@ class Order:
if p['country']: if p['country']:
self.country = p['country'] self.country = p['country']
if p['attendee_name']:
self.first_name = p['attendee_name_parts']['given_name']
self.last_name = p['attendee_name_parts']['family_name']
if p['item'] == 20:
self.has_early = True
if p['item'] == 21:
self.has_late = True
self.total = float(data['total'])
self.fees = 0
for fee in data['fees']:
self.fees += float(fee['value'])
answers = ['payment_provider', 'shirt_size', 'birth_date', 'fursona_name', 'room_confirmed', 'room_id']
self.payment_provider = data['payment_provider']
self.shirt_size = self.ans('shirt_size') self.shirt_size = self.ans('shirt_size')
self.birth_date = self.ans('birth_date')
self.is_artist = True if self.ans('is_artist') != 'No' else False 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_fursuiter = True if self.ans('is_fursuiter') != 'No' else False
self.is_allergic = True if self.ans('is_allergic') != 'No' else False self.is_allergic = True if self.ans('is_allergic') != 'No' else False
@ -47,6 +71,7 @@ class Order:
self.room_owner = (self.code == self.room_id) self.room_owner = (self.code == self.room_id)
self.room_secret = self.ans('room_secret') self.room_secret = self.ans('room_secret')
def __getitem__(self, var): def __getitem__(self, var):
return self.data[var] return self.data[var]
@ -61,6 +86,7 @@ class Order:
async def edit_answer(self, name, new_answer): async def edit_answer(self, name, new_answer):
found = False found = False
self.pending_update = True
for key in range(len(self.answers)): for key in range(len(self.answers)):
if self.answers[key]['question_identifier'] == name: if self.answers[key]['question_identifier'] == name:
if new_answer != None: if new_answer != None:
@ -93,6 +119,7 @@ class Order:
async def send_answers(self): async def send_answers(self):
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
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, f'orderpositions/{self.position_id}/'), headers=headers, json={'answers': self.answers})
self.pending_update = False
@dataclass @dataclass
class Quotas: class Quotas:
@ -118,33 +145,72 @@ async def get_quotas(request: Request=None):
return Quotas(res) return Quotas(res)
async def get_order(request: Request=None, code=None, secret=None, insecure=False): async def get_order(request: Request=None):
if request: await request.receive_body()
await request.receive_body() return await request.app.ctx.om.get_order(request=request)
code = request.cookies.get("foxo_code")
secret = request.cookies.get("foxo_secret")
if re.match('^[A-Z0-9]{5}$', code or '') and (secret is None or re.match('^[a-z0-9]{16,}$', secret)):
print('Fetching', code, 'with secret', secret)
async with httpx.AsyncClient() as client: class OrderManager:
res = await client.get(join(base_url, f"orders/{code}/"), headers=headers) def __init__(self):
if res.status_code != 200: self.cache = {}
if request: self.order_list = []
raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.")
def add_cache(self, order):
self.cache[order.code] = order
if not order.code in self.order_list:
self.order_list.append(order.code)
def remove_cache(self, code):
if code in self.cache:
del self.cache[code]
self.order_list.remove(code)
async def get_order(self, request=None, code=None, secret=None, cached=False):
# Fill the cache on first load
if not self.cache:
p = 0
res = res.json() async with httpx.AsyncClient() as client:
while 1:
p += 1
res = await client.get(join(base_url, f"orders/?page={p}"), headers=headers)
if request and res: if res.status_code == 404: break
request.app.ctx.order_cache = {}
if res['status'] in ['c', 'e']: data = res.json()
if request: for o in data['results']:
raise exceptions.Forbidden(f"Your order has been deleted. Contact support with your order identifier ({res['code']}) for further info.") self.add_cache(Order(o))
# If a cached order is needed, just get it if available
if code and cached and code in self.cache and time()-self.cache[code].time < 1800:
return self.cache[code]
# If it's a request, ignore all the other parameters and just get the order of the requestor
if request:
code = request.cookies.get("foxo_code")
secret = request.cookies.get("foxo_secret")
if secret == res['secret'] or insecure: if re.match('^[A-Z0-9]{5}$', code or '') and (secret is None or re.match('^[a-z0-9]{16,}$', secret)):
return Order(res) print('Fetching', code, 'with secret', secret)
else:
if request: async with httpx.AsyncClient() as client:
res = await client.get(join(base_url, f"orders/{code}/"), headers=headers)
if res.status_code != 200:
if request:
raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.")
else:
self.remove_cache(code)
return None
res = res.json()
if res['status'] in ['c', 'e']:
if request:
raise exceptions.Forbidden(f"Your order has been deleted. Contact support with your order identifier ({res['code']}) for further info.")
order = Order(res)
self.add_cache(order)
if request and secret != res['secret']:
raise exceptions.Forbidden("Your session has expired due to a token change. Please check your E-Mail for an updated link!") raise exceptions.Forbidden("Your session has expired due to a token change. Please check your E-Mail for an updated link!")
return None return order