Skip to content

Commit

Permalink
Merge pull request #4427 from MidwestFurryFandom/artist-marketplace
Browse files Browse the repository at this point in the history
Sync with MFF
  • Loading branch information
kitsuta authored Nov 5, 2024
2 parents 115bb83 + 76fc191 commit 77699b4
Show file tree
Hide file tree
Showing 30 changed files with 289 additions and 161 deletions.
Original file line number Diff line number Diff line change
@@ -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)
45 changes: 10 additions & 35 deletions uber/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,9 @@ class AWSSecretFetcher:
"""

def __init__(self):
self.start_session()

def start_session(self):
import boto3

aws_session = boto3.session.Session(
Expand All @@ -1213,10 +1216,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
Expand Down Expand Up @@ -1262,9 +1270,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", "")
Expand Down Expand Up @@ -1372,10 +1380,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,
Expand Down Expand Up @@ -1870,33 +1875,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'
}
}
})
1 change: 0 additions & 1 deletion uber/configspec.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2004,7 +2004,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]]
Expand Down
42 changes: 28 additions & 14 deletions uber/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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'
Expand All @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion uber/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion uber/models/art_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion uber/models/hotel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand Down
19 changes: 18 additions & 1 deletion uber/models/marketplace.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
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 <a href='../preregistration/confirm?id={self.attendee.id}' "
"target='_blank'>purchase your badge here</a> 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.")
7 changes: 6 additions & 1 deletion uber/payments.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 2 additions & 2 deletions uber/receipt_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,11 +519,11 @@ def badge_cost(group, new_group=None):
cost_table = defaultdict(int)
ordered_badges = sorted(group.floating, key=lambda a: a.badge_cost, reverse=True)

if len(ordered_badges) <= badge_diff:
if len(ordered_badges) < badge_diff:
log.error("We tried to compute a group reducing its badges to below its floating badges, "
"but that shouldn't be possible!")
return

for count in range(badge_diff):
attendee = ordered_badges[count]
cost_table[attendee.badge_cost * 100 * -1] += 1
Expand Down
9 changes: 4 additions & 5 deletions uber/site_sections/dealer_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,17 +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:
log.error(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):
Expand All @@ -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()

Expand Down
4 changes: 1 addition & 3 deletions uber/site_sections/group_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,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
Expand Down
Loading

0 comments on commit 77699b4

Please sign in to comment.