From 70f5aaa3628ec7473e3c030902bb1cec483e7ffc Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 17 Jan 2023 22:25:35 +0100 Subject: [PATCH] Added order caching --- app.py | 42 ++++++++++---------- ext.py | 118 ++++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 114 insertions(+), 46 deletions(-) diff --git a/app.py b/app.py index fa66c00..3c6679b 100644 --- a/app.py +++ b/app.py @@ -19,8 +19,9 @@ app.ext.add_dependency(Quotas, get_quotas) from room import bp as room_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) async def clear_session(request, exception): @@ -35,12 +36,12 @@ async def clear_session(request, exception): @app.before_server_start async def main_start(*_): print(">>>>>> main_start <<<<<<") + + app.ctx.om = OrderManager() app.ctx.tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=True) app.ctx.tpl.globals.update(time=time) app.ctx.tpl.globals.update(int=int) app.ctx.tpl.globals.update(len=len) - - app.ctx.order_cache = {} @app.route("/manage/barcode/") async def gen_barcode(request, code): @@ -49,23 +50,24 @@ async def gen_barcode(request, code): aa.save(img, format='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") async def nose_count(request, order: Order): - p = 0 - 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)} + 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']} tpl = app.ctx.tpl.get_template('nosecount.html') return html(tpl.render(orders=orders, order=order)) @@ -99,12 +101,12 @@ async def welcome(request, order: Order, quota: Quotas): if order.pending_roommates: for pr in order.pending_roommates: 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 = [] if order.room_id: 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: room_owner = order @@ -115,7 +117,7 @@ async def welcome(request, order: Order, quota: Quotas): if member_id == order.code: room_members.append(order) 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') return html(tpl.render(order=order, quota=quota, room_members=room_members, pending_roommates=pending_roommates)) diff --git a/ext.py b/ext.py index 5b4a5df..0439311 100644 --- a/ext.py +++ b/ext.py @@ -5,15 +5,22 @@ import re from config import * from os.path import join import json +from time import time @dataclass class Order: def __init__(self, data): + + self.time = time self.data = data self.status = {'n': 'pending', 'p': 'paid', 'e': 'expired', 'c': 'canceled'}[self.data['status']] self.code = data['code'] + self.pending_update = False + self.has_card = False self.sponsorship = None + self.has_early = False + self.has_late = False for p in self.data['positions']: if p['item'] in [16, 38]: @@ -30,9 +37,26 @@ class Order: if 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.birth_date = self.ans('birth_date') 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 @@ -47,6 +71,7 @@ class Order: self.room_owner = (self.code == self.room_id) self.room_secret = self.ans('room_secret') + def __getitem__(self, var): return self.data[var] @@ -61,6 +86,7 @@ class Order: async def edit_answer(self, name, new_answer): found = False + self.pending_update = True for key in range(len(self.answers)): if self.answers[key]['question_identifier'] == name: if new_answer != None: @@ -93,6 +119,7 @@ class Order: async def send_answers(self): async with httpx.AsyncClient() as client: res = await client.patch(join(base_url, f'orderpositions/{self.position_id}/'), headers=headers, json={'answers': self.answers}) + self.pending_update = False @dataclass class Quotas: @@ -118,33 +145,72 @@ async def get_quotas(request: Request=None): return Quotas(res) -async def get_order(request: Request=None, code=None, secret=None, insecure=False): - if request: - await request.receive_body() - 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 def get_order(request: Request=None): + await request.receive_body() + return await request.app.ctx.om.get_order(request=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.") +class OrderManager: + def __init__(self): + self.cache = {} + self.order_list = [] + + 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: - request.app.ctx.order_cache = {} + if res.status_code == 404: break - 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.") + data = res.json() + for o in data['results']: + 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: - return Order(res) - else: - if request: + 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: + 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!") - return None + return order