Skip to content

Commit

Permalink
Merge pull request #4412 from magfest/fix-magdev1313
Browse files Browse the repository at this point in the history
Update Rock Island for Super 2025
  • Loading branch information
kitsuta authored Oct 11, 2024
2 parents 5cd37dc + f056e31 commit ff124fe
Show file tree
Hide file tree
Showing 17 changed files with 496 additions and 100 deletions.
85 changes: 85 additions & 0 deletions alembic/versions/2e4feb6c2d18_update_rock_island_checklist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""Update rock island checklist
Revision ID: 2e4feb6c2d18
Revises: 9b657ae4c4ac
Create Date: 2024-10-11 11:40:31.124938
"""


# revision identifiers, used by Alembic.
revision = '2e4feb6c2d18'
down_revision = '9b657ae4c4ac'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa
import residue


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.add_column('guest_merch', sa.Column('inventory_updated', residue.UTCDateTime(), nullable=True))
op.add_column('guest_merch', sa.Column('delivery_method', sa.Integer(), nullable=True))
op.add_column('guest_merch', sa.Column('payout_method', sa.Integer(), nullable=True))
op.add_column('guest_merch', sa.Column('paypal_email', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('check_payable', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('check_zip_code', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('check_address1', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('check_address2', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('check_city', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('check_region', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('check_country', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('arrival_plans', sa.Unicode(), server_default='', nullable=False))
op.add_column('guest_merch', sa.Column('merch_events', sa.Unicode(), server_default='', nullable=False))
op.drop_column('guest_merch', 'bringing_boxes')


def downgrade():
op.add_column('guest_merch', sa.Column('bringing_boxes', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False))
op.drop_column('guest_merch', 'merch_events')
op.drop_column('guest_merch', 'arrival_plans')
op.drop_column('guest_merch', 'check_country')
op.drop_column('guest_merch', 'check_region')
op.drop_column('guest_merch', 'check_city')
op.drop_column('guest_merch', 'check_address2')
op.drop_column('guest_merch', 'check_address1')
op.drop_column('guest_merch', 'check_zip_code')
op.drop_column('guest_merch', 'check_payable')
op.drop_column('guest_merch', 'paypal_email')
op.drop_column('guest_merch', 'payout_method')
op.drop_column('guest_merch', 'delivery_method')
op.drop_column('guest_merch', 'inventory_updated')
40 changes: 28 additions & 12 deletions uber/automated_emails.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ def generic_placeholder(a): return a.placeholder and (not deferred_attendee_plac
Attendee,
'Claim your deferred badge for {EVENT_NAME} {EVENT_YEAR}!',
'placeholders/deferred.html',
deferred_attendee_placeholder,
deferred_attendee_placeholder,
when=after(c.PREREG_OPEN),
ident='claim_deferred_badge')

Expand Down Expand Up @@ -864,13 +864,14 @@ def __init__(self, conf):
when=days_before(7, c.FINAL_EMAIL_DEADLINE),
ident='hotel_requirements_reminder_last_chance')

AutomatedEmailFixture(
Room,
'{EVENT_NAME} Hotel Room Assignment',
'hotel/room_assignment.txt',
lambda r: r.locked_in,
sender=c.ROOM_EMAIL_SENDER,
ident='hotel_room_assignment')
if not c.HOTEL_REQUESTS_URL:
AutomatedEmailFixture(
Room,
'{EVENT_NAME} Hotel Room Assignment',
'hotel/room_assignment.txt',
lambda r: r.locked_in,
sender=c.ROOM_EMAIL_SENDER,
ident='hotel_room_assignment')


# =============================
Expand Down Expand Up @@ -1264,7 +1265,6 @@ def __init__(self, subject, template, filter, ident, **kwargs):


if c.PANELS_ENABLED:

PanelAppEmailFixture(
'Your {EVENT_NAME} Panel Application Has Been Received: {{ app.name }}',
'panels/application.html',
Expand Down Expand Up @@ -1344,11 +1344,10 @@ def __init__(self, subject, template, filter, ident, **kwargs):
**kwargs)


AutomatedEmailFixture(
GuestGroup,
BandEmailFixture(
'{EVENT_NAME} Performer Checklist',
'guests/band_notification.txt',
lambda b: b.group_type == c.BAND, sender=c.BAND_EMAIL,
lambda b: True,
ident='band_checklist_inquiry')

BandEmailFixture(
Expand Down Expand Up @@ -1468,3 +1467,20 @@ def __init__(self, subject, template, filter, ident, **kwargs):
lambda g: not g.checklist_completed,
when=days_after(7, c.GUEST_INFO_DEADLINE),
ident='guest_reminder_2')

AutomatedEmailFixture(
GuestGroup,
f'Sign up to sell merch at {c.EVENT_NAME} Rock Island',
'guests/rock_island_intro.txt',
lambda g: g.group_type in c.ROCK_ISLAND_GROUPS and g.deadline_from_model('merch') and not g.group_type == c.BAND,
ident='rock_island_intro',
sender=c.ROCK_ISLAND_EMAIL)

AutomatedEmailFixture(
GuestGroup,
f'Last chance to finalize your {c.EVENT_NAME} Rock Island Inventory',
'guests/rock_island_inventory_reminder.txt',
lambda g: g.group_type in c.ROCK_ISLAND_GROUPS and g.merch and g.merch.selling_merch == c.ROCK_ISLAND,
ident='ri_inventory_reminder',
when=days_before(7, c.ROCK_ISLAND_DEADLINE),
sender=c.ROCK_ISLAND_EMAIL)
13 changes: 12 additions & 1 deletion uber/configspec.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1899,13 +1899,24 @@ no = string(default="No, we definitely will not need rehearsal space and we are
maybe = string(default="We might need rehearsal space; please contact us about our needs.")
yes = string(default="We definitely need rehearsal space; please contact us about our needs.")

[[guest_merch_delivery]]
shipping = string(default="Shipping to the warehouse")
in_person = string(default="Bringing in person")

[[guest_merch_payout_method]]
paypal = string(default="PayPal")
check = string(default="Check")
other = string(default="Other")

[[merch_types]]
cd = string(default="Album")
cd = string(default="Music")
tshirt = string(default="T-Shirt")
apparel = string(default="Other Apparel")
pin = string(default="Pin")
sticker = string(default="Sticker")
poster = string(default="Poster")
button = string(default="Button")
patch = string(default="Patch")
miscellaneous = string(default="Miscellaneous")

[[album_media]]
Expand Down
20 changes: 20 additions & 0 deletions uber/model_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,26 @@ def is_merch_checklist_complete(guest_merch):
and guest_merch.poc_region
and guest_merch.poc_country):
return 'You must tell us your complete mailing address'

elif not guest_merch.delivery_method:
return 'Please tell us how you will bring us your inventory'
elif not guest_merch.payout_method:
return 'Please tell us how you would like to be paid for your merch'
elif guest_merch.payout_method == c.PAYPAL and not guest_merch.paypal_email:
return 'We need your PayPal email address to pay you via PayPal'
elif guest_merch.payout_method == c.CHECK:
if not guest_merch.check_payable:
return 'Please include the name that should go on your check'
if not (
guest_merch.check_zip_code
and guest_merch.check_address1
and guest_merch.check_city
and guest_merch.check_region
and guest_merch.check_country
):
return 'Please include the mailing address to send a check to.'
elif not guest_merch.arrival_plans:
return 'Please tell us your estimated arrival to Rock Island to check in your inventory'


@validation.GuestTravelPlans
Expand Down
24 changes: 22 additions & 2 deletions uber/models/guests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import shutil
import uuid
from collections import defaultdict
from datetime import timedelta
from datetime import datetime, timedelta

from pockets import uniquify, classproperty
from residue import JSON, CoerceUTF8 as UnicodeText, UTCDateTime, UUID
Expand Down Expand Up @@ -343,8 +343,21 @@ class GuestMerch(MagModel):

guest_id = Column(UUID, ForeignKey('guest_group.id'), unique=True)
selling_merch = Column(Choice(c.GUEST_MERCH_OPTS), nullable=True)
delivery_method = Column(Choice(c.GUEST_MERCH_DELIVERY_OPTS), nullable=True)
payout_method = Column(Choice(c.GUEST_MERCH_PAYOUT_METHOD_OPTS), nullable=True)
paypal_email = Column(UnicodeText)
check_payable = Column(UnicodeText)
check_zip_code = Column(UnicodeText)
check_address1 = Column(UnicodeText)
check_address2 = Column(UnicodeText)
check_city = Column(UnicodeText)
check_region = Column(UnicodeText)
check_country = Column(UnicodeText)

arrival_plans = Column(UnicodeText)
merch_events = Column(UnicodeText)
inventory = Column(JSON, default={}, server_default='{}')
bringing_boxes = Column(UnicodeText)
inventory_updated = Column(UTCDateTime, nullable=True)
extra_info = Column(UnicodeText)
tax_phone = Column(UnicodeText)

Expand Down Expand Up @@ -402,6 +415,10 @@ def rock_island_url(self):
@property
def rock_island_csv_url(self):
return '../guest_reports/rock_island_csv?id={}'.format(self.guest_id)

@property
def rock_island_square_export_url(self):
return f'../guest_reports/rock_island_square_xlsx?id={self.guest_id}'

@property
def status(self):
Expand Down Expand Up @@ -592,19 +609,22 @@ def remove_inventory_item(self, item_id, *, persist_files=True):
if persist_files:
self._prune_inventory_file(item, inventory, prune_missing=True)
self.inventory = inventory
self.inventory_updated = datetime.now()
return item

def set_inventory(self, inventory, *, persist_files=True):
if persist_files:
self._save_inventory_files(inventory)
self._prune_inventory_files(inventory, prune_missing=True)
self.inventory = inventory
self.inventory_updated = datetime.now()

def update_inventory(self, inventory, *, persist_files=True):
if persist_files:
self._save_inventory_files(inventory)
self._prune_inventory_files(inventory, prune_missing=False)
self.inventory = dict(self.inventory, **inventory)
self.inventory_updated = datetime.now()


class GuestCharity(MagModel):
Expand Down
3 changes: 2 additions & 1 deletion uber/models/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ def repr(cls, column, value):
def differences(cls, instance):
diff = {}
for attr, column in instance.__table__.columns.items():
if attr in ['currently_sending', 'last_send_time', 'unapproved_count', 'last_updated', 'last_synced']:
if attr in ['currently_sending', 'last_send_time',
'unapproved_count', 'last_updated', 'last_synced', 'inventory_updated']:
continue

new_val = getattr(instance, attr)
Expand Down
71 changes: 69 additions & 2 deletions uber/site_sections/guest_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from uber.config import c
from uber.custom_tags import time_day_local
from uber.decorators import all_renderable, csv_file, site_mappable
from uber.decorators import all_renderable, csv_file, site_mappable, xlsx_file
from uber.errors import HTTPRedirect
from uber.models import Group, GuestAutograph, GuestGroup, GuestMerch, GuestTravelPlans
from uber.utils import convert_to_absolute_url
Expand Down Expand Up @@ -101,6 +101,73 @@ def rock_island(self, session, message='', only_empty=None, id=None, **params):
'guest_groups': [guest for guest in guest_groups if session.admin_can_see_guest_group(guest)],
'only_empty': only_empty
}

@site_mappable(download=True)
@xlsx_file
def rock_island_square_xlsx(self, out, session, id=None, **params):
header_row = [
'Token', 'Item Name', 'Variation Name', 'Unit and Precision', 'SKU', 'Description', 'Category',
'SEO Title', 'SEO Description', 'Permalink', 'Square Online Item Visibility', 'Weight (lb)', 'Shipping Enabled',
'Self-serve Ordering Enabled', 'Delivery Enabled', 'Pickup Enabled', 'Price', 'Sellable', 'Stockable',
'Skip Detail Screen in POS', 'Option Name 1', 'Option Value 1', 'Current Quantity MAGFest Rock Island',
'New Quantity MAGFest Rock Island'
]

query = session.query(GuestGroup).options(
subqueryload(GuestGroup.group)).options(
subqueryload(GuestGroup.merch))

if id:
guest_groups = [query.get(id)]
else:
guest_groups = query.filter(
GuestGroup.id == GuestMerch.guest_id,
GuestMerch.selling_merch == c.ROCK_ISLAND,
GuestGroup.group_id == Group.id).order_by(
Group.name).all()

rows = []
item_type_square_name = {
c.CD: "MUSIC",
c.TSHIRT: "APPAREL",
c.APPAREL: "APPAREL",
c.PIN: "PIN",
c.STICKER: "STICKER",
c.POSTER: "POSTER",
c.BUTTON: "BUTTON",
c.PATCH: "PATCH",
c.MISCELLANEOUS: "MISC",
}

def _inventory_sort_key(item):
return ' '.join([
c.MERCH_TYPES[int(item['type'])],
item['name']
])

def _generate_row(item, guest, variation_name='Regular'):
item_type = int(item['type'])
item_name = f'{item_type_square_name[item_type]} {guest.group.name} {item['name']}'
if item_type == c.CD:
item_name = f'{item_name} {c.ALBUM_MEDIAS[int(item['media'])]}'
elif item_type == c.TSHIRT:
item_name = f'{item_name} T-shirt'

return [
'', item_name, variation_name, '', '', '', guest.group.name, '', '', '',
'hidden', '', 'N', '', 'N', 'N', '{:.2f}'.format(float(item['price'])),
'', '', 'N', '', '', '', ''
]

for guest in guest_groups:
for item in sorted(guest.merch.inventory.values(), key=_inventory_sort_key):
merch_type = int(item['type'])
if merch_type in (c.TSHIRT, c.APPAREL):
for line_item in guest.merch.line_items(item):
rows.append(_generate_row(item, guest, guest.merch.line_item_to_string(item, line_item)))
else:
rows.append(_generate_row(item, guest))
out.writerows(header_row, rows)

@site_mappable(download=True)
@csv_file
Expand All @@ -127,7 +194,7 @@ def _inventory_sort_key(item):
item['price']
])

for guest in [guest for guest in guest_groups if session.admin_can_see_guest_group(guest)]:
for guest in guest_groups:
for item in sorted(guest.merch.inventory.values(), key=_inventory_sort_key):
merch_type = int(item['type'])
if merch_type in (c.TSHIRT, c.APPAREL):
Expand Down
Loading

0 comments on commit ff124fe

Please sign in to comment.