[wip4] Room preview generation improvements + Room checking with user email
This commit is contained in:
parent
6fe87b6ea1
commit
aa54032492
44
admin.py
44
admin.py
|
@ -1,5 +1,5 @@
|
||||||
from email.mime.text import MIMEText
|
|
||||||
from sanic import response, redirect, Blueprint, exceptions
|
from sanic import response, redirect, Blueprint, exceptions
|
||||||
|
from room import unconfirm_room_by_order
|
||||||
from config import *
|
from config import *
|
||||||
from utils import *
|
from utils import *
|
||||||
from ext import *
|
from ext import *
|
||||||
|
@ -12,7 +12,7 @@ import json
|
||||||
|
|
||||||
bp = Blueprint("admin", url_prefix="/manage/admin")
|
bp = Blueprint("admin", url_prefix="/manage/admin")
|
||||||
|
|
||||||
def credentialsCheck(request, order:Order):
|
def credentials_check(request, order:Order):
|
||||||
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 EXTRA_PRINTS:
|
if EXTRA_PRINTS:
|
||||||
|
@ -22,15 +22,15 @@ def credentialsCheck(request, order:Order):
|
||||||
|
|
||||||
|
|
||||||
@bp.get('/cache/clear')
|
@bp.get('/cache/clear')
|
||||||
async def clearCache(request, order:Order):
|
async def clear_cache(request, order:Order):
|
||||||
credentialsCheck(request, order)
|
credentials_check(request, order)
|
||||||
await request.app.ctx.om.fill_cache()
|
await request.app.ctx.om.fill_cache()
|
||||||
return redirect(f'/manage/admin')
|
return redirect(f'/manage/admin')
|
||||||
|
|
||||||
@bp.get('/loginas/<code>')
|
@bp.get('/loginas/<code>')
|
||||||
async def loginAs(request, code, order:Order):
|
async def login_as(request, code, order:Order):
|
||||||
credentialsCheck(request, order)
|
credentials_check(request, order)
|
||||||
dOrder = await getOrderByCode(request, code, throwException=True)
|
dOrder = await get_order_by_code(request, code, throwException=True)
|
||||||
if(dOrder.isAdmin()):
|
if(dOrder.isAdmin()):
|
||||||
raise exceptions.Forbidden("You can't login as another admin!")
|
raise exceptions.Forbidden("You can't login as another admin!")
|
||||||
|
|
||||||
|
@ -44,26 +44,18 @@ async def loginAs(request, code, order:Order):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@bp.get('/room/unconfirm/<code>')
|
@bp.get('/room/unconfirm/<code>')
|
||||||
async def unconfirmRoom(request, code, order:Order):
|
async def unconfirm_room(request, code, order:Order):
|
||||||
credentialsCheck(request, order)
|
credentials_check(request, order)
|
||||||
dOrder = await getOrderByCode(request, code, throwException=True)
|
dOrder = await get_order_by_code(request, code, throwException=True)
|
||||||
|
unconfirm_room_by_order(dOrder, True, request)
|
||||||
if(not dOrder.room_confirmed):
|
|
||||||
raise exceptions.BadRequest("Room is not confirmed!")
|
|
||||||
|
|
||||||
ppl = getPeopleInRoomByRoomId(request, code)
|
|
||||||
for p in ppl:
|
|
||||||
await p.edit_answer('room_confirmed', "False")
|
|
||||||
await p.send_answers()
|
|
||||||
|
|
||||||
return redirect(f'/manage/nosecount')
|
return redirect(f'/manage/nosecount')
|
||||||
|
|
||||||
@bp.get('/room/delete/<code>')
|
@bp.get('/room/delete/<code>')
|
||||||
async def deleteRoom(request, code, order:Order):
|
async def delete_room(request, code, order:Order):
|
||||||
credentialsCheck(request, order)
|
credentials_check(request, order)
|
||||||
dOrder = await getOrderByCode(request, code, throwException=True)
|
dOrder = await get_order_by_code(request, code, throwException=True)
|
||||||
|
|
||||||
ppl = getPeopleInRoomByRoomId(request, code)
|
ppl = get_people_in_room_by_code(request, code)
|
||||||
for p in ppl:
|
for p in ppl:
|
||||||
await p.edit_answer('room_id', None)
|
await p.edit_answer('room_id', None)
|
||||||
await p.edit_answer('room_confirmed', "False")
|
await p.edit_answer('room_confirmed', "False")
|
||||||
|
@ -79,9 +71,9 @@ async def deleteRoom(request, code, order:Order):
|
||||||
return redirect(f'/manage/nosecount')
|
return redirect(f'/manage/nosecount')
|
||||||
|
|
||||||
@bp.post('/room/rename/<code>')
|
@bp.post('/room/rename/<code>')
|
||||||
async def renameRoom(request, code, order:Order):
|
async def rename_room(request, code, order:Order):
|
||||||
credentialsCheck(request, order)
|
credentials_check(request, order)
|
||||||
dOrder = await getOrderByCode(request, code, throwException=True)
|
dOrder = await get_order_by_code(request, code, throwException=True)
|
||||||
|
|
||||||
name = request.form.get('name')
|
name = request.form.get('name')
|
||||||
if len(name) > 64 or len(name) < 4:
|
if len(name) > 64 or len(name) < 4:
|
||||||
|
|
8
ext.py
8
ext.py
|
@ -65,7 +65,7 @@ class Order:
|
||||||
self.has_card = True
|
self.has_card = True
|
||||||
|
|
||||||
if p['item'] == ITEMS_ID_MAP['sponsorship_item']:
|
if p['item'] == ITEMS_ID_MAP['sponsorship_item']:
|
||||||
sponsorshipType = keyFromValue(ITEM_VARIATIONS_MAP['sponsorship_item'], p['variation'])
|
sponsorshipType = key_from_value(ITEM_VARIATIONS_MAP['sponsorship_item'], p['variation'])
|
||||||
self.sponsorship = sponsorshipType[0].replace ('sponsorship_item_', '') if len(sponsorshipType) > 0 else None
|
self.sponsorship = sponsorshipType[0].replace ('sponsorship_item_', '') if len(sponsorshipType) > 0 else None
|
||||||
|
|
||||||
if p['attendee_name']:
|
if p['attendee_name']:
|
||||||
|
@ -79,7 +79,7 @@ class Order:
|
||||||
self.has_late = True
|
self.has_late = True
|
||||||
|
|
||||||
if p['item'] == ITEMS_ID_MAP['bed_in_room']:
|
if p['item'] == ITEMS_ID_MAP['bed_in_room']:
|
||||||
roomTypeLst = keyFromValue(ITEM_VARIATIONS_MAP['bed_in_room'], p['variation'])
|
roomTypeLst = key_from_value(ITEM_VARIATIONS_MAP['bed_in_room'], p['variation'])
|
||||||
roomTypeId = roomTypeLst[0] if len(roomTypeLst) > 0 else None
|
roomTypeId = roomTypeLst[0] if len(roomTypeLst) > 0 else None
|
||||||
self.bed_in_room = p['variation']
|
self.bed_in_room = p['variation']
|
||||||
self.room_person_no = ROOM_CAPACITY_MAP[roomTypeId] if roomTypeId in ROOM_CAPACITY_MAP else None
|
self.room_person_no = ROOM_CAPACITY_MAP[roomTypeId] if roomTypeId in ROOM_CAPACITY_MAP else None
|
||||||
|
@ -270,8 +270,8 @@ class OrderManager:
|
||||||
self.order_list.remove(code)
|
self.order_list.remove(code)
|
||||||
|
|
||||||
async def fill_cache(self):
|
async def fill_cache(self):
|
||||||
await loadItems()
|
await load_items()
|
||||||
await loadQuestions()
|
await load_questions()
|
||||||
self.empty()
|
self.empty()
|
||||||
p = 0
|
p = 0
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
ROOM_ERROR_MESSAGES = {
|
||||||
|
'room_id_mismatch': "There's a member in your room that is actually in another room, too. Please contact us as soon as possible in order to fix this issue.",
|
||||||
|
'unpaid': "Somebody in your room has not paid for their reservation, yet.",
|
||||||
|
'type_mismatch': "A member in your room has a ticket for a different type of room capacity. This happens when users swap their room types with others, without abandoning the room.",
|
||||||
|
'daily': "Some member in your room has a Daily ticket. These tickets do not include a hotel reservation"
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19A2.92,2.92 0 0,0 18,16.08Z" /></svg>
|
After Width: | Height: | Size: 521 B |
152
room.py
152
room.py
|
@ -1,3 +1,6 @@
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from messages import ROOM_ERROR_MESSAGES
|
||||||
|
import smtplib
|
||||||
from sanic.response import html, redirect, text
|
from sanic.response import html, redirect, text
|
||||||
from sanic import Blueprint, exceptions
|
from sanic import Blueprint, exceptions
|
||||||
from random import choice
|
from random import choice
|
||||||
|
@ -207,8 +210,8 @@ async def approve_roomreq(request, code, order: Order):
|
||||||
await order.edit_answer('pending_roommates', (','.join([x for x in order.pending_roommates if x != pending_member.code]) or None))
|
await order.edit_answer('pending_roommates', (','.join([x for x in order.pending_roommates if x != pending_member.code]) or None))
|
||||||
|
|
||||||
await pending_member.send_answers()
|
await pending_member.send_answers()
|
||||||
await order.send_answers()
|
await order.send_answers(order.code)
|
||||||
|
remove_room_preview()
|
||||||
return redirect('/manage/welcome')
|
return redirect('/manage/welcome')
|
||||||
|
|
||||||
@bp.route("/leave")
|
@bp.route("/leave")
|
||||||
|
@ -232,7 +235,7 @@ async def leave_room(request, order: Order):
|
||||||
|
|
||||||
await room_owner.send_answers()
|
await room_owner.send_answers()
|
||||||
await order.send_answers()
|
await order.send_answers()
|
||||||
|
remove_room_preview (order.room_id)
|
||||||
return redirect('/manage/welcome')
|
return redirect('/manage/welcome')
|
||||||
|
|
||||||
@bp.route("/reject/<code>")
|
@bp.route("/reject/<code>")
|
||||||
|
@ -288,7 +291,7 @@ async def rename_room(request, order: Order):
|
||||||
|
|
||||||
await order.edit_answer("room_name", name)
|
await order.edit_answer("room_name", name)
|
||||||
await order.send_answers()
|
await order.send_answers()
|
||||||
remove_room_preview (order.code)
|
remove_room_preview(order.code)
|
||||||
return redirect('/manage/welcome')
|
return redirect('/manage/welcome')
|
||||||
|
|
||||||
@bp.route("/confirm")
|
@bp.route("/confirm")
|
||||||
|
@ -357,6 +360,78 @@ async def confirm_room(request, order: Order, quotas: Quotas):
|
||||||
|
|
||||||
return redirect('/manage/welcome')
|
return redirect('/manage/welcome')
|
||||||
|
|
||||||
|
async def unconfirm_room_by_order(order, throw=True, request=None):
|
||||||
|
if not order.room_confirmed and throw:
|
||||||
|
raise exceptions.BadRequest("Room is not confirmed!")
|
||||||
|
|
||||||
|
ppl = get_people_in_room_by_code(request, order.code)
|
||||||
|
for p in ppl:
|
||||||
|
await p.edit_answer('room_confirmed', "False")
|
||||||
|
await p.send_answers()
|
||||||
|
|
||||||
|
async def validate_room(request, order):
|
||||||
|
check, room_errors, member_orders = await check_room(request, order)
|
||||||
|
if check == True: return
|
||||||
|
try:
|
||||||
|
# Build message
|
||||||
|
issues_str = ""
|
||||||
|
for err in room_errors:
|
||||||
|
if err in ROOM_ERROR_MESSAGES:
|
||||||
|
issues_str += f" - {ROOM_ERROR_MESSAGES['err']}"
|
||||||
|
memberMessages = []
|
||||||
|
for member in member_orders:
|
||||||
|
msg = MIMEText(f"Hello {member.name}!\n\nWe had to unconfirm your room {order.room_name} due to th{'ese issues' if len(room_errors) > 1 else 'is issue'}:\n{issues_str}\n\nPlease contact your room's owner or contact our support for further informations at https://furizon.net/contact/.\nThank you")
|
||||||
|
msg['Subject'] = '[Furizon] Your room cannot be confirmed'
|
||||||
|
msg['From'] = 'Furizon <no-reply@furizon.net>'
|
||||||
|
msg['To'] = f"{member.name} <{member.email}>"
|
||||||
|
memberMessages.append(msg)
|
||||||
|
|
||||||
|
if (len(memberMessages) == 0): return
|
||||||
|
|
||||||
|
s = smtplib.SMTP_SSL(SMTP_HOST)
|
||||||
|
s.login(SMTP_USER, SMTP_PASSWORD)
|
||||||
|
for message in memberMessages:
|
||||||
|
s.sendmail(message['From'], message['to'], message.as_string())
|
||||||
|
s.quit()
|
||||||
|
except Exception as ex:
|
||||||
|
if EXTRA_PRINTS: print(ex)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_room(request, order):
|
||||||
|
room_errors = []
|
||||||
|
room_members = []
|
||||||
|
if not order or not order.room_id or order.room_id != order.code: return False, room_errors, room_members
|
||||||
|
|
||||||
|
# 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
|
||||||
|
for m in order.room_members:
|
||||||
|
if m == order.code:
|
||||||
|
res = order
|
||||||
|
else:
|
||||||
|
res = await request.app.ctx.om.get_order(code=m)
|
||||||
|
|
||||||
|
# Room user in another room
|
||||||
|
if res.room_id != order.code:
|
||||||
|
room_errors.append('room_id_mismatch')
|
||||||
|
|
||||||
|
if res.status != 'paid':
|
||||||
|
room_errors.append('unpaid')
|
||||||
|
|
||||||
|
if res.bed_in_room != bed_in_room:
|
||||||
|
room_errors.append('type_mismatch')
|
||||||
|
|
||||||
|
if res.daily:
|
||||||
|
room_errors.append('daily')
|
||||||
|
|
||||||
|
room_members.append(res)
|
||||||
|
|
||||||
|
if len(room_members) != order.room_person_no and order.room_person_no != None:
|
||||||
|
room_errors.append('capacity_mismatch')
|
||||||
|
return len(room_errors) == 0, room_errors, room_members
|
||||||
|
|
||||||
async def get_room (request, code):
|
async def get_room (request, code):
|
||||||
order_data = await request.app.ctx.om.get_order(code=code)
|
order_data = await request.app.ctx.om.get_order(code=code)
|
||||||
if not order_data or not order_data.room_owner: return None
|
if not order_data or not order_data.room_owner: return None
|
||||||
|
@ -369,6 +444,7 @@ async def get_room (request, code):
|
||||||
return {'name': order_data.room_name,
|
return {'name': order_data.room_name,
|
||||||
'confirmed': order_data.room_confirmed,
|
'confirmed': order_data.room_confirmed,
|
||||||
'capacity': order_data.room_person_no,
|
'capacity': order_data.room_person_no,
|
||||||
|
'free_spots': order_data.room_person_no - len(members_map),
|
||||||
'members': members_map}
|
'members': members_map}
|
||||||
|
|
||||||
async def get_room_with_order (request, code):
|
async def get_room_with_order (request, code):
|
||||||
|
@ -382,35 +458,67 @@ def remove_room_preview(code):
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if (EXTRA_PRINTS): print(ex)
|
if (EXTRA_PRINTS): print(ex)
|
||||||
|
|
||||||
|
def draw_profile (source, member, position, font, size=(170, 170), border_width=5):
|
||||||
|
idraw = ImageDraw.Draw(source)
|
||||||
|
source_size = source.size
|
||||||
|
main_fill = (187, 198, 206)
|
||||||
|
propic_x = position[0]
|
||||||
|
propic_y = (source_size[1] // 2) - (size[1] // 2)
|
||||||
|
border_loc = (propic_x, propic_y, propic_x + size[0] + border_width * 2, propic_y + size[1] + border_width *2)
|
||||||
|
profile_location = (propic_x + border_width, propic_y + border_width)
|
||||||
|
propic_name_y = propic_y + size[1] + border_width + 20
|
||||||
|
border_color = SPONSORSHIP_COLOR_MAP[member['sponsorship']] if member['sponsorship'] in SPONSORSHIP_COLOR_MAP.keys() else (84, 110, 122)
|
||||||
|
# Draw border
|
||||||
|
idraw.rounded_rectangle(border_loc, border_width, border_color)
|
||||||
|
# Draw profile picture
|
||||||
|
with Image.open(f'res/propic/{member['propic'] or 'default.png'}') as to_add:
|
||||||
|
source.paste(to_add.resize (size), profile_location)
|
||||||
|
name_len = idraw.textlength(str(member['name']), font)
|
||||||
|
calc_size = 0
|
||||||
|
if name_len > size[0]:
|
||||||
|
calc_size = size[0] * 20 / name_len if name_len > size[0] else 20
|
||||||
|
font = ImageFont.truetype(font.path, calc_size)
|
||||||
|
name_len = idraw.textlength(str(member['name']), font)
|
||||||
|
name_loc = (position[0] + ((size[0] / 2) - name_len / 2), propic_name_y + (calc_size/2))
|
||||||
|
name_color = SPONSORSHIP_COLOR_MAP[member['sponsorship']] if member['sponsorship'] in SPONSORSHIP_COLOR_MAP.keys() else main_fill
|
||||||
|
idraw.text(name_loc, str(member['name']), font=font, fill=name_color)
|
||||||
|
|
||||||
async def generate_room_preview(request, code, room_data):
|
async def generate_room_preview(request, code, room_data):
|
||||||
font_path = f'res/font/pt-serif-caption-latin-400-normal.ttf'
|
font_path = f'res/font/NotoSans-Bold.ttf'
|
||||||
main_fill = (16, 149, 193)
|
main_fill = (187, 198, 206)
|
||||||
|
propic_size = (170, 170)
|
||||||
|
logo_size = (200, 43)
|
||||||
|
border_width = 5
|
||||||
|
propic_gap = 50
|
||||||
|
propic_width = propic_size[0] + (border_width * 2)
|
||||||
|
propic_total_width = propic_width + propic_gap
|
||||||
jobs.append(code)
|
jobs.append(code)
|
||||||
try:
|
try:
|
||||||
room_data = await get_room(request, code) if not room_data else room_data
|
room_data = await get_room(request, code) if not room_data else room_data
|
||||||
width = 230 * int(room_data['capacity']) + 130
|
if not room_data: return
|
||||||
|
width = max([(propic_width + propic_gap) * int(room_data['capacity']) + propic_gap, 670])
|
||||||
|
height = int(width * 0.525)
|
||||||
font = ImageFont.truetype(font_path, 20)
|
font = ImageFont.truetype(font_path, 20)
|
||||||
with Image.new('RGB', (width, 270), (17, 25, 31)) as to_save:
|
|
||||||
i_draw = ImageDraw.Draw(to_save)
|
# Recalculate gap
|
||||||
|
propic_gap = (width - (propic_width * int(room_data['capacity']))) // (int(room_data['capacity']) + 1)
|
||||||
|
propic_total_width = propic_width + propic_gap
|
||||||
|
|
||||||
|
# Define output image
|
||||||
|
with Image.new('RGB', (width, height), (17, 25, 31)) as source:
|
||||||
|
# Draw logo
|
||||||
|
with (Image.open('res/furizon.png') as logo, logo.resize(logo_size).convert('RGBA') as resized_logo):
|
||||||
|
source.paste(resized_logo, ((source.size[0] // 2) - (logo_size[0] // 2), 10), resized_logo)
|
||||||
|
i_draw = ImageDraw.Draw(source)
|
||||||
# Draw room's name
|
# Draw room's name
|
||||||
room_name_len = i_draw.textlength(room_data['name'], font)
|
room_name_len = i_draw.textlength(room_data['name'], font)
|
||||||
i_draw.text((((width / 2) - room_name_len / 2), 10), room_data['name'], font=font, fill=main_fill)
|
i_draw.text((((width / 2) - room_name_len / 2), 55), room_data['name'], font=font, fill=main_fill)
|
||||||
# Draw members
|
# Draw members
|
||||||
for m in range (room_data['capacity']):
|
for m in range (room_data['capacity']):
|
||||||
member = room_data['members'][m] if m < len(room_data['members']) else { 'name': 'Empty', 'propic': '../new.png', 'sponsorship': None }
|
member = room_data['members'][m] if m < len(room_data['members']) else { 'name': 'Empty', 'propic': '../new.png', 'sponsorship': None }
|
||||||
font = ImageFont.truetype(font_path, 20)
|
font = ImageFont.truetype(font_path, 20)
|
||||||
with Image.open(f'res/propic/{member['propic'] or 'default.png'}') as to_add:
|
draw_profile(source, member, (propic_gap + (propic_total_width * m), 63), font, propic_size, border_width)
|
||||||
to_save.paste(to_add.resize ((180, 180)), (90 + (230 * m), 45))
|
source.save(f'res/rooms/{code}.jpg', 'JPEG', quality=60)
|
||||||
name_len = i_draw.textlength(str(member['name']), font)
|
|
||||||
calc_size = 0
|
|
||||||
if name_len > 180:
|
|
||||||
calc_size = 180 * 20 / name_len if name_len > 180 else 20
|
|
||||||
font = ImageFont.truetype(font_path, calc_size)
|
|
||||||
name_len = i_draw.textlength(str(member['name']), font)
|
|
||||||
name_loc = ((90 + (230 * m)) + (90 - name_len / 2), 235 + (calc_size/2))
|
|
||||||
name_color = SPONSORSHIP_COLOR_MAP[member['sponsorship']] if member['sponsorship'] in SPONSORSHIP_COLOR_MAP.keys() else main_fill
|
|
||||||
i_draw.text(name_loc, str(member['name']), font=font, fill=name_color)
|
|
||||||
to_save.save(f'res/rooms/{code}.jpg', 'JPEG')
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if EXTRA_PRINTS: print(err)
|
if EXTRA_PRINTS: print(err)
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Furizon 2024 Nosecount{% endblock %}
|
{% block title %}Furizon 2024 Nosecount{% endblock %}
|
||||||
|
{% block head %}
|
||||||
|
<meta property="og:title" content="Nose count - Furizon" />
|
||||||
|
<meta property="og:image:type" content="image/jpeg" />
|
||||||
|
<meta property="og:image:alt" content="Furizon logo" />
|
||||||
|
<meta property="og:image" content="https://reg.furizon.net/res/furizon.png" />
|
||||||
|
<meta property="og:image:secure_url" content="https://reg.furizon.net/res/furizon.png" />
|
||||||
|
<meta property="og:description" content="Explore this year's rooms, find your friends and plan your meet-ups."/>
|
||||||
|
{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<main class="container">
|
<main class="container">
|
||||||
{% if order and order.isAdmin() %}
|
{% if order and order.isAdmin() %}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Furizon 2024 Sponsorcount{% endblock %}
|
{% block title %}Furizon 2024 Sponsorcount{% endblock %}
|
||||||
|
{% block head %}
|
||||||
|
<meta property="og:title" content="Sponsor count - Furizon" />
|
||||||
|
<meta property="og:image:type" content="image/jpeg" />
|
||||||
|
<meta property="og:image:alt" content="Furizon logo" />
|
||||||
|
<meta property="og:image" content="https://reg.furizon.net/res/furizon.png" />
|
||||||
|
<meta property="og:image:secure_url" content="https://reg.furizon.net/res/furizon.png" />
|
||||||
|
<meta property="og:description" content="Thanks to all the amazing furs who decided to support us this year ❤️"/>
|
||||||
|
{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<header>
|
<header>
|
||||||
|
|
|
@ -2,9 +2,14 @@
|
||||||
{% block title %}{{room_data['name']}}{% endblock %}
|
{% block title %}{{room_data['name']}}{% endblock %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<!--Open Graph tags here-->
|
<!--Open Graph tags here-->
|
||||||
<meta property="og:title" content="{{room_data['name']}}" />
|
<meta property="og:title" content="View room - Furizon" />
|
||||||
<meta property="og:image:type" content="image/jpeg" />
|
<meta property="og:image:type" content="image/jpeg" />
|
||||||
<meta property="og:image:alt" content="View of a room" />
|
<meta property="og:image:alt" content="View of a room" />
|
||||||
|
<meta property="og:image" content="http://localhost:8188/{{preview}}" />
|
||||||
|
<meta property="og:image:secure_url" content="http://localhost:8188/{{preview}}" />
|
||||||
|
<meta property="og:image:width" content="{{230 * room_data['capacity'] + 130}}"/>
|
||||||
|
<meta property="og:image:height" content="270"/>
|
||||||
|
<meta property="og:description" content="Room {{room_data['name']}} has {{'been confirmed.' if room_data['confirmed'] else ('been filled.' if room_data['free_spots'] == 0 else (room_data['free_spots'] | string) + ' free spots out of ' + (room_data['capacity'] | string )) }}"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<main class="container">
|
<main class="container">
|
||||||
|
@ -12,7 +17,7 @@
|
||||||
<picture>
|
<picture>
|
||||||
<source srcset="/res/furizon.png" media="(prefers-color-scheme:dark)">
|
<source srcset="/res/furizon.png" media="(prefers-color-scheme:dark)">
|
||||||
<img src="/res/furizon-light.png" style="height:4rem;text-align:center;"
|
<img src="/res/furizon-light.png" style="height:4rem;text-align:center;"
|
||||||
onload="window.location.href='/manage/nosecount';">
|
>
|
||||||
</picture>
|
</picture>
|
||||||
</header>
|
</header>
|
||||||
</main>
|
</main>
|
||||||
|
|
38
utils.py
38
utils.py
|
@ -23,7 +23,7 @@ QUESTION_TYPES = { #https://docs.pretix.eu/en/latest/api/resources/questions.htm
|
||||||
TYPE_OF_QUESTIONS = {} # maps questionId -> type
|
TYPE_OF_QUESTIONS = {} # maps questionId -> type
|
||||||
|
|
||||||
|
|
||||||
async def loadQuestions():
|
async def load_questions():
|
||||||
global TYPE_OF_QUESTIONS
|
global TYPE_OF_QUESTIONS
|
||||||
TYPE_OF_QUESTIONS.clear()
|
TYPE_OF_QUESTIONS.clear()
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
|
@ -38,7 +38,7 @@ async def loadQuestions():
|
||||||
for q in data['results']:
|
for q in data['results']:
|
||||||
TYPE_OF_QUESTIONS[q['id']] = q['type']
|
TYPE_OF_QUESTIONS[q['id']] = q['type']
|
||||||
|
|
||||||
async def loadItems():
|
async def load_items():
|
||||||
global ITEMS_ID_MAP
|
global ITEMS_ID_MAP
|
||||||
global ITEM_VARIATIONS_MAP
|
global ITEM_VARIATIONS_MAP
|
||||||
global CATEGORIES_LIST_MAP
|
global CATEGORIES_LIST_MAP
|
||||||
|
@ -54,14 +54,14 @@ async def loadItems():
|
||||||
data = res.json()
|
data = res.json()
|
||||||
for q in data['results']:
|
for q in data['results']:
|
||||||
# Map item id
|
# Map item id
|
||||||
itemName = checkAndGetName ('item', q)
|
itemName = check_and_get_name ('item', q)
|
||||||
if itemName and itemName in ITEMS_ID_MAP:
|
if itemName and itemName in ITEMS_ID_MAP:
|
||||||
ITEMS_ID_MAP[itemName] = q['id']
|
ITEMS_ID_MAP[itemName] = q['id']
|
||||||
# If item has variations, map them, too
|
# If item has variations, map them, too
|
||||||
if itemName in ITEM_VARIATIONS_MAP and VARIATIONS_TAG in q:
|
if itemName in ITEM_VARIATIONS_MAP and VARIATIONS_TAG in q:
|
||||||
isBedInRoom = itemName == 'bed_in_room'
|
isBedInRoom = itemName == 'bed_in_room'
|
||||||
for v in q[VARIATIONS_TAG]:
|
for v in q[VARIATIONS_TAG]:
|
||||||
variationName = checkAndGetName('variation', v)
|
variationName = check_and_get_name('variation', v)
|
||||||
if variationName and variationName in ITEM_VARIATIONS_MAP[itemName]:
|
if variationName and variationName in ITEM_VARIATIONS_MAP[itemName]:
|
||||||
ITEM_VARIATIONS_MAP[itemName][variationName] = v['id']
|
ITEM_VARIATIONS_MAP[itemName][variationName] = v['id']
|
||||||
if isBedInRoom and variationName in ITEM_VARIATIONS_MAP['bed_in_room']:
|
if isBedInRoom and variationName in ITEM_VARIATIONS_MAP['bed_in_room']:
|
||||||
|
@ -70,7 +70,7 @@ async def loadItems():
|
||||||
roomName = v['value'][list(v['value'].keys())[0]]
|
roomName = v['value'][list(v['value'].keys())[0]]
|
||||||
ROOM_TYPE_NAMES[v['id']] = roomName
|
ROOM_TYPE_NAMES[v['id']] = roomName
|
||||||
# Adds itself to the category list
|
# Adds itself to the category list
|
||||||
categoryName = checkAndGetCategory ('item', q)
|
categoryName = check_and_get_category ('item', q)
|
||||||
if not categoryName: continue
|
if not categoryName: continue
|
||||||
CATEGORIES_LIST_MAP[categoryName].append(q['id'])
|
CATEGORIES_LIST_MAP[categoryName].append(q['id'])
|
||||||
if (EXTRA_PRINTS):
|
if (EXTRA_PRINTS):
|
||||||
|
@ -84,38 +84,38 @@ async def loadItems():
|
||||||
print (ROOM_TYPE_NAMES)
|
print (ROOM_TYPE_NAMES)
|
||||||
|
|
||||||
# Tries to get an item name from metadata. Prints a warning if an item has no metadata
|
# Tries to get an item name from metadata. Prints a warning if an item has no metadata
|
||||||
def checkAndGetName(type, q):
|
def check_and_get_name(type, q):
|
||||||
itemName = extractMetadataName(q)
|
itemName = extract_metadata_name(q)
|
||||||
if not itemName and EXTRA_PRINTS:
|
if not itemName and EXTRA_PRINTS:
|
||||||
print (type + ' ' + q['id'] + ' has not been mapped.')
|
print (type + ' ' + q['id'] + ' has not been mapped.')
|
||||||
return itemName
|
return itemName
|
||||||
|
|
||||||
def checkAndGetCategory (type, q):
|
def check_and_get_category (type, q):
|
||||||
categoryName = extractCategory (q)
|
categoryName = extract_category (q)
|
||||||
if not categoryName and EXTRA_PRINTS:
|
if not categoryName and EXTRA_PRINTS:
|
||||||
print (type + ' ' + q['id'] + ' has no category set.')
|
print (type + ' ' + q['id'] + ' has no category set.')
|
||||||
return categoryName
|
return categoryName
|
||||||
|
|
||||||
# Checks if the item has specified metadata name
|
# Checks if the item has specified metadata name
|
||||||
def internalNameCheck (toExtract, name):
|
def internal_name_check (toExtract, name):
|
||||||
return toExtract and name and METADATA_TAG in toExtract and toExtract[METADATA_TAG][METADATA_NAME] == str(name)
|
return toExtract and name and METADATA_TAG in toExtract and toExtract[METADATA_TAG][METADATA_NAME] == str(name)
|
||||||
|
|
||||||
# Returns the item_name metadata from the item or None if not defined
|
# Returns the item_name metadata from the item or None if not defined
|
||||||
def extractMetadataName (toExtract):
|
def extract_metadata_name (toExtract):
|
||||||
return extractData(toExtract, [METADATA_TAG, METADATA_NAME])
|
return extract_data(toExtract, [METADATA_TAG, METADATA_NAME])
|
||||||
|
|
||||||
# Returns the category_name metadata from the item or None if not defined
|
# Returns the category_name metadata from the item or None if not defined
|
||||||
def extractCategory (toExtract):
|
def extract_category (toExtract):
|
||||||
return extractData(toExtract, [METADATA_TAG, METADATA_CATEGORY])
|
return extract_data(toExtract, [METADATA_TAG, METADATA_CATEGORY])
|
||||||
|
|
||||||
def extractData (dataFrom, tags):
|
def extract_data (dataFrom, tags):
|
||||||
data = dataFrom
|
data = dataFrom
|
||||||
for t in tags:
|
for t in tags:
|
||||||
if t not in data: return None
|
if t not in data: return None
|
||||||
data = data[t]
|
data = data[t]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def keyFromValue(dict, value):
|
def key_from_value(dict, value):
|
||||||
return [k for k,v in dict.items() if v == value]
|
return [k for k,v in dict.items() if v == value]
|
||||||
|
|
||||||
def sizeof_fmt(num, suffix="B"):
|
def sizeof_fmt(num, suffix="B"):
|
||||||
|
@ -125,7 +125,7 @@ def sizeof_fmt(num, suffix="B"):
|
||||||
num /= 1000.0
|
num /= 1000.0
|
||||||
return f"{num:.1f}Yi{suffix}"
|
return f"{num:.1f}Yi{suffix}"
|
||||||
|
|
||||||
async def getOrderByCode(request, code, throwException=False):
|
async def get_order_by_code(request, code, throwException=False):
|
||||||
res = await request.app.ctx.om.get_order(code=code)
|
res = await request.app.ctx.om.get_order(code=code)
|
||||||
if not throwException:
|
if not throwException:
|
||||||
return res
|
return res
|
||||||
|
@ -133,10 +133,10 @@ async def getOrderByCode(request, code, throwException=False):
|
||||||
raise exceptions.BadRequest(f"[getOrderByCode] Code {code} not found!")
|
raise exceptions.BadRequest(f"[getOrderByCode] Code {code} not found!")
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getPeopleInRoomByRoomId(request, roomId):
|
def get_people_in_room_by_code(request, code):
|
||||||
c = request.app.ctx.om.cache
|
c = request.app.ctx.om.cache
|
||||||
ret = []
|
ret = []
|
||||||
for person in c.values():
|
for person in c.values():
|
||||||
if person.room_id == roomId:
|
if person.room_id == code:
|
||||||
ret.append(person)
|
ret.append(person)
|
||||||
return ret
|
return ret
|
Loading…
Reference in New Issue