Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support specifying time slot with the subscribe link #393

Merged
1 commit merged into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions api/tests/routes/api/v1/preference_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from yelp_beans.routes.api.v1 import preferences
from yelp_beans.routes.api.v1.preferences import preferences_api
from yelp_beans.routes.api.v1.preferences import preferences_api_post
from yelp_beans.routes.api.v1.types import TimeSlot


def test_preferences_api_no_user(app, session):
Expand Down Expand Up @@ -270,6 +271,49 @@ def test_subscribe_api_post_user_pick_popular(client, session):
assert new_preference.preference == sub_time_2


def test_subscribe_api_post_user_predefined_time(client, session):
sub_time_1 = SubscriptionDateTime(datetime=datetime(2017, 7, 20, 13, 0))
sub_time_2 = SubscriptionDateTime(datetime=datetime(2017, 7, 20, 18, 0))
subscription = MeetingSubscription(
timezone="America/Los_Angeles",
datetime=[sub_time_1, sub_time_2],
title="Test",
size=2,
office="tester",
location="test place",
user_rules=[],
default_auto_opt_in=True,
)
session.add(subscription)
# Make a fake preference, so second time slot is more popular
preference = UserSubscriptionPreferences(subscription=subscription, preference=sub_time_2, auto_opt_in=True, user_id=200)
session.add(preference)

user = User(
first_name="tester",
last_name="user",
email="[email protected]",
meta_data={"email": "[email protected]"},
subscription_preferences=[],
)
session.add(user)
session.commit()
time_slot = TimeSlot.from_sqlalchemy(sub_time_1, subscription.timezone)
resp = client.post(
f"/v1/user/preferences/subscribe/{subscription.id}",
json={"email": user.email, "time_slot": time_slot.model_dump(mode="json")},
)
assert resp.status_code == 200
assert resp.json["time_slot"] == {
"day": time_slot.day.value,
"hour": time_slot.hour,
"minute": time_slot.minute,
}

new_preference = session.query(UserSubscriptionPreferences).filter(UserSubscriptionPreferences.user_id == user.id).one()
assert new_preference.preference == sub_time_1


@pytest.mark.parametrize("auto_opt_in", (True, False))
def test_subscribe_api_post_create_auto_opt_in_specified(client, session, auto_opt_in):
sub_time = SubscriptionDateTime(datetime=datetime(2017, 7, 20, 13, 0))
Expand Down
31 changes: 24 additions & 7 deletions api/yelp_beans/routes/api/v1/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def subscribe_api_post(subscription_id: int) -> dict[str, Any]:
data = request.json
user = get_user(data.get("email"))
auto_opt_in: bool | None = data.get("auto_opt_in")
time_slot_data: str | None = data.get("time_slot")
if not user:
resp = jsonify({"msg": f"A user doesn't exist with the email of \"{data.get('email')}\""})
resp.status_code = 400
Expand All @@ -77,12 +78,28 @@ def subscribe_api_post(subscription_id: int) -> dict[str, Any]:
resp.status_code = 403
return resp

datetime_to_subscriber_counts = get_subscriber_counts(subscription_id)
if datetime_to_subscriber_counts:
best_datetime_id, _ = max(datetime_to_subscriber_counts.items(), key=lambda row: row[1])
if time_slot_data is None:
datetime_to_subscriber_counts = get_subscriber_counts(subscription_id)
if datetime_to_subscriber_counts:
datetime_id, _ = max(datetime_to_subscriber_counts.items(), key=lambda row: row[1])
else:
# No most popular time slot, so just pick the first one
datetime_id = subscription.datetime[0].id
else:
# No most popular time slot, so just pick the first one
best_datetime_id = subscription.datetime[0].id
datetime_id = None
time_slot = TimeSlot.parse_obj(time_slot_data)
for sub_datetime in subscription.datetime:
sub_time_slot = TimeSlot.from_sqlalchemy(sub_datetime, subscription.timezone)
if sub_time_slot == time_slot:
datetime_id = sub_datetime.id
break

if datetime_id is None:
resp = jsonify({"msg": f"Unable to find subscription datetime from time slot {time_slot}"})
resp.status_code = 400
return resp

assert datetime_id is not None, "We shouldn't get to this point without picking a datetime"

existing_matching_prefs = [pref for pref in user.subscription_preferences if pref.subscription_id == subscription_id]

Expand All @@ -95,11 +112,11 @@ def subscribe_api_post(subscription_id: int) -> dict[str, Any]:
preference = PreferenceOptions(active=True)
if auto_opt_in is not None:
preference["auto_opt_in"] = auto_opt_in
add_preferences(user, {best_datetime_id: preference}, subscription_id)
add_preferences(user, {datetime_id: preference}, subscription_id)
new_preference = True

# get_subscriber_counts return this datetime id, so it should always exist in subscription.datetime
datetime = next(rule for rule in subscription.datetime if rule.id == best_datetime_id)
datetime = next(rule for rule in subscription.datetime if rule.id == datetime_id)
return {
"subscription": Subscription.from_sqlalchemy(subscription).model_dump(mode="json"),
"time_slot": TimeSlot.from_sqlalchemy(datetime, subscription.timezone).model_dump(mode="json"),
Expand Down
23 changes: 20 additions & 3 deletions frontend/containers/Subscribe.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,30 @@ const getSubscriptionId = () => {
return path[path.length - 1];
};

const getAutoOptIn = () => {
const searchParams = new URLSearchParams(window.location.search);
const getAutoOptIn = (searchParams) => {
const autoOptIn = searchParams.get("auto_opt_in");
if (autoOptIn == null) {
return null;
}
return ["1", "t", "true", "y", "yes"].includes(autoOptIn);
};

const getTimeSlot = (searchParams) => {
const day = searchParams.get("day");
const hour = searchParams.get("hour");
const minute = searchParams.get("minute");

if (day == null || hour == null || minute == null) {
return null;
}

return {
day,
hour,
minute,
};
};

function SubscribedMessage({ subscription, timeSlot, newPreference }) {
let msg = "have been";
if (!newPreference) {
Expand Down Expand Up @@ -73,10 +88,12 @@ function Subscribe() {
axios
.get("/email")
.then((resEmail) => {
const searchParams = new URLSearchParams(window.location.search);
axios
.post(`/v1/user/preferences/subscribe/${getSubscriptionId()}`, {
email: resEmail.data.email,
auto_opt_in: getAutoOptIn(),
auto_opt_in: getAutoOptIn(searchParams),
time_slot: getTimeSlot(searchParams),
})
.then((resSubscribe) => {
setSubscribedSubscription(resSubscribe.data);
Expand Down
Loading