From 2b9004a776caa5e073b4d5f848e5b3a3db9c0b3c Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Wed, 9 Oct 2024 20:36:45 -0400 Subject: [PATCH 01/19] Fix attendee account page Fixes some errors in the template for attendee accounts that is preventing the page from loading. --- uber/templates/reg_admin/attendee_account_form.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uber/templates/reg_admin/attendee_account_form.html b/uber/templates/reg_admin/attendee_account_form.html index dc173c1fb..1f9ca4b3e 100644 --- a/uber/templates/reg_admin/attendee_account_form.html +++ b/uber/templates/reg_admin/attendee_account_form.html @@ -78,10 +78,10 @@

{{ account.email }} Attendee Account

{% if attendee.marketplace_application %} - View Application + View Application {% else %} N/A - {% endfor %} + {% endif %} {{ attendee.badge_num }} From 953264ae31da31ab0701f611731e37381d4cf0cb Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Fri, 11 Oct 2024 13:46:44 -0400 Subject: [PATCH 02/19] Add accepted dealers export Requested for the hotel lottery. --- uber/site_sections/hotel_lottery_admin.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/uber/site_sections/hotel_lottery_admin.py b/uber/site_sections/hotel_lottery_admin.py index 9e80e65d5..7c9afa7fe 100644 --- a/uber/site_sections/hotel_lottery_admin.py +++ b/uber/site_sections/hotel_lottery_admin.py @@ -12,7 +12,7 @@ from uber.decorators import all_renderable, log_pageview, ajax, ajax_gettable, csv_file, requires_account, render from uber.errors import HTTPRedirect from uber.forms import load_forms -from uber.models import Attendee, LotteryApplication, Email, Tracking, PageViewTracking +from uber.models import Attendee, Group, LotteryApplication, Email, Tracking, PageViewTracking from uber.tasks.email import send_email from uber.utils import Order, get_page, RegistrationCode, validate_model, get_age_from_birthday, normalize_email_legacy @@ -175,7 +175,15 @@ def history(self, session, id): ).order_by(Tracking.when).all(), 'pageviews': session.query(PageViewTracking).filter(PageViewTracking.which == repr(application)) } - + + @csv_file + def accepted_dealers(self, out, session): + out.writerow(['Group Name', 'Group ID', 'Reg ID']) + + for dealer in session.query(Attendee).join(Group, Attendee.group_id == Group.id).filter( + Group.is_dealer, Group.status in [c.APPROVED, c.SHARED]): + out.writerow([dealer.group.name, dealer.group.id, dealer.id]) + @csv_file def interchange_export(self, out, session, staff_lottery=False): def print_dt(dt): From 68981d6644ecd82c0cb00a72d5f2df9ec5812698 Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Fri, 11 Oct 2024 13:59:12 -0400 Subject: [PATCH 03/19] Fix accepted dealer export --- uber/site_sections/hotel_lottery_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uber/site_sections/hotel_lottery_admin.py b/uber/site_sections/hotel_lottery_admin.py index 7c9afa7fe..555ccc529 100644 --- a/uber/site_sections/hotel_lottery_admin.py +++ b/uber/site_sections/hotel_lottery_admin.py @@ -181,7 +181,7 @@ def accepted_dealers(self, out, session): out.writerow(['Group Name', 'Group ID', 'Reg ID']) for dealer in session.query(Attendee).join(Group, Attendee.group_id == Group.id).filter( - Group.is_dealer, Group.status in [c.APPROVED, c.SHARED]): + Group.is_dealer, Group.status.in_([c.APPROVED, c.SHARED])): out.writerow([dealer.group.name, dealer.group.id, dealer.id]) @csv_file From 3d83b925cc15f67fe2ad9136b2b458f85b31537a Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Fri, 18 Oct 2024 16:14:00 -0400 Subject: [PATCH 04/19] Remove IFT from default artist payout method --- uber/configspec.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/uber/configspec.ini b/uber/configspec.ini index 505d5b1cb..428e42701 100644 --- a/uber/configspec.ini +++ b/uber/configspec.ini @@ -1997,7 +1997,6 @@ refund = string(default="Refund") [[artist_payout_method]] check = string(default="Check") -ift = string(default="Interfund Transfer") other = string(default="Other - See admin notes") [[authnet_txn_type]] From acc52d1859ed17a6c3f4c88d6839c0ce5106671f Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Fri, 18 Oct 2024 16:15:04 -0400 Subject: [PATCH 05/19] Fetch SignNow token via task Replaces the old, broken method of fetching the SignNow token with a task that checks if it's been 23 hours since the last token was generated and grabs the new one if it has. --- uber/config.py | 15 ++++++++++----- uber/tasks/redis.py | 21 +++++++++++++++++++-- uber/utils.py | 17 ++++------------- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/uber/config.py b/uber/config.py index ac322b9b2..d280ca799 100644 --- a/uber/config.py +++ b/uber/config.py @@ -1190,6 +1190,9 @@ class AWSSecretFetcher: """ def __init__(self): + self.start_session() + + def start_session(self): import boto3 aws_session = boto3.session.Session( @@ -1202,10 +1205,15 @@ def __init__(self): region_name=c.AWS_REGION ) + self.session_expiration = datetime.now() + timedelta(hours=6) + def get_secret(self, secret_name): import json from botocore.exceptions import ClientError + if not self.client: + self.start_session() + try: get_secret_value_response = self.client.get_secret_value( SecretId=secret_name @@ -1251,9 +1259,9 @@ def get_signnow_secret(self): signnow_secret = self.get_secret(c.AWS_SIGNNOW_SECRET_NAME) if signnow_secret: - c.SIGNNOW_ACCESS_TOKEN = signnow_secret.get('ACCESS_TOKEN', '') or c.SIGNNOW_ACCESS_TOKEN c.SIGNNOW_CLIENT_ID = signnow_secret.get('CLIENT_ID', '') or c.SIGNNOW_CLIENT_ID c.SIGNNOW_CLIENT_SECRET = signnow_secret.get('CLIENT_SECRET', '') or c.SIGNNOW_CLIENT_SECRET + return signnow_secret def get_config_files(plugin_name, module_dir): config_files_str = os.environ.get(f"{plugin_name.upper()}_CONFIG_FILES", "") @@ -1361,10 +1369,7 @@ def parse_config(plugin_name, module_dir): setattr(c, conf.upper(), val) if c.AWS_SECRET_SERVICE_NAME: - aws_secrets_client = AWSSecretFetcher() - aws_secrets_client.get_all_secrets() -else: - aws_secrets_client = None + AWSSecretFetcher().get_all_secrets() signnow_python_sdk.Config(client_id=c.SIGNNOW_CLIENT_ID, client_secret=c.SIGNNOW_CLIENT_SECRET, diff --git a/uber/tasks/redis.py b/uber/tasks/redis.py index 1d8182abe..87f63d620 100644 --- a/uber/tasks/redis.py +++ b/uber/tasks/redis.py @@ -1,14 +1,16 @@ import json import re +from dateutil import parser as dateparser from datetime import datetime, timedelta +from pockets.autolog import log from sqlalchemy import any_ -from uber.config import c +from uber.config import c, AWSSecretFetcher from uber.tasks import celery -__all__ = ['expire_processed_saml_assertions', 'update_shirt_counts', 'update_problem_names'] +__all__ = ['expire_processed_saml_assertions', 'set_signnow_key', 'update_shirt_counts', 'update_problem_names'] @celery.schedule(timedelta(minutes=30)) @@ -25,6 +27,21 @@ def expire_processed_saml_assertions(): rsession.execute() +@celery.schedule(timedelta(15)) +def set_signnow_key(): + if not c.AWS_SIGNNOW_SECRET_NAME or not c.SIGNNOW_DEALER_TEMPLATE_ID: + return + + signnow_access_key = c.REDIS_STORE.get(c.REDIS_PREFIX + 'signnow_access_token') + if not signnow_access_key: + signnow_secret = AWSSecretFetcher().get_signnow_secret() + if not signnow_secret: + log.error("Attempted to update our SignNow token but we didn't get a secret back from AWS!") + c.REDIS_STORE.set(c.REDIS_PREFIX + 'signnow_access_token', signnow_secret.get('ACCESS_TOKEN', '')) + expire_date = dateparser.parse(signnow_secret.get('LAST_UPDATE', '')[:-6]) + timedelta(hours=23) + c.REDIS_STORE.expireat(c.REDIS_PREFIX + 'signnow_access_token', expire_date.timestamp()) + + @celery.schedule(timedelta(seconds=30)) def update_shirt_counts(): if not c.PRE_CON: diff --git a/uber/utils.py b/uber/utils.py index c845241b6..8487bcae4 100644 --- a/uber/utils.py +++ b/uber/utils.py @@ -1371,12 +1371,10 @@ def api_call_headers(self): "Accept": "application/json" } - def set_access_token(self, refresh=False): - from uber.config import aws_secrets_client + def set_access_token(self): + self.access_token = c.REDIS_STORE.get(c.REDIS_PREFIX + 'signnow_access_token') - self.access_token = c.SIGNNOW_ACCESS_TOKEN - - if self.access_token and not refresh: + if self.access_token: return if c.DEV_BOX and c.SIGNNOW_USERNAME and c.SIGNNOW_PASSWORD: @@ -1387,22 +1385,15 @@ def set_access_token(self, refresh=False): else: self.access_token = access_request['access_token'] return - elif not aws_secrets_client: - self.error_message = ("Couldn't get a SignNow access token because there was no AWS Secrets client. " - "If you're on a development box, you can instead use a username and password.") elif not c.AWS_SIGNNOW_SECRET_NAME: self.error_message = ("Couldn't get a SignNow access token because the secret name is not set. " "If you're on a development box, you can instead use a username and password.") - else: - aws_secrets_client.get_signnow_secret() - self.access_token = c.SIGNNOW_ACCESS_TOKEN - return def create_document(self, template_id, doc_title, folder_id='', uneditable_texts_list=None, fields={}): from requests import put from json import dumps, loads - self.set_access_token(refresh=True) + self.set_access_token() if not self.error_message: document_request = signnow_sdk.Template.copy(self.access_token, template_id, doc_title) From 681a8a149d0517c591caae848fd9e9faedd8260f Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Tue, 22 Oct 2024 18:47:13 -0400 Subject: [PATCH 06/19] Fix setting signnow access token I swear I was casting to INT already, but... anyway, this also adds more consistent error handling and logging when the access token doesn't actually get set correctly. --- uber/site_sections/dealer_admin.py | 3 +- uber/site_sections/group_admin.py | 4 +- uber/site_sections/preregistration.py | 13 ++--- uber/tasks/redis.py | 2 +- uber/utils.py | 76 +++++++++++++++++++-------- 5 files changed, 60 insertions(+), 38 deletions(-) diff --git a/uber/site_sections/dealer_admin.py b/uber/site_sections/dealer_admin.py index 9956df9b1..b8c5b0c32 100644 --- a/uber/site_sections/dealer_admin.py +++ b/uber/site_sections/dealer_admin.py @@ -230,7 +230,6 @@ def resend_signnow_link(self, session, id): signnow_request.send_dealer_signing_invite() if signnow_request.error_message: - log.error(signnow_request.error_message) raise HTTPRedirect("form?id={}&message={}", id, f"Error sending SignNow link: {signnow_request.error_message}") else: @@ -248,7 +247,7 @@ def set_table_shared(self, session, id, shared_group_name, **params): group.set_shared_with_name(shared_group_name) except ValueError as e: return {'error': str(e)} - + group.convert_to_shared(session) session.commit() diff --git a/uber/site_sections/group_admin.py b/uber/site_sections/group_admin.py index 4fff46583..342be5309 100644 --- a/uber/site_sections/group_admin.py +++ b/uber/site_sections/group_admin.py @@ -109,9 +109,7 @@ def form(self, session, new_dealer='', message='', **params): else: signnow_request = SignNowRequest(session=session, group=group) - if signnow_request.error_message: - log.error(signnow_request.error_message) - elif signnow_request.document: + if not signnow_request.error_message and signnow_request.document: session.add(signnow_request.document) signnow_signed = signnow_request.document.signed diff --git a/uber/site_sections/preregistration.py b/uber/site_sections/preregistration.py index 9b650e561..04cd1e295 100644 --- a/uber/site_sections/preregistration.py +++ b/uber/site_sections/preregistration.py @@ -1233,9 +1233,7 @@ def group_members(self, session, id, message='', **params): signnow_request = SignNowRequest(session=session, group=group, ident="terms_and_conditions", create_if_none=True) - if signnow_request.error_message: - log.error(signnow_request.error_message) - else: + if not signnow_request.error_message: signnow_document = signnow_request.document session.add(signnow_document) @@ -1249,9 +1247,7 @@ def group_members(self, session, id, message='', **params): signnow_document.link = signnow_link elif not signnow_link: signnow_link = signnow_request.create_dealer_signing_link() - if signnow_request.error_message: - log.error(signnow_request.error_message) - else: + if not signnow_request.error_message: signnow_document.link = signnow_link session.commit() @@ -1294,15 +1290,12 @@ def download_signnow_document(self, session, id, return_to='../preregistration/g group = session.group(id) signnow_request = SignNowRequest(session=session, group=group) if signnow_request.error_message: - log.error(signnow_request.error_message) raise HTTPRedirect(return_to + "?id={}&message={}", id, "We're having an issue fetching this document link. Please try again later!") elif signnow_request.document: if signnow_request.document.signed: download_link = signnow_request.get_download_link() - if signnow_request.error_message: - log.error(signnow_request.error_message) - else: + if not signnow_request.error_message: raise HTTPRedirect(download_link) raise HTTPRedirect(return_to + "?id={}&message={}", id, "We don't have a record of this document being signed.") diff --git a/uber/tasks/redis.py b/uber/tasks/redis.py index 87f63d620..9656fa741 100644 --- a/uber/tasks/redis.py +++ b/uber/tasks/redis.py @@ -39,7 +39,7 @@ def set_signnow_key(): log.error("Attempted to update our SignNow token but we didn't get a secret back from AWS!") c.REDIS_STORE.set(c.REDIS_PREFIX + 'signnow_access_token', signnow_secret.get('ACCESS_TOKEN', '')) expire_date = dateparser.parse(signnow_secret.get('LAST_UPDATE', '')[:-6]) + timedelta(hours=23) - c.REDIS_STORE.expireat(c.REDIS_PREFIX + 'signnow_access_token', expire_date.timestamp()) + c.REDIS_STORE.expireat(c.REDIS_PREFIX + 'signnow_access_token', int(expire_date.timestamp())) @celery.schedule(timedelta(seconds=30)) diff --git a/uber/utils.py b/uber/utils.py index 8487bcae4..0721dd4f4 100644 --- a/uber/utils.py +++ b/uber/utils.py @@ -1372,6 +1372,7 @@ def api_call_headers(self): } def set_access_token(self): + from uber.tasks.redis import set_signnow_key self.access_token = c.REDIS_STORE.get(c.REDIS_PREFIX + 'signnow_access_token') if self.access_token: @@ -1388,21 +1389,47 @@ def set_access_token(self): elif not c.AWS_SIGNNOW_SECRET_NAME: self.error_message = ("Couldn't get a SignNow access token because the secret name is not set. " "If you're on a development box, you can instead use a username and password.") + else: + set_signnow_key.delay() + self.access_token = c.REDIS_STORE.get(c.REDIS_PREFIX + 'signnow_access_token') + if not self.access_token: + self.error_message = "Couldn't set the SignNow key. Check the redis task for errors." + + if self.error_message: + log.error(self.error_message) + + def invalid_request(self, msg, check_group=False): + if check_group: + if not self.group: + self.error_message = f"{msg} without a group attached to the request!" + elif not self.document: + self.error_message = f"{msg} without a document attached to the request!" + else: + self.check_access_token(msg) + return bool(self.error_message) + + def check_access_token(self, msg): + if not self.access_token: + self.set_access_token() + if not self.access_token: + self.error_message = f"{msg} but access token is not set!" def create_document(self, template_id, doc_title, folder_id='', uneditable_texts_list=None, fields={}): from requests import put from json import dumps, loads + if self.invalid_request("Tried to create a document"): + log.error(self.error_message) + return - self.set_access_token() - if not self.error_message: - document_request = signnow_sdk.Template.copy(self.access_token, template_id, doc_title) + document_request = signnow_sdk.Template.copy(self.access_token, template_id, doc_title) - if 'error' in document_request: - self.error_message = (f"Error creating document from template with token {self.access_token}: " + - document_request['error']) + if 'error' in document_request: + self.error_message = (f"Error creating document from template with token {self.access_token}: " + + document_request['error']) if self.error_message: - return None + log.error(self.error_message) + return if uneditable_texts_list: response = put(signnow_sdk.Config().get_base_url() + '/document/' + @@ -1415,6 +1442,7 @@ def create_document(self, template_id, doc_title, folder_id='', uneditable_texts if 'errors' in edit_request: self.error_message = "Error setting up uneditable text fields: " + '; '.join( [e['message'] for e in edit_request['errors']]) + log.error(self.error_message) return None if fields: @@ -1430,6 +1458,7 @@ def create_document(self, template_id, doc_title, folder_id='', uneditable_texts if 'errors' in fields_request: self.error_message = "Error setting up fields: " + '; '.join( [e['message'] for e in fields_request['errors']]) + log.error(self.error_message) return None if folder_id: @@ -1438,13 +1467,14 @@ def create_document(self, template_id, doc_title, folder_id='', uneditable_texts folder_id) if 'error' in result: self.error_message = "Error moving document into folder: " + result['error'] + log.error(self.error_message) # Give the document request back anyway return document_request.get('id') def get_doc_signed_timestamp(self): - if not self.document: - self.error_message = "Tried to get a signed timestamp without a document attached to the request!" + if self.invalid_request("Tried to get a signed timestamp"): + log.error(self.error_message) return details = self.get_document_details() @@ -1452,11 +1482,8 @@ def get_doc_signed_timestamp(self): return details['signatures'][0].get('created') def create_dealer_signing_link(self): - if not self.group: - self.error_message = "Tried to send a dealer signing link without a group attached to the request!" - return - if not self.document: - self.error_message = "Tried to send a dealer signing link without a document attached to the request!" + if self.invalid_request("Tried to send a dealer signing link", check_group=True): + log.error(self.error_message) return first_name = self.group.leader.first_name if self.group.leader else '' @@ -1483,8 +1510,8 @@ def get_signing_link(self, first_name="", last_name="", redirect_uri=""): dict: A dictionary representing the JSON response containing the signing links for the document. """ - if not self.document: - self.error_message = "Tried to send a signing link without a document attached to the request!" + if self.invalid_request("Tried to send a signing link"): + log.error(self.error_message) return response = post(signnow_sdk.Config().get_base_url() + '/link', headers=self.api_call_headers, @@ -1498,13 +1525,15 @@ def get_signing_link(self, first_name="", last_name="", redirect_uri=""): if 'errors' in signing_request: self.error_message = "Error getting signing link: " + '; '.join( [e['message'] for e in signing_request['errors']]) + log.error(self.error_message) else: return signing_request.get('url_no_signup') def send_dealer_signing_invite(self): from uber.custom_tags import email_only - if not self.group: - self.error_message = "Tried to send a dealer signing invite without a group attached to the request!" + + if self.invalid_request("Tried to send a dealer signing invite", check_group=True): + log.error(self.error_message) return invite_payload = { @@ -1526,30 +1555,33 @@ def send_dealer_signing_invite(self): if 'error' in invite_request: self.error_message = "Error sending invite to sign: " + invite_request['error'] + log.error(self.error_message) else: return invite_request def get_download_link(self): - if not self.document: - self.error_message = "Tried to get a download link from a request without a document!" + if self.invalid_request("Tried to get a download link"): + log.error(self.error_message) return download_request = signnow_sdk.Document.download_link(self.access_token, self.document.document_id) if 'error' in download_request: self.error_message = "Error getting download link: " + download_request['error'] + log.error(self.error_message) else: return download_request.get('link') def get_document_details(self): - if not self.document: - self.error_message = "Tried to get document details from a request without a document!" + if self.invalid_request("Tried to get document details from a request"): + log.error(self.error_message) return document_request = signnow_sdk.Document.get(self.access_token, self.document.document_id) if 'error' in document_request: self.error_message = "Error getting document: " + document_request['error'] + log.error(self.error_message) else: return document_request From 2107914eabbca7ba4902dbe5ed04dacdaf0be386 Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Tue, 22 Oct 2024 21:59:24 -0400 Subject: [PATCH 07/19] Let declined dealer assistants buy badges If a group was declined, there was no easy way to get to dealer assistants' badges and even if you were on the assistant's page you couldn't purchase their badge separately. This fixes both those problems, plus a bug where the SignNow link would not generate for Shared groups. --- uber/site_sections/preregistration.py | 2 +- uber/templates/forms/group/table_info.html | 1 - uber/templates/preregistration/confirm.html | 21 ++++++++++--------- .../preregistration/group_members.html | 10 +++++++-- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/uber/site_sections/preregistration.py b/uber/site_sections/preregistration.py index 04cd1e295..be8dc79c7 100644 --- a/uber/site_sections/preregistration.py +++ b/uber/site_sections/preregistration.py @@ -1229,7 +1229,7 @@ def group_members(self, session, id, message='', **params): signnow_document = None signnow_link = '' - if group.is_dealer and c.SIGNNOW_DEALER_TEMPLATE_ID and group.is_valid and group.status == c.APPROVED: + if group.is_dealer and c.SIGNNOW_DEALER_TEMPLATE_ID and group.is_valid and group.status in [c.APPROVED, c.SHARED]: signnow_request = SignNowRequest(session=session, group=group, ident="terms_and_conditions", create_if_none=True) diff --git a/uber/templates/forms/group/table_info.html b/uber/templates/forms/group/table_info.html index 4143189e3..ac1504c2c 100644 --- a/uber/templates/forms/group/table_info.html +++ b/uber/templates/forms/group/table_info.html @@ -121,7 +121,6 @@ var shared_group_name = ''; var updateSharedGroupName = function () { shared_group_name = $("#sharedDealerForm [name='shared_group_name']").val(); - console.log(shared_group_name); } var sharedTable = function() { var form = $("#sharedDealerDiv").html(); diff --git a/uber/templates/preregistration/confirm.html b/uber/templates/preregistration/confirm.html index 07a918a09..e1aa50b2e 100644 --- a/uber/templates/preregistration/confirm.html +++ b/uber/templates/preregistration/confirm.html @@ -66,7 +66,7 @@ {% endif %} - {% if attendee.is_dealer and attendee.is_group_leader %} + {% if attendee.is_dealer %} {% if attendee.group.status == c.DECLINED %} {% set bg_class = 'danger' %} {% elif attendee.group.status in [c.APPROVED, c.SHARED] %} @@ -77,15 +77,16 @@ {% set bg_class = 'info' %} {% endif %} {% endif %} diff --git a/uber/templates/preregistration/group_members.html b/uber/templates/preregistration/group_members.html index 8dd0438d0..a2a37ce74 100644 --- a/uber/templates/preregistration/group_members.html +++ b/uber/templates/preregistration/group_members.html @@ -95,10 +95,10 @@

"{{ group.name }}" Information

{% endif %} {% endif%} -{% if group.status not in [c.CANCELLED, c.DECLINED] %}

Members of "{{ group.name }}"

+{% if group.status not in [c.CANCELLED, c.DECLINED] %} {% if group.amount_unpaid and not group.is_dealer %}
{{ stripe_form('process_group_payment',group) }} @@ -125,11 +125,15 @@

Members of "{{ group.name }}"

someone else. Upgraded badges may only be transferred directly between two people; please contact us at {{ c.REGDESK_EMAIL|email_only|email_to_link }} if you wish to transfer badges.
+{% elif group.attendees|length - group.floating|length != 1 %} +

Because your {{ c.DEALER_APP_TERM }} is {{ group.status_label }}, you cannot add or assign {{ c.DEALER_HELPER_TERM }}s, but you can view your assigned {{ c.DEALER_HELPER_TERM }}s below.

+{% endif %} -
Here are the badges for your group: +Here are the badges for your group: {% for attendee in group.sorted_attendees %} + {% if attendee.first_name or group.status not in [c.CANCELLED, c.DECLINED] %} {% if attendee.first_name %} {% endif %} + {% endif %} {% endfor %}
@@ -171,9 +175,11 @@

Members of "{{ group.name }}"

+{% if group.status not in [c.CANCELLED, c.DECLINED] %}
{% if group.is_dealer and group.min_badges_addable %} You may purchase up to {{ group.dealer_badges_remaining }} additional {{ c.DEALER_HELPER_TERM }} badges.

{% elif group.is_dealer and group.status != c.SHARED %} From f1b8989344b33e7879df2530d42b128c4b2c38af Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Wed, 23 Oct 2024 16:36:37 -0400 Subject: [PATCH 08/19] Set group members' status when group is paid Copies the logic for setting the group members' status to the payment code so that it happens when the group is marked paid, as the existing presave adjustments weren't working. Also fetches the SignNow token more frequently and handles the token expiry time getting unset. --- uber/payments.py | 7 ++++++- uber/tasks/redis.py | 3 ++- uber/utils.py | 8 +++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/uber/payments.py b/uber/payments.py index f183e1deb..8f30ec478 100644 --- a/uber/payments.py +++ b/uber/payments.py @@ -1553,7 +1553,12 @@ def mark_paid_from_ids(intent_id, charge_id): if model.paid in [c.NOT_PAID, c.PENDING]: model.paid = c.HAS_PAID if isinstance(model, Group) and model.is_paid: - model.paid = c.HAS_PAID + for attendee in model.attendees: + if attendee.paid == c.PAID_BY_GROUP and attendee.badge_status == c.NEW_STATUS and \ + not attendee.placeholder and \ + attendee.first_name: + attendee.badge_status = c.COMPLETED_STATUS + session.add(attendee) session.add(model) session.commit() diff --git a/uber/tasks/redis.py b/uber/tasks/redis.py index 9656fa741..e39d8baaf 100644 --- a/uber/tasks/redis.py +++ b/uber/tasks/redis.py @@ -33,7 +33,8 @@ def set_signnow_key(): return signnow_access_key = c.REDIS_STORE.get(c.REDIS_PREFIX + 'signnow_access_token') - if not signnow_access_key: + expired = c.REDIS_STORE.expiretime(c.REDIS_PREFIX + 'signnow_access_token') + if not signnow_access_key or expired == -1: signnow_secret = AWSSecretFetcher().get_signnow_secret() if not signnow_secret: log.error("Attempted to update our SignNow token but we didn't get a secret back from AWS!") diff --git a/uber/utils.py b/uber/utils.py index 0721dd4f4..49077e0e1 100644 --- a/uber/utils.py +++ b/uber/utils.py @@ -1373,6 +1373,8 @@ def api_call_headers(self): def set_access_token(self): from uber.tasks.redis import set_signnow_key + + set_signnow_key.delay() self.access_token = c.REDIS_STORE.get(c.REDIS_PREFIX + 'signnow_access_token') if self.access_token: @@ -1389,11 +1391,7 @@ def set_access_token(self): elif not c.AWS_SIGNNOW_SECRET_NAME: self.error_message = ("Couldn't get a SignNow access token because the secret name is not set. " "If you're on a development box, you can instead use a username and password.") - else: - set_signnow_key.delay() - self.access_token = c.REDIS_STORE.get(c.REDIS_PREFIX + 'signnow_access_token') - if not self.access_token: - self.error_message = "Couldn't set the SignNow key. Check the redis task for errors." + self.error_message = "Couldn't set the SignNow key. Check the redis task for errors." if self.error_message: log.error(self.error_message) From c449e0615ab4490aff2a21d6358278912995d30a Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Fri, 25 Oct 2024 16:18:25 -0400 Subject: [PATCH 09/19] Pin Redis to higher version EXPIRETIME is a 7.0.0 command -- I didn't catch this earlier because for unknown reasons my local setup is already at 7. This should hopefully fix staging, though. --- requirements.txt | 2 +- uber/tasks/redis.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4cefae7ca..9042e45ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,7 +24,7 @@ python-dateutil==2.9.0.post0 python3-saml==1.16.0 pytz==2024.1 pyyaml==6.0.1 -redis==5.0.3 +redis==7.0.0 requests==2.31.0 residue @ git+https://github.com/magfest/residue.git@3935b8cce36f85f9c8a7837c52a88ee096f24d3d rpctools @ git+https://github.com/appliedsec/rpctools.git@4e4108c3b7b4b6c482e515b1eeea2d79568cf065 diff --git a/uber/tasks/redis.py b/uber/tasks/redis.py index e39d8baaf..920e86416 100644 --- a/uber/tasks/redis.py +++ b/uber/tasks/redis.py @@ -34,7 +34,7 @@ def set_signnow_key(): signnow_access_key = c.REDIS_STORE.get(c.REDIS_PREFIX + 'signnow_access_token') expired = c.REDIS_STORE.expiretime(c.REDIS_PREFIX + 'signnow_access_token') - if not signnow_access_key or expired == -1: + if not signnow_access_key or expired < 0: signnow_secret = AWSSecretFetcher().get_signnow_secret() if not signnow_secret: log.error("Attempted to update our SignNow token but we didn't get a secret back from AWS!") From 8b28835ea51e639ede89a1ad63e0782869e61459 Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Fri, 25 Oct 2024 16:21:48 -0400 Subject: [PATCH 10/19] Wrong redis I changed the redis-py package, which is confusing because it's just called redis --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9042e45ec..4cefae7ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,7 +24,7 @@ python-dateutil==2.9.0.post0 python3-saml==1.16.0 pytz==2024.1 pyyaml==6.0.1 -redis==7.0.0 +redis==5.0.3 requests==2.31.0 residue @ git+https://github.com/magfest/residue.git@3935b8cce36f85f9c8a7837c52a88ee096f24d3d rpctools @ git+https://github.com/appliedsec/rpctools.git@4e4108c3b7b4b6c482e515b1eeea2d79568cf065 From 26d4a801cd6705365f9df6818bd3f6eeda4aea4e Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Sun, 27 Oct 2024 10:46:14 -0400 Subject: [PATCH 11/19] Remove hard-coded logging --- uber/config.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/uber/config.py b/uber/config.py index d280ca799..d3368fa8d 100644 --- a/uber/config.py +++ b/uber/config.py @@ -1864,33 +1864,3 @@ def _make_room_trie(rooms): c.SAML_SETTINGS["debug"] = True else: c.SAML_SETTINGS["strict"] = True - -logging.config.dictConfig({ - 'version': 1, - 'root': { - 'handlers': ['default'], - 'level': "INFO", - 'propagate': False - }, - 'loggers': { - name: { - 'handlers': ['default'], - 'level': level, - 'propagate': False - } - for name, level in _config['loggers'].items() if name != 'root' - }, - 'handlers': { - 'default': { - 'level': 'INFO', - 'formatter': 'standard', - 'class': 'logging.StreamHandler', - 'stream': 'ext://sys.stdout' - } - }, - 'formatters': { - 'standard': { - 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' - } - } -}) From 194c3627f0405410e48912c09cda384be747a7cc Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Mon, 28 Oct 2024 17:38:08 -0400 Subject: [PATCH 12/19] Only allow valid badges to pay for marketplace app Detects if someone has an invalid badge status, with a special case for unapproved dealers. Also fixes a few bugs in the SignNow link page handler and moves the Terms and Conditions button to the bottom of the dealer page more reliably. --- uber/models/marketplace.py | 19 ++++++++++++++++++- uber/site_sections/dealer_admin.py | 6 +++--- uber/templates/forms/group.html | 4 ++-- uber/templates/marketplace/edit.html | 2 +- .../preregistration/group_members.html | 2 ++ 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/uber/models/marketplace.py b/uber/models/marketplace.py index 1af8e7a16..fcfd60e37 100644 --- a/uber/models/marketplace.py +++ b/uber/models/marketplace.py @@ -1,9 +1,11 @@ from uber.config import c +from uber.custom_tags import email_only, email_to_link from uber.models import MagModel from uber.decorators import presave_adjustment from uber.models.types import Choice, DefaultColumn as Column, default_relationship as relationship, MultiChoice, utcnow from datetime import datetime +from markupsafe import Markup from pytz import UTC from residue import CoerceUTF8 as UnicodeText, UTCDateTime, UUID from sqlalchemy.orm import backref @@ -84,4 +86,19 @@ def amount_paid(self): if self.receipt_items: return sum([item.amount for item in self.receipt_items if item.closed and ( not item.receipt_txn or not item.receipt_txn.refunded)]) - return 0 \ No newline at end of file + return 0 + + @property + def incomplete_reason(self): + if self.attendee.badge_status == c.UNAPPROVED_DEALER_STATUS: + if self.attendee.group.status == c.UNAPPROVED: + return Markup(f"Your registration is still pending as part of your {self.attendee.group.status_label} " + f"{c.DEALER_APP_TERM}. Please contact us at {email_to_link(email_only(c.MARKETPLACE_EMAIL))}.") + return Markup(f"Your registration is still pending as part of your {self.attendee.group.status_label} " + f"{c.DEALER_APP_TERM}. Please purchase your badge here and return to this page to complete your " + "artist marketplace application.") + elif not self.attendee.has_badge: + return Markup("You cannot complete your marketplace application because your badge status is " + f"{self.attendee.badge_status_label}. Please contact us at {email_to_link(email_only(c.REGDESK_EMAIL))} " + "for more information.") \ No newline at end of file diff --git a/uber/site_sections/dealer_admin.py b/uber/site_sections/dealer_admin.py index b8c5b0c32..addbd2bc1 100644 --- a/uber/site_sections/dealer_admin.py +++ b/uber/site_sections/dealer_admin.py @@ -226,16 +226,16 @@ def resend_signnow_link(self, session, id): signnow_request = SignNowRequest(session=session, group=group) if not signnow_request.document: - raise HTTPRedirect("form?id={}&message={}").format(id, "SignNow document not found.") + raise HTTPRedirect("../group_admin/form?id={}&message={}".format(id, "SignNow document not found.")) signnow_request.send_dealer_signing_invite() if signnow_request.error_message: - raise HTTPRedirect("form?id={}&message={}", id, + raise HTTPRedirect("../group_admin/form?id={}&message={}", id, f"Error sending SignNow link: {signnow_request.error_message}") else: signnow_request.document.last_emailed = datetime.now(UTC) session.add(signnow_request.document) - raise HTTPRedirect("form?id={}&message={}", id, "SignNow link sent!") + raise HTTPRedirect("../group_admin/form?id={}&message={}", id, "SignNow link sent!") @ajax def set_table_shared(self, session, id, shared_group_name, **params): diff --git a/uber/templates/forms/group.html b/uber/templates/forms/group.html index 8c7bfa4ca..5b7de295b 100644 --- a/uber/templates/forms/group.html +++ b/uber/templates/forms/group.html @@ -13,7 +13,7 @@ {% elif signnow_link %} @@ -22,7 +22,7 @@

Review and Sign

-

Make sure to click "Finish" in the top right to confirm your signature.

+

Make sure to click "Finish" at the bottom of the page to confirm your signature.

{% endif %} {% endif %} diff --git a/uber/templates/marketplace/edit.html b/uber/templates/marketplace/edit.html index e222817ab..acca03d1f 100644 --- a/uber/templates/marketplace/edit.html +++ b/uber/templates/marketplace/edit.html @@ -54,7 +54,7 @@

{{ c.EVENT_NAME }} Artist Marketplace Application

{{ stripe_form('process_marketplace_payment', app) }} {% endif %} - {% endif %} + {% else %}

{{ app.incomplete_reason }}

{% endif %} {% elif app.status in [c.DECLINED, c.CANCELLED] %} Unfortunately, since your application has been {{ app.status_label|lower }}, you may no longer edit it. However, you may still view the details of your application below. diff --git a/uber/templates/preregistration/group_members.html b/uber/templates/preregistration/group_members.html index a2a37ce74..02bd87f6e 100644 --- a/uber/templates/preregistration/group_members.html +++ b/uber/templates/preregistration/group_members.html @@ -82,11 +82,13 @@

"{{ group.name }}" Information

{% endif %} {% include "groupextra.html" %} {% if group.status in c.DEALER_EDITABLE_STATUSES %} +
{% endif %} {% if group.status in c.DEALER_CANCELLABLE_STATUSES %} {% endif %} +
{% if group.status in c.DEALER_CANCELLABLE_STATUSES %}
From a9538bba80c0b875fc69eba2d3cb9d9d7a7fe591 Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Thu, 31 Oct 2024 12:32:35 -0400 Subject: [PATCH 13/19] Update art show mail-in dates Fixes https://jira.furfest.org/projects/SEI_SD/queues/custom/66/SEI_SD-1816. --- uber/templates/emails/art_show/mailing_in.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uber/templates/emails/art_show/mailing_in.html b/uber/templates/emails/art_show/mailing_in.html index 70b873b0e..ad4d71cab 100644 --- a/uber/templates/emails/art_show/mailing_in.html +++ b/uber/templates/emails/art_show/mailing_in.html @@ -6,7 +6,7 @@ }}/art_show_applications/edit?id={{ app.id }}" target="_blank">your application.

**If you have not paid for your space in the show, please do so -now. Any unpaid reservations will be deleted on November 1st, and you +now. Any unpaid reservations will be deleted on November 8th, and you will lose your space to the waitlist.

  1. Enter your mailing address.
  2. @@ -28,7 +28,7 @@ All art must be shipped via trackable means both ways. DO NOT SEND SIGNATURE REQUIRED. -
    Deadline to receive mail-in packages is Wednesday, November 22nd. +
    Deadline to receive mail-in packages is Monday, November 25th. From 5a03bc1e1a3a11ca038910887fc99989cad113a0 Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Thu, 31 Oct 2024 13:43:10 -0400 Subject: [PATCH 14/19] Allow lottery applications to be attendee-less This will fix the 500 error when unassigning group badges that have a lottery application. Also upgrades our account required decorator so that it can check multiple models. --- ...4d_allow_hotel_lottery_applications_to_.py | 65 +++++++++++++++++++ uber/decorators.py | 42 ++++++++---- uber/models/__init__.py | 2 +- uber/models/hotel.py | 6 +- uber/site_sections/hotel_lottery.py | 28 ++++---- uber/site_sections/hotel_lottery_admin.py | 4 +- uber/templates/hotel_lottery_admin/form.html | 12 ++-- .../hotel_lottery_admin/history.html | 6 +- uber/templates/hotel_lottery_admin/index.html | 2 +- 9 files changed, 127 insertions(+), 40 deletions(-) create mode 100644 alembic/versions/58756e2dfe4d_allow_hotel_lottery_applications_to_.py diff --git a/alembic/versions/58756e2dfe4d_allow_hotel_lottery_applications_to_.py b/alembic/versions/58756e2dfe4d_allow_hotel_lottery_applications_to_.py new file mode 100644 index 000000000..881513b53 --- /dev/null +++ b/alembic/versions/58756e2dfe4d_allow_hotel_lottery_applications_to_.py @@ -0,0 +1,65 @@ +"""Allow hotel lottery applications to have no attendee + +Revision ID: 58756e2dfe4d +Revises: 128e7228f182 +Create Date: 2024-10-31 17:36:53.320109 + +""" + + +# revision identifiers, used by Alembic. +revision = '58756e2dfe4d' +down_revision = '128e7228f182' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + + +try: + is_sqlite = op.get_context().dialect.name == 'sqlite' +except Exception: + is_sqlite = False + +if is_sqlite: + op.get_context().connection.execute('PRAGMA foreign_keys=ON;') + utcnow_server_default = "(datetime('now', 'utc'))" +else: + utcnow_server_default = "timezone('utc', current_timestamp)" + +def sqlite_column_reflect_listener(inspector, table, column_info): + """Adds parenthesis around SQLite datetime defaults for utcnow.""" + if column_info['default'] == "datetime('now', 'utc')": + column_info['default'] = utcnow_server_default + +sqlite_reflect_kwargs = { + 'listeners': [('column_reflect', sqlite_column_reflect_listener)] +} + +# =========================================================================== +# HOWTO: Handle alter statements in SQLite +# +# def upgrade(): +# if is_sqlite: +# with op.batch_alter_table('table_name', reflect_kwargs=sqlite_reflect_kwargs) as batch_op: +# batch_op.alter_column('column_name', type_=sa.Unicode(), server_default='', nullable=False) +# else: +# op.alter_column('table_name', 'column_name', type_=sa.Unicode(), server_default='', nullable=False) +# +# =========================================================================== + + +def upgrade(): + op.alter_column('lottery_application', 'attendee_id', + existing_type=postgresql.UUID(), + nullable=True) + op.create_unique_constraint(op.f('uq_lottery_application_attendee_id'), 'lottery_application', ['attendee_id']) + + +def downgrade(): + op.drop_constraint(op.f('uq_lottery_application_attendee_id'), 'lottery_application', type_='unique') + op.alter_column('lottery_application', 'attendee_id', + existing_type=postgresql.UUID(), + nullable=False) diff --git a/uber/decorators.py b/uber/decorators.py index c742679c4..61027c501 100644 --- a/uber/decorators.py +++ b/uber/decorators.py @@ -159,7 +159,7 @@ def check_dept_admin(session, department_id=None, inherent_role=None): return check_can_edit_dept(session, department_id, inherent_role, override_access='full_dept_admin') -def requires_account(model=None): +def requires_account(models=None): from uber.models import Attendee, AttendeeAccount, Group def model_requires_account(func): @@ -171,7 +171,7 @@ def protected(*args, **kwargs): admin_account_id = cherrypy.session.get('account_id') attendee_account_id = cherrypy.session.get('attendee_account_id') message = '' - if not model and not attendee_account_id and c.PAGE_PATH != '/preregistration/homepage': + if not models and not attendee_account_id and c.PAGE_PATH != '/preregistration/homepage': # These should all be pages like the prereg form if c.PAGE_PATH in ['/preregistration/form', '/preregistration/post_form']: message_add = 'register' @@ -182,16 +182,28 @@ def protected(*args, **kwargs): elif attendee_account_id is None and admin_account_id is None or \ attendee_account_id is None and c.PAGE_PATH == '/preregistration/homepage': message = 'You must log in to view this page.' - elif kwargs.get('id') and model: - if model == Attendee: - check_id_for_model(model, alt_id='attendee_id', **kwargs) - attendee = session.attendee(kwargs.get('attendee_id', kwargs.get('id')), allow_invalid=True) - elif model == Group: - check_id_for_model(model, alt_id='group_id', **kwargs) - attendee = session.query(model).filter_by(id=kwargs.get('group_id', - kwargs.get('id'))).first().leader - else: - attendee = session.query(model).filter_by(id=kwargs.get('id')).first().attendee + elif kwargs.get('id') and models: + model_list = [models] if not isinstance(models, list) else models + attendee, error, model_id = None, None, None + for model in model_list: + if model == Attendee: + error, model_id = check_id_for_model(model, alt_id='attendee_id', **kwargs) + if not error: + attendee = session.attendee(model_id, allow_invalid=True) + elif model == Group: + error, model_id = check_id_for_model(model, alt_id='group_id', **kwargs) + if not error: + attendee = session.query(model).filter_by(id=model_id).first().leader + else: + other_model = session.query(model).filter_by(id=kwargs.get('id')).first() + if other_model: + attendee = other_model.attendee + + if attendee: + break + + if error and not attendee: + raise HTTPRedirect('../preregistration/not_found?id={}&message={}', model_id, error) # Admin account override if session.admin_attendee_max_access(attendee): @@ -901,7 +913,9 @@ def id_required(model): def model_id_required(func): @wraps(func) def check_id(*args, **params): - check_id_for_model(model=model, **params) + error, model_id = check_id_for_model(model=model, **params) + if error: + raise HTTPRedirect('../preregistration/not_found?id={}&message={}', model_id, error) return func(*args, **params) return check_id return model_id_required @@ -932,4 +946,4 @@ def check_id_for_model(model, alt_id=None, **params): if message: log.error("check_id {} error: {}: id={}".format(model.__name__, message, model_id)) - raise HTTPRedirect('../preregistration/not_found?id={}&message={}', model_id, message) + return message, model_id diff --git a/uber/models/__init__.py b/uber/models/__init__.py index 9a42d5183..cffb4957e 100644 --- a/uber/models/__init__.py +++ b/uber/models/__init__.py @@ -766,7 +766,7 @@ def current_attendee_account(self): def get_attendee_account_by_attendee(self, attendee): logged_in_account = self.current_attendee_account() - if not logged_in_account: + if not logged_in_account or not attendee: return attendee_accounts = attendee.managers diff --git a/uber/models/hotel.py b/uber/models/hotel.py index 6428f5f50..bca9cd0e0 100644 --- a/uber/models/hotel.py +++ b/uber/models/hotel.py @@ -119,7 +119,7 @@ class RoomAssignment(MagModel): class LotteryApplication(MagModel): - attendee_id = Column(UUID, ForeignKey('attendee.id')) + attendee_id = Column(UUID, ForeignKey('attendee.id'), unique=True, nullable=True) attendee = relationship('Attendee', backref=backref('lottery_application', uselist=False), cascade='save-update,merge,refresh-expire,expunge', uselist=False) @@ -233,6 +233,10 @@ def group_leader_name(self): def email(self): if self.attendee: return self.attendee.email + + @property + def attendee_name(self): + return self.attendee.full_name if self.attendee else "[DISASSOCIATED]" @property def current_status_str(self): diff --git a/uber/site_sections/hotel_lottery.py b/uber/site_sections/hotel_lottery.py index 19b24f4dc..9f47fd7ca 100644 --- a/uber/site_sections/hotel_lottery.py +++ b/uber/site_sections/hotel_lottery.py @@ -104,7 +104,7 @@ def terms(self, session, attendee_id, message="", **params): 'attendee': attendee, } - @requires_account(Attendee) + @requires_account([Attendee, LotteryApplication]) def index(self, session, attendee_id=None, message="", **params): if 'id' in params: application = session.lottery_application(params['id']) @@ -146,8 +146,8 @@ def enter_attendee_lottery(self, session, id=None, **params): application.status = c.COMPLETE application.confirmation_num = '' session.add(application) - raise HTTPRedirect('index?attendee_id={}&message={}', - application.attendee.id, + raise HTTPRedirect('index?id={}&message={}', + application.id, "Your staff lottery entry has been entered into the attendee lottery.") @requires_account(LotteryApplication) @@ -204,7 +204,7 @@ def room_lottery(self, session, id=None, message="", **params): if application.parent_application: message = "You cannot edit your room group's application." - raise HTTPRedirect(f'index?attendee_id={application.attendee.id}&messsage={message}') + raise HTTPRedirect(f'index?id={application.id}&messsage={message}') forms = load_forms(params, application, forms_list) @@ -249,8 +249,8 @@ def room_lottery(self, session, id=None, message="", **params): format='html', model=application.to_dict('id')) - raise HTTPRedirect('index?attendee_id={}&confirm=room&action=updated', - application.attendee.id) + raise HTTPRedirect('index?id={}&confirm=room&action=updated', + application.id) return { 'id': application.id, @@ -267,7 +267,7 @@ def suite_lottery(self, session, id=None, message="", **params): if application.parent_application: message = "You cannot edit your room group's application." - raise HTTPRedirect(f'index?attendee_id={application.attendee.id}&messsage={message}') + raise HTTPRedirect(f'index?id={application.id}&messsage={message}') forms = load_forms(params, application, forms_list) @@ -313,8 +313,8 @@ def suite_lottery(self, session, id=None, message="", **params): format='html', model=application.to_dict('id')) - raise HTTPRedirect('index?attendee_id={}&confirm=suite&action=updated', - application.attendee.id) + raise HTTPRedirect('index?id={}&confirm=suite&action=updated', + application.id) return { 'id': application.id, @@ -398,8 +398,8 @@ def guarantee_confirm(self, session, id=None, message="", **params): format='html', model=application.to_dict('id')) - raise HTTPRedirect('index?attendee_id={}&confirm={}&action=confirmation', - application.attendee.id, + raise HTTPRedirect('index?id={}&confirm={}&action=confirmation', + application.id, room_or_suite) return { 'id': application.id, @@ -484,7 +484,7 @@ def delete_group(self, session, id=None, message="", **params): application.confirmation_num = '' - raise HTTPRedirect('index?attendee_id={}&message={}', application.attendee.id, + raise HTTPRedirect('index?id={}&message={}', application.id, f"{old_room_group_name} has been disbanded.") @ajax @@ -615,8 +615,8 @@ def leave_group(self, session, id=None, message="", **params): if application.status == c.WITHDRAWN: raise HTTPRedirect('../preregistration/homepage?message={}', f'You have left the room group "{room_group.room_group_name}" and been removed from the hotel lottery.') - raise HTTPRedirect('index?attendee_id={}&message={}&confirm={}&action={}', - application.attendee.id, + raise HTTPRedirect('index?id={}&message={}&confirm={}&action={}', + application.id, f'Successfully left the room group "{room_group.room_group_name}".', "suite" if application.entry_type == c.SUITE_ENTRY else "room", 're-entered') diff --git a/uber/site_sections/hotel_lottery_admin.py b/uber/site_sections/hotel_lottery_admin.py index 555ccc529..2a4e95dc6 100644 --- a/uber/site_sections/hotel_lottery_admin.py +++ b/uber/site_sections/hotel_lottery_admin.py @@ -144,7 +144,7 @@ def form(self, session, message='', return_to='', **params): for form in forms.values(): form.populate_obj(application, is_admin=True) - message = '{}\'s entry (conf # {}) has been saved.'.format(application.attendee.full_name, + message = '{}\'s entry (conf # {}) has been saved.'.format(application.attendee_name, application.confirmation_num) stay_on_form = params.get('save_return_to_search', False) is False session.add(application) @@ -277,7 +277,7 @@ def print_bool(bool): # Entry data if app.parent_application: - row.extend([app.parent_application.confirmation_num, app.parent_application.attendee.email, + row.extend([app.parent_application.confirmation_num, app.parent_application.email, '', '', '', '', '', '', '', '']) else: row.extend(['', '', print_bool(app.entry_form_completed)]) diff --git a/uber/templates/hotel_lottery_admin/form.html b/uber/templates/hotel_lottery_admin/form.html index f858f98e0..4e75d6d5e 100644 --- a/uber/templates/hotel_lottery_admin/form.html +++ b/uber/templates/hotel_lottery_admin/form.html @@ -1,7 +1,7 @@ {% extends "base.html" %}{% set admin_area=True %} {% import "forms/macros.html" as form_macros with context %} {% set lottery_admin_info = lottery_admin_info or forms['lottery_admin_info'] %} -{% block title %}Lottery Application{% if id != "None" %} for {{ application.attendee.full_name }}{% endif %}{% endblock %} +{% block title %}Lottery Application{% if id != "None" and application.attendee %} for {{ application.attendee_name }}{% endif %}{% endblock %} {% block content %} {{ macros.nav_menu( @@ -40,12 +40,12 @@ } -{% if not application.is_new %} +{% if not application.is_new and application.attendee %} {% set edit_link = "suite_lottery" if application.entry_type == c.SUITE_ENTRY else "room_lottery" %}
    {% if application.parent_application %} - View as Attendee + View as Attendee {% else %} View as Attendee {% endif %} @@ -65,7 +65,11 @@
    + {% if application.attendee %} This lottery application is for {{ application.attendee|form_link }}. + {% else %} + This lottery application is not associated with any attendee. + {% endif %}
    @@ -116,7 +120,7 @@ {% if application.parent_application %}
    - This application is a group member of {{ application.parent_application.attendee.full_name }}'s entry (conf # {{ application.parent_application.confirmation_num }}). + This application is a group member of {{ application.parent_application.attendee_name }}'s entry (conf # {{ application.parent_application.confirmation_num }}). The details below are INACTIVE and will not be included in the lottery export. They will become active again if this attendee leaves the group, at which point a new confirmation number will be generated.
    {% else %} diff --git a/uber/templates/hotel_lottery_admin/history.html b/uber/templates/hotel_lottery_admin/history.html index 2c99fa637..c0b5c8681 100644 --- a/uber/templates/hotel_lottery_admin/history.html +++ b/uber/templates/hotel_lottery_admin/history.html @@ -1,6 +1,6 @@ {% extends "base.html" %}{% set admin_area=True %} {% import 'macros.html' as macros with context %} -{% block title %}Lottery Application History for {{ application.attendee.full_name }}{% endblock %} +{% block title %}Lottery Application History for {{ application.attendee_name }}{% endblock %} {% block content %} {{ macros.nav_menu( application, c.PAGE_PATH, @@ -10,7 +10,7 @@ }}
    -

    Changelog for {{ application.attendee.full_name }}'s Lottery Application

    +

    Changelog for {{ application.attendee_name }}'s Lottery Application

    @@ -31,7 +31,7 @@

    Changelog for {{ application.attendee.full_name }}'s Lottery Application

    -

    Page View History for {{ application.attendee.full_name }}'s Lottery Application

    +

    Page View History for {{ application.attendee_name }}'s Lottery Application

    diff --git a/uber/templates/hotel_lottery_admin/index.html b/uber/templates/hotel_lottery_admin/index.html index 4340ee0b0..3def30ee9 100644 --- a/uber/templates/hotel_lottery_admin/index.html +++ b/uber/templates/hotel_lottery_admin/index.html @@ -104,7 +104,7 @@ - + From d25a473eb522c7569c5a59c3cf56836d516731bd Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Thu, 31 Oct 2024 13:46:14 -0400 Subject: [PATCH 15/19] Fix artist check-in/out form Turns out we were just missing a template change. --- uber/models/art_show.py | 2 +- uber/templates/art_show_common/artist_checkin_macros.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uber/models/art_show.py b/uber/models/art_show.py index c1d63ef5f..f81a5caad 100644 --- a/uber/models/art_show.py +++ b/uber/models/art_show.py @@ -165,7 +165,7 @@ def valid_agent_codes(self): @property def current_agents(self): return [code.attendee for code in self.valid_agent_codes if code.attendee is not None] - + @property def single_agent(self): return self.current_agents[0] if self.current_agents else None diff --git a/uber/templates/art_show_common/artist_checkin_macros.html b/uber/templates/art_show_common/artist_checkin_macros.html index fb1cd4ad2..859f5100d 100644 --- a/uber/templates/art_show_common/artist_checkin_macros.html +++ b/uber/templates/art_show_common/artist_checkin_macros.html @@ -165,7 +165,7 @@ - + {% endfor %}
    {{ app.entry_type_label }} {{ app.confirmation_num }} {{ app.room_group_name }}{{ app.attendee|form_link }}{{ app.attendee|form_link if app.attendee else 'Dissociated/No Attendee' }} {{ app.group_members|length if app.room_group_name else "" }} {{ app.admin_notes }} {{ app.is_staff_entry|yesno("Yes,No") }}{{ app.website|url_to_link(target="_blank", is_relative=False) }} {{ app.tax_number }} {{ app.admin_notes }}{{ app.amount_paid|format_currency }}{{ (app.amount_paid / 100)|format_currency }}