From d5061e0d8130982010f4171cc1e184e594adb1a1 Mon Sep 17 00:00:00 2001 From: Victoria Earl Date: Sun, 10 Sep 2023 01:41:52 -0400 Subject: [PATCH] Remove tabletop tournament logic Fixes https://jira.magfest.net/browse/MAGDEV-410. --- development-defaults.ini | 1 - uber/configspec.ini | 38 ++------------------- uber/models/__init__.py | 18 ---------- uber/models/attendee.py | 1 - uber/models/panels.py | 1 - uber/models/tabletop.py | 65 +---------------------------------- uber/tasks/__init__.py | 1 - uber/tasks/tabletop.py | 74 ---------------------------------------- 8 files changed, 3 insertions(+), 196 deletions(-) delete mode 100644 uber/tasks/tabletop.py diff --git a/development-defaults.ini b/development-defaults.ini index 3dfa940fa..d66533de6 100644 --- a/development-defaults.ini +++ b/development-defaults.ini @@ -33,7 +33,6 @@ expected_response = "December 2016" alt_schedule_url = "" -tabletop_twilio_number = "+15713646627" tabletop_locations = , diff --git a/uber/configspec.ini b/uber/configspec.ini index 6936b5416..2adec1a51 100644 --- a/uber/configspec.ini +++ b/uber/configspec.ini @@ -43,7 +43,7 @@ slow_load_check = boolean(default=True) send_emails = boolean(default=False) # This turns on/off our automated sms messages. -# (SMS is currently used by panels & tabletop plugins) +# (SMS is currently used by panels plugins) send_sms = boolean(default=False) # All dates/times in our code and emails will use this timezone. This can be @@ -704,37 +704,10 @@ app_limit = integer(default=3) tabletop_locations = string_list(default=list()) -# Twilio-provided phone number used to send/receive SMS messages. -tabletop_twilio_number = string(default="") - # If we're in dev_box mode then we'll ONLY send messages to phone numbers in # this list. In production this list does nothing. testing_phone_numbers = string_list(default=list()) -# The country in which we'll be sending and receiving SMS messages. -# This is passed to the phonenumbers library for parsing/formatting. -tabletop_phone_country = string(default="US") - -# Content of the SMS text sent to tournament entrants reminding them about the -# tournament and asking them to confirm. -tabletop_reminder_sms = string(default="Reminder: {entrant.tournament.name} tournament starts at {entrant.tournament.event.start_time_local:%-I:%M %p}. Reply with Y to confirm your spot or N to drop out.") - -# Number of minutes before the start of a tournament to send an SMS. -tabletop_sms_reminder_minutes = integer(default=30) - -# Wait at least this many minutes after someone signs up for a tournament -# before sending them a reminder SMS. -tabletop_sms_stagger_minutes = integer(default=10) - -# If someone signs up for a tournament within this many minutes of the -# tournament starting, don't bother sending a confirmation SMS and just -# automatically mark them as confirmed. -tabletop_sms_cutoff_minutes = integer(default=10) - -# Number of minutes of slack to give someone after the official start of the -# tournament during which we'll still accept Y response texts. -tabletop_tournament_slack = integer(default=5) - # ============================= # guests @@ -929,10 +902,6 @@ barcode_event_id = integer(default=0) panels_twilio_sid = string(default="") panels_twilio_token = string(default="") -# Twilio account used for tabletop notifications -tabletop_twilio_sid = string(default="") -tabletop_twilio_token = string(default="") - # The email address that is used by /accounts/insert_test_admin test_admin_email = string(default="magfest@example.com") @@ -994,9 +963,7 @@ shifts_created = string(default="") # Dealer registration automatically opens on DEALER_REG_START. After DEALER_REG_DEADLINE # all dealer registration are automatically waitlisted. After DEALER_REG_SHUTDOWN dealers # can no longer even add themselves to the waitlist. Dealer payment reminder emails are -# sent in the days and weeks leading up to DEALER_PAYMENT_DUE. All waitlisted dealers will -# be emailed when the waitlist has been exhausted and all available positions have been -# filled, as defined by DEALER_WAITLIST_CLOSED. +# sent in the days and weeks leading up to DEALER_PAYMENT_DUE. # # Leaving all of these blank will completely turn off dealer registration for your event. # @@ -1004,7 +971,6 @@ dealer_reg_start = string(default="") dealer_reg_deadline = string(default="") dealer_reg_shutdown = string(default="") dealer_payment_due = string(default="") -dealer_waitlist_closed = string(default="") dealer_reg_public = string(default="%(dealer_reg_start)s") # These are similar to the dealer dates, but for Marketplace applications. diff --git a/uber/models/__init__.py b/uber/models/__init__.py index 877987a9e..8ec57bd51 100644 --- a/uber/models/__init__.py +++ b/uber/models/__init__.py @@ -575,7 +575,6 @@ def minutestr(dt): from uber.models.mivs import IndieJudge, IndieGame, IndieStudio # noqa: E402 from uber.models.panels import PanelApplication, PanelApplicant # noqa: E402 from uber.models.promo_code import PromoCode, PromoCodeGroup # noqa: E402 -from uber.models.tabletop import TabletopEntrant, TabletopTournament # noqa: E402 from uber.models.tracking import Tracking # noqa: E402 @@ -2040,23 +2039,6 @@ def panel_applicants(self): return self.query(PanelApplicant).options(joinedload(PanelApplicant.application)) \ .order_by('first_name', 'last_name') - # ========================= - # tabletop - # ========================= - - def entrants(self): - return self.query(TabletopEntrant).options( - joinedload(TabletopEntrant.reminder), - joinedload(TabletopEntrant.attendee), - subqueryload(TabletopEntrant.tournament).subqueryload(TabletopTournament.event)) - - def entrants_by_phone(self): - entrants = defaultdict(list) - for entrant in self.entrants(): - cellphone = normalize_phone(entrant.attendee.cellphone) - entrants[cellphone].append(entrant) - return entrants - @classmethod def model_mixin(cls, model): if model.__name__ in ['SessionMixin', 'QuerySubclass']: diff --git a/uber/models/attendee.py b/uber/models/attendee.py index 6619aa4a7..d653a760b 100644 --- a/uber/models/attendee.py +++ b/uber/models/attendee.py @@ -432,7 +432,6 @@ class Attendee(MagModel, TakesPaymentMixin): # ========================= games = relationship('TabletopGame', backref='attendee') checkouts = relationship('TabletopCheckout', backref='attendee') - entrants = relationship('TabletopEntrant', backref='attendee') # ========================= # badge printing diff --git a/uber/models/panels.py b/uber/models/panels.py index 33770ad6d..090ecc3f8 100644 --- a/uber/models/panels.py +++ b/uber/models/panels.py @@ -27,7 +27,6 @@ class Event(MagModel): applications = relationship('PanelApplication', backref=backref('event', cascade="save-update,merge"), cascade="save-update,merge") panel_feedback = relationship('EventFeedback', backref='event') - tournaments = relationship('TabletopTournament', backref='event', uselist=False) guest = relationship('GuestGroup', backref=backref('event', cascade="save-update,merge"), cascade='save-update,merge') diff --git a/uber/models/tabletop.py b/uber/models/tabletop.py index e91109192..948a3878c 100644 --- a/uber/models/tabletop.py +++ b/uber/models/tabletop.py @@ -13,8 +13,7 @@ __all__ = [ - 'TabletopGame', 'TabletopCheckout', 'TabletopTournament', - 'TabletopEntrant', 'TabletopSmsReminder', 'TabletopSmsReply'] + 'TabletopGame', 'TabletopCheckout'] class TabletopGame(MagModel): @@ -39,65 +38,3 @@ class TabletopCheckout(MagModel): attendee_id = Column(UUID, ForeignKey('attendee.id')) checked_out = Column(UTCDateTime, default=lambda: datetime.now(UTC)) returned = Column(UTCDateTime, nullable=True) - - -class TabletopTournament(MagModel): - event_id = Column(UUID, ForeignKey('event.id'), unique=True) - - # Separate from the event name for cases where we want a shorter name in our SMS messages. - name = Column(UnicodeText) - - entrants = relationship('TabletopEntrant', backref='tournament') - - -class TabletopEntrant(MagModel): - tournament_id = Column(UUID, ForeignKey('tabletop_tournament.id')) - attendee_id = Column(UUID, ForeignKey('attendee.id')) - signed_up = Column(UTCDateTime, default=lambda: datetime.now(UTC)) - confirmed = Column(Boolean, default=False) - - reminder = relationship('TabletopSmsReminder', backref='entrant', uselist=False) - replies = relationship('TabletopSmsReply', backref='entrant') - - @presave_adjustment - def _within_cutoff(self): - if self.is_new: - tournament = self.tournament or self.session.tabletop_tournament(self.tournament_id) - cutoff = timedelta(minutes=c.TABLETOP_SMS_CUTOFF_MINUTES) - if self.signed_up > tournament.event.start_time - cutoff: - self.confirmed = True - - @property - def should_send_reminder(self): - stagger = timedelta(minutes=c.TABLETOP_SMS_STAGGER_MINUTES) - reminder = timedelta(minutes=c.TABLETOP_SMS_REMINDER_MINUTES) - return not self.confirmed \ - and not self.reminder \ - and localized_now() < self.tournament.event.start_time \ - and localized_now() > self.signed_up + stagger \ - and localized_now() > self.tournament.event.start_time - reminder - - def matches(self, message): - sent = message.date_sent.replace(tzinfo=UTC) - start_time_slack = timedelta(minutes=c.TABLETOP_TOURNAMENT_SLACK) - return normalize_phone(self.attendee.cellphone) == message.from_ \ - and self.reminder and sent > self.reminder.when \ - and sent < self.tournament.event.start_time + start_time_slack - - __table_args__ = ( - UniqueConstraint('tournament_id', 'attendee_id', name='_tournament_entrant_uniq'), - ) - - -class TabletopSmsReminder(MagModel): - entrant_id = Column(UUID, ForeignKey('tabletop_entrant.id'), unique=True) - sid = Column(UnicodeText) - when = Column(UTCDateTime, default=lambda: datetime.now(UTC)) - text = Column(UnicodeText) - - -class TabletopSmsReply(MagModel): - entrant_id = Column(UUID, ForeignKey('tabletop_entrant.id'), nullable=True) - sid = Column(UnicodeText) - when = Column(UTCDateTime) - text = Column(UnicodeText) diff --git a/uber/tasks/__init__.py b/uber/tasks/__init__.py index ba00cf776..0b7210c6d 100644 --- a/uber/tasks/__init__.py +++ b/uber/tasks/__init__.py @@ -73,4 +73,3 @@ def configure_celery_logger(loglevel, logfile, format, colorize, **kwargs): from uber.tasks import redis # noqa: F401 from uber.tasks import registration # noqa: F401 from uber.tasks import sms # noqa: F401 -from uber.tasks import tabletop # noqa: F401 diff --git a/uber/tasks/tabletop.py b/uber/tasks/tabletop.py deleted file mode 100644 index 9859914dc..000000000 --- a/uber/tasks/tabletop.py +++ /dev/null @@ -1,74 +0,0 @@ -from datetime import timedelta -from http.client import BadStatusLine -from pytz import UTC -from pockets.autolog import log - -from uber.config import c -from uber.models import Session -from uber.models.tabletop import TabletopSmsReply, TabletopSmsReminder -from uber.tasks import celery -from uber.tasks.sms import get_twilio_client, send_sms_with_client - - -__all__ = ['tabletop_check_notification_replies', 'tabletop_send_notifications'] - - -def tabletop_check_notification_replies(): - twilio_client = get_twilio_client(c.TABLETOP_TWILIO_SID, c.TABLETOP_TWILIO_TOKEN) - if not twilio_client or not c.TABLETOP_TWILIO_NUMBER: - log.warn('SMS notification replies disabled for tabletop') - return - - with Session() as session: - entrants = session.entrants_by_phone() - existing_sids = {sid for [sid] in session.query(TabletopSmsReply.sid)} - messages = [] - - # Pull all the messages down before attempting to act on them. The new - # twilio client uses a streaming mode, so the stream might be timing - # out while it waits for us to act on each message inside our loop. - try: - stream = twilio_client.messages.list(to=c.TABLETOP_TWILIO_NUMBER) - messages = [message for message in stream] - except ConnectionError as ex: - if ex.errno == 'Connection aborted.' \ - and isinstance(ex.strerror, BadStatusLine) \ - and ex.strerror.line == "''": - log.warning('Twilio connection closed unexpectedly') - else: - raise ex - - for message in messages: - if message.sid in existing_sids: - continue - - for entrant in entrants[message.from_]: - if entrant.matches(message): - session.add(TabletopSmsReply( - entrant=entrant, - sid=message.sid, - text=message.body, - when=message.date_sent.replace(tzinfo=UTC) - )) - entrant.confirmed = 'Y' in message.body.upper() - session.commit() - - -def tabletop_send_notifications(): - twilio_client = get_twilio_client(c.TABLETOP_TWILIO_SID, c.TABLETOP_TWILIO_TOKEN) - if not twilio_client or not c.TABLETOP_TWILIO_NUMBER: - log.warn('SMS notification sending disabled for tabletop') - return - - with Session() as session: - for entrant in session.entrants(): - if entrant.should_send_reminder: - body = c.TABLETOP_REMINDER_SMS.format(entrant=entrant) - sid = send_sms_with_client(twilio_client, entrant.attendee.cellphone, body, c.TABLETOP_TWILIO_NUMBER) - entrant.session.add(TabletopSmsReminder(entrant=entrant, text=body, sid=sid)) - entrant.session.commit() - - -if c.SEND_SMS and c.TABLETOP_TWILIO_NUMBER and c.TABLETOP_TWILIO_SID and c.TABLETOP_TWILIO_TOKEN: - tabletop_check_notification_replies = celery.schedule(timedelta(minutes=3))(tabletop_check_notification_replies) - tabletop_send_notifications = celery.schedule(timedelta(minutes=3))(tabletop_send_notifications)