Improved CSV export #26

Merged
drew merged 1 commits from stranck-dev into drew-dev 2024-05-13 20:10:59 +00:00
5 changed files with 89 additions and 91 deletions

View File

@ -4,7 +4,10 @@ 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 *
from io import StringIO
from sanic.log import logger from sanic.log import logger
import csv
import time
bp = Blueprint("admin", url_prefix="/manage/admin") bp = Blueprint("admin", url_prefix="/manage/admin")
@ -42,6 +45,7 @@ async def login_as(request, code, order:Order):
@bp.get('/room/verify') @bp.get('/room/verify')
async def verify_rooms(request, order:Order): async def verify_rooms(request, order:Order):
await clear_cache(request, order)
already_checked, success = await request.app.ctx.om.update_cache() already_checked, success = await request.app.ctx.om.update_cache()
if not already_checked and success: if not already_checked and success:
orders = filter(lambda x: x.status not in ['c', 'e'] and x.room_id == x.code, request.app.ctx.om.cache.values()) orders = filter(lambda x: x.status not in ['c', 'e'] and x.room_id == x.code, request.app.ctx.om.cache.values())
@ -56,6 +60,7 @@ async def unconfirm_room(request, code, order:Order):
@bp.get('/room/autoconfirm') @bp.get('/room/autoconfirm')
async def autoconfirm_room(request, code, order:Order): async def autoconfirm_room(request, code, order:Order):
await clear_cache(request, order)
orders = request.app.ctx.om.cache.values() orders = request.app.ctx.om.cache.values()
for order in orders: for order in orders:
if(order.code == order.room_id and not order.room_confirmed and len(order.room_members) == order.room_person_no): if(order.code == order.room_id and not order.room_confirmed and len(order.room_members) == order.room_person_no):
@ -65,6 +70,7 @@ async def autoconfirm_room(request, code, order:Order):
@bp.get('/room/delete/<code>') @bp.get('/room/delete/<code>')
async def delete_room(request, code, order:Order): async def delete_room(request, code, order:Order):
await clear_cache(request, order)
dOrder = await get_order_by_code(request, code, throwException=True) dOrder = await get_order_by_code(request, code, throwException=True)
ppl = await get_people_in_room_by_code(request, code) ppl = await get_people_in_room_by_code(request, code)
@ -84,6 +90,7 @@ async def delete_room(request, code, order:Order):
@bp.post('/room/rename/<code>') @bp.post('/room/rename/<code>')
async def rename_room(request, code, order:Order): async def rename_room(request, code, order:Order):
await clear_cache(request, order)
dOrder = await get_order_by_code(request, code, throwException=True) dOrder = await get_order_by_code(request, code, throwException=True)
name = request.form.get('name') name = request.form.get('name')
@ -108,3 +115,80 @@ async def propic_remind_missing(request, order:Order):
await send_missing_propic_message(order, missingPropic, missingFursuitPropic) await send_missing_propic_message(order, missingPropic, missingFursuitPropic)
return redirect(f'/manage/admin') return redirect(f'/manage/admin')
@bp.get('/export/export')
async def export_export(request, order:Order):
await clear_cache(request, order)
data = StringIO()
w = csv.writer(data)
w.writerow(['Status', 'Code', 'First name', 'Last name', 'Nick', 'State', 'Card', 'Artist', 'Fursuiter', 'Sponsorhip', 'Early', 'Late', 'Daily', 'Daily days', 'Shirt', 'Room type', 'Room count', 'Room members', 'Payment', 'Price', 'Refunds', 'Staff'])
orders = request.app.ctx.om.cache.values()
order: Order
for order in orders:
w.writerow([
order.status,
order.code,
order.first_name,
order.last_name,
order.name,
order.country,
order.has_card,
order.is_artist,
order.is_fursuiter,
order.sponsorship,
order.has_early,
order.has_late,
order.daily,
','.join(order.dailyDays),
order.shirt_size,
ROOM_TYPE_NAMES[order.bed_in_room] if order.bed_in_room in ROOM_TYPE_NAMES else "-",
len(order.room_members),
','.join(order.room_members),
order.payment_provider,
order.total - order.fees,
order.refunds,
order.ans('staff_role') or 'attendee',
])
data.seek(0)
str = data.read() #data.read().decode("UTF-8")
data.flush()
data.close()
return raw(str, status=200, headers={'Content-Disposition': f'attachment; filename="export_{int(time.time())}.csv"', "Content-Type": "text/csv; charset=UTF-8"})
@bp.get('/export/hotel')
async def export_hotel(request, order:Order):
await clear_cache(request, order)
data = StringIO()
w = csv.writer(data)
w.writerow(['Room type', 'Room name', 'Room code', 'First name', 'Last name', 'Birthday', 'Address', 'Email', 'Phone number', 'Status'])
orders = sorted(request.app.ctx.om.cache.values(), key=lambda d: (d.room_id if d.room_id != None else "~"))
order: Order
for order in orders:
w.writerow([
ROOM_TYPE_NAMES[order.bed_in_room] if order.bed_in_room in ROOM_TYPE_NAMES else "-",
order.room_name,
order.room_id,
order.first_name,
order.last_name,
order.birth_date,
order.address,
order.email,
order.phone,
order.status,
order.code
])
data.seek(0)
str = data.read() #data.read().decode("UTF-8")
data.flush()
data.close()
return raw(str, status=200, headers={'Content-Disposition': f'attachment; filename="hotel_{int(time.time())}.csv"', "Content-Type": "text/csv; charset=UTF-8"})

3
app.py
View File

@ -29,14 +29,13 @@ 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 karaoke import bp as karaoke_bp from karaoke import bp as karaoke_bp
from export import bp as export_bp
from stats import bp as stats_bp from stats import bp as stats_bp
from api import bp as api_bp from api import bp as api_bp
from carpooling import bp as carpooling_bp from carpooling import bp as carpooling_bp
from checkin import bp as checkin_bp from checkin import bp as checkin_bp
from admin import bp as admin_bp from admin import bp as admin_bp
app.blueprint([room_bp, karaoke_bp, propic_bp, export_bp, stats_bp, api_bp, carpooling_bp, checkin_bp, admin_bp]) app.blueprint([room_bp, karaoke_bp, propic_bp, stats_bp, api_bp, carpooling_bp, checkin_bp, admin_bp])
async def clear_session(response): async def clear_session(response):

View File

@ -1,87 +0,0 @@
from sanic.response import text
from sanic import Blueprint, exceptions
from ext import *
from config import headers, ADMINS, ORGANIZER, EVENT_NAME
bp = Blueprint("export", url_prefix="/manage/export")
@bp.route("/export.csv")
async def export_csv(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 not order.isAdmin(): raise exceptions.Forbidden("Birichino :)")
page = 0
orders = {}
ret = 'status;code;nome;cognome;nick;nazione;tessera;artista;fursuiter;sponsorship;early;late;shirt;roomsize;roommembers;payment;price;refunds;staff\n'
while 1:
page += 1
r = httpx.get(f'https://reg.furizon.net/api/v1/organizers/{ORGANIZER}/events/{EVENT_NAME}/orders/?page={page}', headers=headers)
if r.status_code == 404: break
for r in r.json()['results']:
o = Order(r)
orders[o.code] = o
ret += (';'.join(map(lambda x: str(x),
[
o.status,
o.code,
o.first_name,
o.last_name,
o.name,
o.country,
o.has_card or '',
o.is_artist or '',
o.is_fursuiter or '',
o.sponsorship or '',
o.has_early or '',
o.has_late or '',
o.shirt_size,
len(o.room_members),
','.join(o.room_members),
o.payment_provider,
o.total-o.fees,
o.refunds,
o.ans('staff_role') or 'attendee',
]))) + "\n"
return text(ret)
@bp.route("/hotel_export.csv")
async def export_hotel_csv(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.code not in ['HWUC9','9YKGJ']: raise exceptions.Forbidden("Birichino :)")
page = 0
orders = {}
ret = 'code;nome;cognome;datanascita;posnascita;indirizzo;mail;status\n'
while 1:
page += 1
r = httpx.get(f'https://reg.furizon.net/api/v1/organizers/{ORGANIZER}/events/{EVENT_NAME}/orders/?page={page}', headers=headers)
if r.status_code == 404: break
for r in r.json()['results']:
o = Order(r)
orders[o.code] = o
ret += (';'.join(map(lambda x: str(x),
[
o.code,
o.first_name,
o.last_name,
o.birth_date,
o.birth_location,
o.address,
o.email,
o.status
]))) + "\n"
return text(ret)

2
ext.py
View File

@ -57,7 +57,7 @@ class Order:
idata = data['invoice_address'] idata = data['invoice_address']
if idata: if idata:
self.address = f"{idata['street']} - {idata['zipcode']} {idata['city']} - {idata['country']}" self.address = f"{idata['street'].strip()} - {idata['zipcode'].strip()} {idata['city'].strip()} - {idata['country'].strip()}".replace("\n", "").replace("\r", "")
self.country = idata['country'] self.country = idata['country']
for p in self.data['positions']: for p in self.data['positions']:

View File

@ -16,6 +16,8 @@
<a href="/manage/admin/room/verify" role="button" title="Will unconfirm rooms that fail the default check. Useful when editing answers from Pretix">Verify Rooms</a> <a href="/manage/admin/room/verify" role="button" title="Will unconfirm rooms that fail the default check. Useful when editing answers from Pretix">Verify Rooms</a>
<a href="#" onclick="confirmAction('propicReminder', this)" role="button" title="Will remind via mail all people who event uploaded a badge to do it" action="/manage/admin/propic/remind">Remind badge upload</a> <a href="#" onclick="confirmAction('propicReminder', this)" role="button" title="Will remind via mail all people who event uploaded a badge to do it" action="/manage/admin/propic/remind">Remind badge upload</a>
<a href="/manage/admin/room/autoconfirm" role="button" title="Will confirm all the full rooms that are still unconfirmed">Auto-confirm Rooms</a> <a href="/manage/admin/room/autoconfirm" role="button" title="Will confirm all the full rooms that are still unconfirmed">Auto-confirm Rooms</a>
<a download href="/manage/admin/export/export" role="button" title="Will export most of informations about the orders">Export CSV</a>
<a download href="/manage/admin/export/hotel" role="button" title="Will export a CSV for the hotel accomodation">Export hotel CSV</a>
<hr> <hr>
{% include 'components/confirm_action_modal.html' %} {% include 'components/confirm_action_modal.html' %}
</main> </main>