diff --git a/admin.py b/admin.py index 9c37864..7a8d384 100644 --- a/admin.py +++ b/admin.py @@ -54,6 +54,15 @@ async def unconfirm_room(request, code, order:Order): await unconfirm_room_by_order(order=dOrder, throw=True, request=request) return redirect(f'/manage/nosecount') +@bp.get('/room/autoconfirm') +async def autoconfirm_room(request, code, order:Order): + orders = request.app.ctx.om.cache.values() + for order in orders: + if(order.code == order.room_id and not order.room_confirmed and len(order.room_members) == order.room_person_no): + logger.info(f"Auto-Confirming room {order.room_id}") + await confirm_room_by_order(order, request) + return redirect(f'/manage/admin') + @bp.get('/room/delete/') async def delete_room(request, code, order:Order): dOrder = await get_order_by_code(request, code, throwException=True) diff --git a/app.py b/app.py index 2e51d66..f029ba2 100644 --- a/app.py +++ b/app.py @@ -55,7 +55,7 @@ async def handleException(request, exception): traceback.print_exc() if statusCode == 403: - clear_session(r) + await clear_session(r) return r diff --git a/email_util.py b/email_util.py index 7b0fd01..3ba60fb 100644 --- a/email_util.py +++ b/email_util.py @@ -80,16 +80,17 @@ async def send_unconfirm_message(room_order, orders): issues_html += "" for member in orders: - plain_body = EMAILS_TEXT["ROOM_UNCONFIRM_TEXT"]['plain'].format(member.name, room_order.room_name, issues_plain) - html_body = render_email_template(EMAILS_TEXT["ROOM_UNCONFIRM_TITLE"], EMAILS_TEXT["ROOM_UNCONFIRM_TEXT"]['html'].format(member.name, room_order.room_name, issues_html)) - plain_text = MIMEText(plain_body, "plain") - html_text = MIMEText(html_body, "html") - message = MIMEMultipart("alternative") - message.attach(plain_text) - message.attach(html_text) - message['Subject'] = f'[{EMAIL_SENDER_NAME}] Your room cannot be confirmed' - message['To'] = f"{member.name} <{member.email}>" - memberMessages.append(message) + if(member.status != 'canceled'): + plain_body = EMAILS_TEXT["ROOM_UNCONFIRM_TEXT"]['plain'].format(member.name, room_order.room_name, issues_plain) + html_body = render_email_template(EMAILS_TEXT["ROOM_UNCONFIRM_TITLE"], EMAILS_TEXT["ROOM_UNCONFIRM_TEXT"]['html'].format(member.name, room_order.room_name, issues_html)) + plain_text = MIMEText(plain_body, "plain") + html_text = MIMEText(html_body, "html") + message = MIMEMultipart("alternative") + message.attach(plain_text) + message.attach(html_text) + message['Subject'] = f'[{EMAIL_SENDER_NAME}] Your room cannot be confirmed' + message['To'] = f"{member.name} <{member.email}>" + memberMessages.append(message) if len(memberMessages) == 0: return diff --git a/ext.py b/ext.py index 35aa170..1039f7d 100644 --- a/ext.py +++ b/ext.py @@ -20,6 +20,10 @@ class Order: self.time = time() self.data = data + if(len(self.data['positions']) == 0): + for fee in data['fees']: + if(fee['fee_type'] == "cancellation"): + self.data['status'] = 'c' self.status = {'n': 'pending', 'p': 'paid', 'e': 'expired', 'c': 'canceled'}[self.data['status']] self.secret = data['secret'] @@ -218,13 +222,18 @@ class Order: #if ans['question'] == 40: # del self.answers[i]['options'] # del self.answers[i]['option_identifiers'] - - res = await pretixClient.patch(f'orderpositions/{self.position_id}/', json={'answers': self.answers}, expectedStatusCodes=None) + + ans = [] if self.status == "canceled" else self.answers + res = await pretixClient.patch(f'orderpositions/{self.position_id}/', json={'answers': ans}, expectedStatusCodes=None) if res.status_code != 200: - for ans, err in zip(self.answers, res.json()['answers']): - if err: - logger.error ('[ANSWERS SENDING] ERROR ON %s %s', ans, err) + e = res.json() + if "answers" in e: + for ans, err in zip(self.answers, res.json()['answers']): + if err: + logger.error ('[ANSWERS SENDING] ERROR ON %s %s', ans, err) + else: + logger.error("[ANSWERS SENDING] GENERIC ERROR. Response: '%s'", str(e)) raise exceptions.ServerError('There has been an error while updating this answers.') diff --git a/messages.py b/messages.py index 5664829..455f238 100644 --- a/messages.py +++ b/messages.py @@ -3,15 +3,16 @@ ROOM_ERROR_TYPES = { '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.", - 'capacity_mismatch': "The number of people in your room mismatches your type of ticket." + 'capacity_mismatch': "The number of people in your room mismatches your type of ticket.", + 'canceled': "Someone in your room canceled his booking and it was removed from your room." } EMAILS_TEXT = { "ROOM_UNCONFIRM_TITLE": "Your room got unconfirmed", "ROOM_UNCONFIRM_TEXT": { - 'html': "Hello {0}
We had to unconfirm your room '{1}' due to the following issues:

{2}

Please contact your room's owner or contact our support for further informations at https://furizon.net/contact/.
Thank you.

Manage booking", + 'html': "Hello {0}
We had to unconfirm or change your room '{1}' due to the following issues:

{2}

Please contact your room's owner or contact our support for further informations at https://furizon.net/contact/.
Thank you.

Manage booking", - 'plain': "Hello {0}\nWe had to unconfirm your room '{1}' due to the following issues:\n{2}\nPlease contact your room's owner or contact our support for further informations at https://furizon.net/contact/.\nThank you\n\nTo manage your booking: https://reg.furizon.net/manage/welcome" + 'plain': "Hello {0}\nWe had to unconfirm or change your room '{1}' due to the following issues:\n{2}\nPlease contact your room's owner or contact our support for further informations at https://furizon.net/contact/.\nThank you\n\nTo manage your booking: https://reg.furizon.net/manage/welcome" }, diff --git a/room.py b/room.py index 6fb9dc3..a0f41c2 100644 --- a/room.py +++ b/room.py @@ -5,6 +5,7 @@ from ext import * from config import headers import os from image_util import generate_room_preview, get_room +from utils import confirm_room_by_order bp = Blueprint("room", url_prefix="/manage/room") @@ -303,40 +304,7 @@ async def confirm_room(request, order: Order, quotas: Quotas): #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 - room_members = [] - for m in order.room_members: - if m == order.code: - res = order - else: - res = await request.app.ctx.om.get_order(code=m) - - if res.room_id != order.code: - raise exceptions.BadRequest("Please contact support: some of the members in your room are actually somewhere else") - - if res.status != 'paid': - raise exceptions.BadRequest("Somebody hasn't paid.") - - if res.bed_in_room != bed_in_room: - raise exceptions.BadRequest("Somebody has a ticket for a different type of room!") - - if res.daily: - raise exceptions.BadRequest("Somebody in your room has a daily ticket!") - - room_members.append(res) - - - if len(room_members) != order.room_person_no: - raise exceptions.BadRequest("The number of people in your room mismatches your type of ticket!") - - for rm in room_members: - await rm.edit_answer('room_id', order.code) - await rm.edit_answer('room_confirmed', "True") - await rm.edit_answer('pending_roommates', None) - await rm.edit_answer('pending_room', None) - - for rm in room_members: - await rm.send_answers() + confirm_room_by_order(order, request) return redirect('/manage/welcome') diff --git a/tpl/admin.html b/tpl/admin.html index d234b31..98acde6 100644 --- a/tpl/admin.html +++ b/tpl/admin.html @@ -15,6 +15,7 @@ Manage rooms Verify Rooms Remind badge upload + Auto-confirm Rooms


{% include 'components/confirm_action_modal.html' %} diff --git a/tpl/welcome.html b/tpl/welcome.html index 89cbd50..390f721 100644 --- a/tpl/welcome.html +++ b/tpl/welcome.html @@ -88,8 +88,10 @@
Shuttle -

This year, a shuttle service operated by the tourism company of Val di Fiemme will be available. The shuttle service will consist of a bus serving the convention, with scheduled stops at major airports and train stations. More informations in the dedicated page.

-

Book now!

+

Due to the low number of requests, the shuttle service managed by Trentino Trasporti will not be available. Those who have purchased a bus ticket will be refunded directly by the transport company

+

On the Furizon Telegram group, there is an active topic dedicated to car sharing, and the staff is available to look for custom alternative solutions. We apologize for the inconvenience.

+
diff --git a/utils.py b/utils.py index d82879f..0876148 100644 --- a/utils.py +++ b/utils.py @@ -151,6 +151,42 @@ async def get_people_in_room_by_code(request, code, om=None): await om.update_cache() return filter(lambda rm: rm.room_id == code, om.cache.values()) +async def confirm_room_by_order(order, request): + bed_in_room = order.bed_in_room # Variation id of the ticket for that kind of room + room_members = [] + for m in order.room_members: + if m == order.code: + res = order + else: + res = await request.app.ctx.om.get_order(code=m) + + if res.room_id != order.code: + raise exceptions.BadRequest("Please contact support: some of the members in your room are actually somewhere else") + + if res.status != 'paid': + raise exceptions.BadRequest("Somebody hasn't paid.") + + if res.bed_in_room != bed_in_room: + raise exceptions.BadRequest("Somebody has a ticket for a different type of room!") + + if res.daily: + raise exceptions.BadRequest("Somebody in your room has a daily ticket!") + + room_members.append(res) + + + if len(room_members) != order.room_person_no: + raise exceptions.BadRequest("The number of people in your room mismatches your type of ticket!") + + for rm in room_members: + await rm.edit_answer('room_id', order.code) + await rm.edit_answer('room_confirmed', "True") + await rm.edit_answer('pending_roommates', None) + await rm.edit_answer('pending_room', None) + + for rm in room_members: + await rm.send_answers() + async def unconfirm_room_by_order(order, room_members=None, throw=True, request=None, om=None): if not om: om = request.app.ctx.om if not order.room_confirmed: @@ -164,6 +200,17 @@ async def unconfirm_room_by_order(order, room_members=None, throw=True, request= await p.edit_answer('room_confirmed', "False") await p.send_answers() +async def remove_members_from_room(order, removeMembers): + didSomething = False + for member in removeMembers: + if (member in order.room_members): + order.room_members.remove(member) + didSomething = True + if(didSomething): + await order.edit_answer("room_members", ','.join(order.room_members)) + await order.send_answers() + return didSomething + async def validate_rooms(request, rooms, om): logger.info('Validating rooms...') if not om: om = request.app.ctx.om @@ -171,6 +218,7 @@ async def validate_rooms(request, rooms, om): # rooms_to_unconfirm is the room that MUST be unconfirmed, room_with_errors is a less strict set containing all rooms with kind-ish errors rooms_to_unconfirm = [] room_with_errors = [] + remove_members = [] # Validate rooms for order in rooms: @@ -195,43 +243,58 @@ async def validate_rooms(request, rooms, om): # Get confirmed rooms that fail validation failed_confirmed_rooms = list(filter(lambda fr: (fr[0].room_confirmed == True), rooms_to_unconfirm)) + didSomething = False + if len(failed_confirmed_rooms) == 0: logger.info('[ROOM VALIDATION] No rooms to unconfirm.') - return + else: + didSomething = True + logger.info(f"[ROOM VALIDATION] Trying to unconfirm {len(failed_confirmed_rooms)} rooms...") - logger.info(f"[ROOM VALIDATION] Trying to unconfirm {len(failed_confirmed_rooms)} rooms...") - - # Try unconfirming them - for rtu in failed_confirmed_rooms: - order = rtu[0] - member_orders = rtu[2] - logger.warning(f"[ROOM VALIDATION] [UNCONFIRMING] Unconfirming room {order.code}...") - - # Unconfirm and email users about the room - if UNCONFIRM_ROOMS_ENABLE: - await unconfirm_room_by_order(order, member_orders, False, None, om) - - logger.info(f"[ROOM VALIDATION] Sending unconfirm notice to room members...") - sent_count = 0 - # Send unconfirm notice via email - for rtu in failed_confirmed_rooms: - order = rtu[0] - member_orders = rtu[2] - try: + # Try unconfirming them + for rtu in failed_confirmed_rooms: + order = rtu[0] + member_orders = rtu[2] + logger.warning(f"[ROOM VALIDATION] [UNCONFIRMING] Unconfirming room {order.code}...") + + # Unconfirm and email users about the room if UNCONFIRM_ROOMS_ENABLE: - await send_unconfirm_message(order, member_orders) - sent_count += len(member_orders) - except Exception as ex: - if EXTRA_PRINTS: logger.exception(str(ex)) - logger.info(f"[ROOM VALIDATION] Sent {sent_count} emails") + await unconfirm_room_by_order(order, member_orders, False, None, om) + + for r in rooms_to_unconfirm: + order = r[0] + removeMembers = r[3] + if len(removeMembers) > 0: + logger.warning(f"[ROOM VALIDATION] [REMOVING] Removing members '{','.join(removeMembers)}' from room {order.code}") + + if UNCONFIRM_ROOMS_ENABLE: + didSomething |= await remove_members_from_room(order, removeMembers) + if(r not in failed_confirmed_rooms): failed_confirmed_rooms.append(r) + + + if(didSomething): + logger.info(f"[ROOM VALIDATION] Sending unconfirm notice to room members...") + sent_count = 0 + # Send unconfirm notice via email + for rtu in failed_confirmed_rooms: + order = rtu[0] + member_orders = rtu[2] + try: + if UNCONFIRM_ROOMS_ENABLE: + await send_unconfirm_message(order, member_orders) + sent_count += len(member_orders) + except Exception as ex: + if EXTRA_PRINTS: logger.exception(str(ex)) + logger.info(f"[ROOM VALIDATION] Sent {sent_count} emails") async def check_room(request, order, om=None): room_errors = [] room_members = [] + remove_members = [] use_cached = request == None if not om: om = request.app.ctx.om - if not order or not order.room_id or order.room_id != order.code: return (order, False, room_members) + if not order or not order.room_id or order.room_id != order.code: return (order, False, room_members, remove_members) # This is not needed anymore you buy tickets already #if quotas.get_left(len(order.room_members)) == 0: @@ -249,8 +312,12 @@ async def check_room(request, order, om=None): if res.room_id != order.code: room_errors.append((res.code, 'room_id_mismatch')) allOk = False - - if res.status != 'paid': + + if res.status == 'canceled': + room_errors.append((res.code, 'canceled')) + remove_members.append(res.code) + allOk = False + elif res.status != 'paid': room_errors.append((res.code, 'unpaid')) if res.bed_in_room != bed_in_room: @@ -270,4 +337,4 @@ async def check_room(request, order, om=None): if order.room_confirmed: allOk = False order.set_room_errors(room_errors) - return (order, allOk, room_members) \ No newline at end of file + return (order, allOk, room_members, remove_members) \ No newline at end of file