Skip to content

Commit

Permalink
Remove the discounts framework
Browse files Browse the repository at this point in the history
A few older discount programs have been discontinued already and SOLE
has expressed some objection at MITOC sharing any members' information
on their behalf (even with those members' consent). To comply with these
concerns, let's just remove discounts entirely -- if any local
businesses want to extend discounts to MITOCers, that's entirely at the
discretion of those local businesses.

Puring the spreadsheet data
===========================
We'll have to inform business that previously partnered with MITOC that
the spreadsheets will be going away.

Ikon will remain hardcoded for this year
========================================
At a later date, we can just move the link for this program to the main
MITOC static site. But let's just leave the form up for now!

Safety of removing & descheduling tasks
=======================================
While it's not technically safe to just delete Celery tasks without
first descheduling them (since old messages could be attempted & fail
due to no matching method), I will be sure to totally stop Celery when
deploying this.

Models will be destroyed later
==============================
This commit just removes the model definitions. We'll actually drop the
underlying tables at a later date.
  • Loading branch information
DavidCain committed Sep 3, 2024
1 parent afd0ab0 commit 8c28892
Show file tree
Hide file tree
Showing 33 changed files with 83 additions and 1,338 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ there to easily launch an instance.
## Account management
![Email address management][screenshots-email_address_management]

## Member discounts
![MITOC members can receive discounts][screenshots-discounts]

## Leader application
![Submitted application][screenshots-leader_application_submitted]

Expand Down Expand Up @@ -78,7 +75,6 @@ trip formats once subject to same problems as Winter School.

[screenshots-profile]: https://dcain.me/static/images/mitoc-trips/profile.png
[screenshots-email_address_management]: https://dcain.me/static/images/mitoc-trips/email_address_management.png
[screenshots-discounts]: https://dcain.me/static/images/mitoc-trips/discounts.png
[screenshots-leader_application_submitted]: https://dcain.me/static/images/mitoc-trips/leader_application_submitted.png
[screenshots-leader_application_queue]: https://dcain.me/static/images/mitoc-trips/leader_application_queue.png
[screenshots-leader_application]: https://dcain.me/static/images/mitoc-trips/leader_application.png
Expand Down
108 changes: 1 addition & 107 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ django-phonenumber-field = { version = ">= 2.0", extras = ["phonenumberslite"] }
django-pipeline = "*" # TODO: To eventually be replaced by webpack-loader
django-smtp-ssl = "*"
django-webpack-loader = "^1.1.0" # Should maintain parity with frontend/package.json
gspread = "*"
gunicorn = "^22.0.0" # Used to run production worker
markdown2 = "*"
mitoc-const = "^1.0.0" # (1.0.0 includes type hints)
Expand Down
15 changes: 1 addition & 14 deletions ws/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import ws.utils.membership as membership_utils
import ws.utils.perms as perm_utils
import ws.utils.signups as signup_utils
from ws import enums, models, tasks
from ws import enums, models
from ws.decorators import group_required
from ws.middleware import RequestWithParticipant
from ws.mixins import JsonTripLeadersOnlyView, TripLeadersOnlyView
Expand Down Expand Up @@ -568,9 +568,6 @@ def post(self, request, *args, **kwargs):
if not participant: # Not in our system, nothing to do
return JsonResponse({})

# Can be true in case of early renewal!
was_already_active = participant.membership_active

keys = ("membership_expires", "waiver_expires")
update_fields = {
key: date.fromisoformat(self.payload[key])
Expand All @@ -579,14 +576,6 @@ def post(self, request, *args, **kwargs):
}
_membership, created = participant.update_membership(**update_fields)

# If the participant has reactivated a membership, update any discount sheets
# (this will ensure that they do not have to wait for the next daily refresh)
if not was_already_active:
for discount in participant.discounts.all():
tasks.update_discount_sheet_for_participant.delay(
discount.pk, participant.pk
)

return JsonResponse({}, status=201 if created else 200)


Expand Down Expand Up @@ -654,7 +643,6 @@ class MemberInfo(TypedDict):
is_leader: NotRequired[bool]
num_trips_attended: NotRequired[int]
num_trips_led: NotRequired[int]
num_discounts: NotRequired[int]


class RawMembershipStatsView(View):
Expand All @@ -678,7 +666,6 @@ def _flat_members_info(
"is_leader": info.trips_information.is_leader,
"num_trips_attended": info.trips_information.num_trips_attended,
"num_trips_led": info.trips_information.num_trips_led,
"num_discounts": info.trips_information.num_discounts,
}
)
# If there's a verified MIT email address from the trips site, use it!
Expand Down
16 changes: 0 additions & 16 deletions ws/cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,6 @@ def lapsed_participants() -> QuerySet[models.Participant]:
return models.Participant.objects.filter(lapsed_update).exclude(active_members)


def purge_non_student_discounts() -> None:
"""Purge non-students from student-only discounts.
Student eligibility is enforced at the API and form level. If somebody was
a student at the time of enrolling but is no longer a student, we should
unenroll them.
"""
stu_discounts = models.Discount.objects.filter(student_required=True)
not_student = ~Q(affiliation__in=models.Participant.STUDENT_AFFILIATIONS)

# Remove student discounts from all non-students who have them
participants = models.Participant.objects.all()
for par in participants.filter(not_student, discounts__in=stu_discounts):
par.discounts.set(par.discounts.filter(student_required=False))


@transaction.atomic
def purge_old_medical_data() -> None:
"""For privacy reasons, purge old medical information.
Expand Down
1 change: 0 additions & 1 deletion ws/email/renew.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ def send_email_reminding_to_renew(

context = {
"participant": participant,
"discounts": participant.discounts.all().order_by("name"),
"expiry_if_renewing": membership.membership_expires + timedelta(days=365),
"unsubscribe_token": unsubscribe.generate_unsubscribe_token(participant),
}
Expand Down
31 changes: 0 additions & 31 deletions ws/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,37 +57,6 @@ class RequiredModelForm(forms.ModelForm):
error_css_class = "warning"


class DiscountForm(forms.ModelForm):
send_membership_reminder = forms.BooleanField(
label="Email me when it's time to renew my membership",
help_text="Ensure continued access to discounts (not required, but strongly recommended!)",
required=False,
)

def clean_discounts(self):
"""Ensure the participant meets the requirements for each discount."""
participant = self.instance
discounts = self.cleaned_data["discounts"]

if not participant.is_student:
for discount in discounts:
if discount.student_required:
raise ValidationError(f"{discount.name} is a student-only discount")
if not discount.ga_key:
# The UI should prevent "enrolling" in these read-only discounts, but check anyway.
raise ValidationError(
f"{discount.name} does not support sharing your information automatically. "
"See discount terms for instructions."
)

return discounts

class Meta:
model = models.Participant
fields = ["discounts", "send_membership_reminder"]
widgets = {"discounts": forms.CheckboxSelectMultiple}


class ParticipantForm(forms.ModelForm):
class Meta:
model = models.Participant
Expand Down
2 changes: 0 additions & 2 deletions ws/merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,11 @@
"ws_leaderrecommendation": ("creator_id", "participant_id"),
"ws_lectureattendance": ("participant_id", "creator_id"),
"ws_winterschoolsettings": ("last_updated_by_id",),
"ws_discount_administrators": ("participant_id",),
"ws_distinctaccounts": ("left_id", "right_id"),
# Each of these tables should only have one row for the given person.
# (For example, it's possible that two participants representing the same human are on the same trip.
# In practice, though, this should never actually be happening. Uniqueness constraints will protect us.
"ws_tripinfo_drivers": ("participant_id",),
"ws_participant_discounts": ("participant_id",),
"ws_trip_leaders": ("participant_id",),
"ws_leadersignup": ("participant_id",),
"ws_signup": ("participant_id",),
Expand Down
52 changes: 0 additions & 52 deletions ws/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,56 +115,6 @@ def get_queryset(self):
return leaders.prefetch_related("leaderrating_set")


class Discount(models.Model):
"""Discount at another company available to MITOC members."""

administrators = models.ManyToManyField(
"ws.Participant",
blank=True,
help_text="Persons selected to administer this discount",
related_name="discounts_administered",
)

active = models.BooleanField(
default=True, help_text="Discount is currently open & active"
)
name = models.CharField(max_length=255)
summary = models.CharField(max_length=255)
terms = models.TextField(max_length=4095)
url = models.URLField(blank=True)
ga_key = models.CharField(
max_length=63,
# If blank, then we don't actually report this information to a spreadsheet
blank=True,
help_text="key for Google spreadsheet with membership information (shared as read-only with the company)",
)

time_created = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)

student_required = models.BooleanField(
default=False, help_text="Discount provider requires recipients to be students"
)

report_school = models.BooleanField(
default=False, help_text="Report MIT affiliation if participant is a student"
)
report_student = models.BooleanField(
default=False,
help_text="Report MIT affiliation and student status to discount provider",
)
report_leader = models.BooleanField(
default=False, help_text="Report MITOC leader status to discount provider"
)
report_access = models.BooleanField(
default=False,
help_text="Report if participant should have leader, student, or admin level access",
)

def __str__(self): # pylint: disable=invalid-str-returned
return self.name


class MembershipStats(SingletonModel):
"""Cached response from https://mitoc-gear.mit.edu/api-auth/v1/stats
Expand Down Expand Up @@ -418,8 +368,6 @@ class Participant(models.Model):
}
)

discounts = models.ManyToManyField(Discount, blank=True)

class Meta:
ordering = ["name", "email"]

Expand Down
7 changes: 0 additions & 7 deletions ws/privacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def all_data(self):
fields = [
"user",
"membership",
"discounts",
"car",
"medical",
"lottery_info",
Expand Down Expand Up @@ -60,12 +59,6 @@ def car(self):
"""Participant's car information."""
return self.par.car and model_to_dict(self.par.car, exclude="id")

@property
def discounts(self):
"""Discounts where the participant elected to share their info."""
for d in self.par.discounts.all():
yield model_to_dict(d, fields=["name", "active", "summary", "url"])

@property
def authored_feedback(self):
"""Feedback supplied by the participant."""
Expand Down
Loading

0 comments on commit 8c28892

Please sign in to comment.