Skip to content

Commit

Permalink
Merge branch 'art-show-launch-2023' into fix-minor-badge-validations
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsuta committed Sep 25, 2023
2 parents c37335c + 989a63e commit f34de14
Show file tree
Hide file tree
Showing 25 changed files with 230 additions and 93 deletions.
21 changes: 10 additions & 11 deletions uber/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from uber.errors import HTTPRedirect
from uber.decorators import cost_property, department_id_adapter, presave_adjustment, suffix_property
from uber.models.types import Choice, DefaultColumn as Column, MultiChoice
from uber.utils import check_csrf, normalize_email, normalize_phone, DeptChecklistConf, report_critical_exception, \
from uber.utils import check_csrf, normalize_email_legacy, normalize_phone, DeptChecklistConf, report_critical_exception, \
valid_email, valid_password
from uber.payments import ReceiptManager

Expand Down Expand Up @@ -952,7 +952,7 @@ def guess_watchentry_attendees(self, entry):
Attendee.watchlist_id == None).all()

def get_attendee_account_by_email(self, email):
return self.query(AttendeeAccount).filter_by(email=normalize_email(email)).one()
return self.query(AttendeeAccount).filter_by(normalized_email=normalize_email_legacy(email)).one()

def get_admin_account_by_email(self, email):
from uber.utils import normalize_email_legacy
Expand All @@ -967,7 +967,7 @@ def lookup_attendee(self, first_name, last_name, email, zip_code):
last_name=last_name,
zip_code=zip_code
).filter(
Attendee.normalized_email == normalize_email(email),
Attendee.normalized_email == normalize_email_legacy(email),
Attendee.is_valid == True
).limit(10).all()

Expand Down Expand Up @@ -1041,12 +1041,8 @@ def create_admin_account(self, attendee, password='', generate_pwd=True, **param

def create_attendee_account(self, email=None, normalized_email=None, password=None):
from uber.models import Attendee, AttendeeAccount
from uber.utils import normalize_email_legacy

if email:
normalized_email = uber.utils.normalize_email(email)

new_account = AttendeeAccount(email=normalized_email, hashed=bcrypt.hashpw(password, bcrypt.gensalt()) if password else '')
new_account = AttendeeAccount(email=email, hashed=bcrypt.hashpw(password, bcrypt.gensalt()) if password else '')
self.add(new_account)

return new_account
Expand All @@ -1058,7 +1054,7 @@ def add_attendee_to_account(self, attendee, account):
account.attendees.append(attendee)

def match_attendee_to_account(self, attendee):
existing_account = self.query(AttendeeAccount).filter_by(email=normalize_email(attendee.email)).first()
existing_account = self.query(AttendeeAccount).filter_by(normalized_email=normalize_email_legacy(attendee.email)).first()
if existing_account:
self.add_attendee_to_account(attendee, existing_account)

Expand Down Expand Up @@ -1607,6 +1603,7 @@ def search(self, text, *filters):
attendees = self.query(Attendee) \
.outerjoin(Attendee.group) \
.outerjoin(Attendee.promo_code) \
.outerjoin(Attendee.managers) \
.outerjoin(aliased_pcg, PromoCode.group) \
.options(
joinedload(Attendee.group),
Expand Down Expand Up @@ -1685,9 +1682,11 @@ def check_text_fields(search_text):
target, term, search_term, op = self.parse_attr_search_terms(search_text)

if target == 'email':
attr_search_filter = Attendee.normalized_email.icontains(normalize_email(search_term))
attr_search_filter = Attendee.normalized_email.contains(normalize_email_legacy(search_term))
elif target == 'account_email':
attr_search_filter = AttendeeAccount.normalized_email.contains(normalize_email_legacy(search_term))
elif target == 'group':
attr_search_filter = Group.name.icontains(search_term.strip())
attr_search_filter = Group.normalized_name.contains(search_term.strip().lower())
elif target == 'has_ribbon':
attr_search_filter = Attendee.ribbon == Attendee.ribbon.type.convert_if_labels(search_term.title())
elif target in Attendee.searchable_bools:
Expand Down
10 changes: 9 additions & 1 deletion uber/models/attendee.py
Original file line number Diff line number Diff line change
Expand Up @@ -2158,7 +2158,7 @@ class AttendeeAccount(MagModel):
hashed = Column(UnicodeText, private=True)
password_reset = relationship('PasswordReset', backref='attendee_account', uselist=False)
attendees = relationship(
'Attendee', backref='managers', cascade='save-update,merge,refresh-expire,expunge',
'Attendee', backref='managers', order_by='Attendee.registered', cascade='save-update,merge,refresh-expire,expunge',
secondary='attendee_attendee_account')
imported = Column(Boolean, default=False)

Expand All @@ -2172,6 +2172,14 @@ def strip_email(self):
def normalize_email(self):
self.email = normalize_email(self.email)

@hybrid_property
def normalized_email(self):
return normalize_email_legacy(self.email)

@normalized_email.expression
def normalized_email(cls):
return func.replace(func.lower(func.trim(cls.email)), '.', '')

@property
def has_only_one_badge(self):
return len(self.attendees) == 1
Expand Down
2 changes: 1 addition & 1 deletion uber/models/commerce.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ def available_actions(self):

@property
def refundable(self):
return self.receipt_txn.refundable and not self.comped and not self.reverted
return self.receipt_txn.refundable and not self.comped and not self.reverted and self.amount > 0

@property
def cannot_delete_reason(self):
Expand Down
8 changes: 8 additions & 0 deletions uber/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ def floating(self):
badges.
"""
return [a for a in self.attendees if a.is_unassigned and a.paid == c.PAID_BY_GROUP]

@hybrid_property
def normalized_name(self):
return self.name.strip().lower()

@normalized_name.expression
def normalized_name(cls):
return func.lower(func.trim(cls.name))

@hybrid_property
def is_valid(self):
Expand Down
5 changes: 5 additions & 0 deletions uber/payments.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ def __init__(self, receipt=None, receipt_email='', description='', amount=0, cus
self.receipt_email = receipt_email
self.description = description
self.customer_id = customer_id
self.refund_str = "refunded" # Set to "voided" when applicable to better inform admins
self.intent, self.response, self.receipt_manager = None, None, None
self.tracking_id = str(uuid4())

Expand Down Expand Up @@ -371,6 +372,7 @@ def stripe_or_authnet_refund(self, txn, amount):
if self.response.transactionStatus == "capturedPendingSettlement":
if amount != int(self.response.authAmount * 100):
return "This transaction cannot be partially refunded until it's settled."
self.refund_str = "voided"
error = self.send_authorizenet_txn(txn_type=c.VOID, txn_id=txn.charge_id)
elif self.response.transactionStatus != "settledSuccessfully":
return "This transaction cannot be refunded because of an invalid status: {}.".format(self.response.transactionStatus)
Expand Down Expand Up @@ -641,6 +643,7 @@ def get_authorizenet_txn(self, txn_id):

def send_authorizenet_txn(self, txn_type=c.AUTHCAPTURE, **params):
from decimal import Decimal

payment_profile = None
order = None

Expand Down Expand Up @@ -695,6 +698,7 @@ def send_authorizenet_txn(self, txn_type=c.AUTHCAPTURE, **params):
transaction.order = order

transaction.transactionType = c.AUTHNET_TXN_TYPES[txn_type]
transaction.customerIP = cherrypy.request.remote.ip

if self.amount:
transaction.amount = Decimal(int(self.amount) / 100)
Expand Down Expand Up @@ -815,6 +819,7 @@ def create_new_receipt(cls, model, create_model=False, items=None):
for calculation in i[model.__class__.__name__].values():
item = calculation(model)
if item:
log.debug(item)
try:
desc, cost, col_name, count = item
except ValueError:
Expand Down
10 changes: 5 additions & 5 deletions uber/receipt_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@ def overridden_app_cost(app):

@cost_calculation.ArtShowApplication
def panel_cost(app):
return ("General Panel", c.COST_PER_PANEL * 100, app.panels, None) if app.panels else None
return ("General Panel", c.COST_PER_PANEL * 100, 'panels', app.panels) if app.panels else None

@cost_calculation.ArtShowApplication
def table_cost(app):
return ("General Table", c.COST_PER_TABLE * 100, app.tables, None) if app.tables else None
return ("General Table", c.COST_PER_TABLE * 100, 'tables', app.tables) if app.tables else None

@cost_calculation.ArtShowApplication
def mature_panel_cost(app):
return ("Mature Panel", c.COST_PER_PANEL * 100, app.panels_ad, None) if app.panels_ad else None
return ("Mature Panel", c.COST_PER_PANEL * 100, 'panels_ad', app.panels_ad) if app.panels_ad else None

@cost_calculation.ArtShowApplication
def mature_table_cost(app):
return ("Mature Table", c.COST_PER_TABLE * 100, app.tables_ad, None) if app.tables_ad else None
return ("Mature Table", c.COST_PER_TABLE * 100, 'tables_ad', app.tables_ad) if app.tables_ad else None

@cost_calculation.ArtShowApplication
def mailing_fee_cost(app):
Expand Down Expand Up @@ -151,7 +151,7 @@ def badge_cost(group):
@cost_calculation.Group
def set_cost(group):
if not group.auto_recalc:
return ("Custom fee for group {}".format(group.name), group.cost * 100, None)
return ("Custom fee for group {}".format(group.name), group.cost * 100, 'cost')


@cost_calculation.Attendee
Expand Down
5 changes: 4 additions & 1 deletion uber/site_sections/art_show_applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,10 @@ def confirm_pieces(self, session, id, **params):
'Confirmation email sent')

def confirmation(self, session, id):
return {'app': session.art_show_application(id)}
return {
'app': session.art_show_application(id),
'logged_in_account': session.current_attendee_account(),
}

def mailing_address(self, session, message='', **params):
app = session.art_show_application(params)
Expand Down
34 changes: 23 additions & 11 deletions uber/site_sections/preregistration.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from uber.models import Attendee, AttendeeAccount, Attraction, Email, Group, ModelReceipt, PromoCode, PromoCodeGroup, \
ReceiptTransaction, SignedDocument, Tracking
from uber.tasks.email import send_email
from uber.utils import add_opt, check, check_pii_consent, localized_now, normalize_email, genpasswd, valid_email, \
from uber.utils import add_opt, check, check_pii_consent, localized_now, normalize_email, normalize_email_legacy, genpasswd, valid_email, \
valid_password, SignNowDocument, validate_model
from uber.payments import PreregCart, TransactionRequest, ReceiptManager

Expand Down Expand Up @@ -109,12 +109,15 @@ def check_account(session, email, password, confirm_password, skip_if_logged_in=

if email and valid_email(email):
return valid_email(email)

super_normalized_old_email = normalize_email_legacy(normalize_email(old_email))

existing_account = session.query(AttendeeAccount).filter_by(email=normalize_email(email)).first()
if existing_account and (old_email and existing_account.email != normalize_email(old_email)
or logged_in_account and logged_in_account.email != existing_account.email
existing_account = session.query(AttendeeAccount).filter_by(normalized_email=normalize_email_legacy(email)).first()
if existing_account and (old_email and normalize_email_legacy(normalize_email(existing_account.email)) != super_normalized_old_email
or not old_email and not logged_in_account):
return "There's already an account with that email address."
elif logged_in_account and logged_in_account.normalized_email != existing_account.normalized_email:
return "You cannot reset someone's password while logged in as someone else."

if update_password:
if password and password != confirm_password:
Expand All @@ -125,7 +128,7 @@ def check_account(session, email, password, confirm_password, skip_if_logged_in=
def set_up_new_account(session, attendee, email=None):
email = email or attendee.email
token = genpasswd(short=True)
account = session.query(AttendeeAccount).filter_by(email=normalize_email(email)).first()
account = session.query(AttendeeAccount).filter_by(normalized_email=normalize_email_legacy(email)).first()
if account:
if account.password_reset:
session.delete(account.password_reset)
Expand Down Expand Up @@ -1391,7 +1394,7 @@ def validate_account_email(self, account_email, **params):
def login(self, session, **params):
email = params.get('account_email') # This email has already been validated
password = params.get('account_password')
account = session.query(AttendeeAccount).filter_by(email=normalize_email(email)).first()
account = session.query(AttendeeAccount).filter(AttendeeAccount.normalized_email == normalize_email_legacy(email)).first()
if account and not account.hashed:
return {'success': False, 'message': "We had an issue logging you into your account. Please contact an administrator."}
elif not account or not bcrypt.hashpw(password, account.hashed) == account.hashed:
Expand All @@ -1403,7 +1406,7 @@ def login(self, session, **params):
@ajax
def create_account(self, session, **params):
email = params.get('account_email') # This email has already been validated
account = session.query(AttendeeAccount).filter_by(email=normalize_email(email)).first()
account = session.query(AttendeeAccount).filter_by(normalized_email=normalize_email_legacy(email)).first()
if account:
return {'success': False, 'message': "You already have an account. Please use the 'forgot your password' link. \
Keep in mind your account may be from a prior year."}
Expand All @@ -1429,12 +1432,21 @@ def homepage(self, session, message=''):
if receipt and receipt.current_amount_owed and attendee.is_valid:
attendees_who_owe_money[attendee.full_name] = receipt.current_amount_owed

account_attendee = None
account_attendees = session.valid_attendees().filter(~Attendee.badge_status.in_([c.REFUNDED_STATUS, c.NOT_ATTENDING])
).filter(Attendee.normalized_email == normalize_email_legacy(account.email))
if account_attendees.count() == 1:
account_attendee = account_attendees.first()
if account_attendee not in account.attendees:
account_attendee = None

if not account:
raise HTTPRedirect('../landing/index')

return {
'message': message,
'homepage_account': account,
'account_attendee': account_attendee,
'attendees_who_owe_money': attendees_who_owe_money,
}

Expand All @@ -1446,7 +1458,7 @@ def grant_account(self, session, id, message=''):
message = "Something went wrong. Please try again."
if not attendee.email:
message = "This attendee needs an email address to set up a new account."
if session.current_attendee_account() and normalize_email(attendee.email) == session.current_attendee_account().email:
if session.current_attendee_account() and normalize_email_legacy(attendee.email) == session.current_attendee_account().normalized_email:
message = "You cannot grant an account to someone with the same email address as your account."
if not message:
set_up_new_account(session, attendee)
Expand Down Expand Up @@ -1821,7 +1833,7 @@ def update_account(self, session, id, **params):
def reset_password(self, session, **params):
if 'account_email' in params:
account_email = params['account_email']
account = session.query(AttendeeAccount).filter_by(email=normalize_email(account_email)).first()
account = session.query(AttendeeAccount).filter_by(normalized_email=normalize_email_legacy(account_email)).first()
if 'admin_url' in params:
success_url = "../{}message=Password reset email sent.".format(params['admin_url'])
else:
Expand Down Expand Up @@ -1853,7 +1865,7 @@ def new_password_setup(self, session, account_email, token, message='', **params
if 'id' in params:
account = session.attendee_account(params['id'])
else:
account = session.query(AttendeeAccount).filter_by(email=normalize_email(account_email)).first()
account = session.query(AttendeeAccount).filter_by(normalized_email=normalize_email_legacy(account_email)).first()
if not account or not account.password_reset:
message = 'Invalid link. This link may have already been used or replaced.'
elif account.password_reset.is_expired:
Expand All @@ -1870,7 +1882,7 @@ def new_password_setup(self, session, account_email, token, message='', **params
params.get('confirm_password'), False, True, account.email)

if not message:
account.email = account_email
account.email = normalize_email(account_email)
account.hashed = bcrypt.hashpw(account_password, bcrypt.gensalt())
session.delete(account.password_reset)
for attendee in account.attendees:
Expand Down
Loading

0 comments on commit f34de14

Please sign in to comment.