Skip to content
Open
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
1 change: 1 addition & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@ To run the app locally in development, use `yarn start` (not Docker)

- Run tests with `make test file=<test_name>` in the backend folder
- See `/app/backend/readme.md` for more details
- Always add a downgrade to migrations when possible and relevant.
70 changes: 35 additions & 35 deletions app/backend/src/couchers/jobs/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,8 @@ def send_request_notifications(payload: empty_pb2.Empty) -> None:
select(1)
.select_from(HostRequest)
.join(Message, Message.conversation_id == HostRequest.conversation_id)
.where(HostRequest.surfer_user_id == User.id)
.where(Message.id > HostRequest.surfer_last_seen_message_id)
.where(HostRequest.initiator_user_id == User.id)
.where(Message.id > HostRequest.initiator_last_seen_message_id)
.where(Message.id > User.last_notified_request_message_id)
.where(Message.time < now() - timedelta(minutes=5))
.where(Message.message_type == MessageType.text)
Expand All @@ -316,8 +316,8 @@ def send_request_notifications(payload: empty_pb2.Empty) -> None:
select(1)
.select_from(HostRequest)
.join(Message, Message.conversation_id == HostRequest.conversation_id)
.where(HostRequest.host_user_id == User.id)
.where(Message.id > HostRequest.host_last_seen_message_id)
.where(HostRequest.recipient_user_id == User.id)
.where(Message.id > HostRequest.recipient_last_seen_message_id)
.where(Message.id > User.last_notified_request_message_id)
.where(Message.time < now() - timedelta(minutes=5))
.where(Message.message_type == MessageType.text)
Expand All @@ -338,15 +338,15 @@ def send_request_notifications(payload: empty_pb2.Empty) -> None:
where_moderated_content_visible_to_user_column(
select(User, HostRequest, func.max(Message.id))
.where(User.id == user_id)
.join(HostRequest, HostRequest.surfer_user_id == User.id),
.join(HostRequest, HostRequest.initiator_user_id == User.id),
HostRequest,
HostRequest.surfer_user_id,
HostRequest.initiator_user_id,
),
context,
HostRequest.host_user_id,
HostRequest.recipient_user_id,
)
.join(Message, Message.conversation_id == HostRequest.conversation_id)
.where(Message.id > HostRequest.surfer_last_seen_message_id)
.where(Message.id > HostRequest.initiator_last_seen_message_id)
.where(Message.id > User.last_notified_request_message_id)
.where(Message.time < now() - timedelta(minutes=5))
.where(Message.message_type == MessageType.text)
Expand All @@ -359,15 +359,15 @@ def send_request_notifications(payload: empty_pb2.Empty) -> None:
where_moderated_content_visible_to_user_column(
select(User, HostRequest, func.max(Message.id))
.where(User.id == user_id)
.join(HostRequest, HostRequest.host_user_id == User.id),
.join(HostRequest, HostRequest.recipient_user_id == User.id),
HostRequest,
HostRequest.host_user_id,
HostRequest.recipient_user_id,
),
context,
HostRequest.surfer_user_id,
HostRequest.initiator_user_id,
)
.join(Message, Message.conversation_id == HostRequest.conversation_id)
.where(Message.id > HostRequest.host_last_seen_message_id)
.where(Message.id > HostRequest.recipient_last_seen_message_id)
.where(Message.id > User.last_notified_request_message_id)
.where(Message.time < now() - timedelta(minutes=5))
.where(Message.message_type == MessageType.text)
Expand All @@ -385,7 +385,7 @@ def send_request_notifications(payload: empty_pb2.Empty) -> None:
key=str(host_request.conversation_id),
data=notification_data_pb2.HostRequestMissedMessages(
host_request=host_request_to_pb(host_request, session, context),
user=user_model_to_pb(host_request.host, session, context),
user=user_model_to_pb(host_request.recipient, session, context),
am_host=False,
),
)
Expand All @@ -401,7 +401,7 @@ def send_request_notifications(payload: empty_pb2.Empty) -> None:
key=str(host_request.conversation_id),
data=notification_data_pb2.HostRequestMissedMessages(
host_request=host_request_to_pb(host_request, session, context),
user=user_model_to_pb(host_request.surfer, session, context),
user=user_model_to_pb(host_request.initiator, session, context),
am_host=True,
),
)
Expand Down Expand Up @@ -482,42 +482,42 @@ def send_reference_reminders(payload: empty_pb2.Empty) -> None:
# surfers needing to write a ref
q1 = (
select(literal(True), HostRequest, user, other_user)
.join(user, user.id == HostRequest.surfer_user_id)
.join(other_user, other_user.id == HostRequest.host_user_id)
.join(user, user.id == HostRequest.initiator_user_id)
.join(other_user, other_user.id == HostRequest.recipient_user_id)
.outerjoin(
Reference,
and_(
Reference.host_request_id == HostRequest.conversation_id,
# if no reference is found in this join, then the surfer has not written a ref
Reference.from_user_id == HostRequest.surfer_user_id,
Reference.from_user_id == HostRequest.initiator_user_id,
),
)
.where(Reference.id == None)
.where(HostRequest.can_write_reference)
.where(HostRequest.surfer_sent_reference_reminders < reminder_number)
.where(HostRequest.initiator_sent_reference_reminders < reminder_number)
.where(HostRequest.end_time_to_write_reference - reminder_time < now())
.where(HostRequest.surfer_reason_didnt_meetup == None)
.where(HostRequest.initiator_reason_didnt_meetup == None)
.where(users_visible_to_each_other(user, other_user))
)

# hosts needing to write a ref
q2 = (
select(literal(False), HostRequest, user, other_user)
.join(user, user.id == HostRequest.host_user_id)
.join(other_user, other_user.id == HostRequest.surfer_user_id)
.join(user, user.id == HostRequest.recipient_user_id)
.join(other_user, other_user.id == HostRequest.initiator_user_id)
.outerjoin(
Reference,
and_(
Reference.host_request_id == HostRequest.conversation_id,
# if no reference is found in this join, then the host has not written a ref
Reference.from_user_id == HostRequest.host_user_id,
Reference.from_user_id == HostRequest.recipient_user_id,
),
)
.where(Reference.id == None)
.where(HostRequest.can_write_reference)
.where(HostRequest.host_sent_reference_reminders < reminder_number)
.where(HostRequest.recipient_sent_reference_reminders < reminder_number)
.where(HostRequest.end_time_to_write_reference - reminder_time < now())
.where(HostRequest.host_reason_didnt_meetup == None)
.where(HostRequest.recipient_reason_didnt_meetup == None)
.where(users_visible_to_each_other(user, other_user))
)

Expand Down Expand Up @@ -551,16 +551,16 @@ def send_reference_reminders(payload: empty_pb2.Empty) -> None:
),
)
if surfed:
host_request.surfer_sent_reference_reminders = reminder_number
host_request.initiator_sent_reference_reminders = reminder_number
else:
host_request.host_sent_reference_reminders = reminder_number
host_request.recipient_sent_reference_reminders = reminder_number
session.commit()


def send_host_request_reminders(payload: empty_pb2.Empty) -> None:
with session_scope() as session:
host_has_sent_message = select(1).where(
Message.conversation_id == HostRequest.conversation_id, Message.author_id == HostRequest.host_user_id
Message.conversation_id == HostRequest.conversation_id, Message.author_id == HostRequest.recipient_user_id
)

requests = (
Expand All @@ -569,34 +569,34 @@ def send_host_request_reminders(payload: empty_pb2.Empty) -> None:
where_moderated_content_visible_to_user_column(
select(HostRequest),
HostRequest,
HostRequest.host_user_id,
HostRequest.recipient_user_id,
)
.where(HostRequest.status == HostRequestStatus.pending)
.where(HostRequest.host_sent_request_reminders < HOST_REQUEST_MAX_REMINDERS)
.where(HostRequest.recipient_sent_request_reminders < HOST_REQUEST_MAX_REMINDERS)
.where(HostRequest.start_time > func.now())
.where((func.now() - HostRequest.last_sent_request_reminder_time) >= HOST_REQUEST_REMINDER_INTERVAL)
.where(~exists(host_has_sent_message)),
HostRequest.host_user_id,
HostRequest.surfer_user_id,
HostRequest.recipient_user_id,
HostRequest.initiator_user_id,
)
)
.scalars()
.all()
)

for host_request in requests:
host_request.host_sent_request_reminders += 1
host_request.recipient_sent_request_reminders += 1
host_request.last_sent_request_reminder_time = now()

context = make_background_user_context(user_id=host_request.host_user_id)
context = make_background_user_context(user_id=host_request.recipient_user_id)
notify(
session,
user_id=host_request.host_user_id,
user_id=host_request.recipient_user_id,
topic_action=NotificationTopicAction.host_request__reminder,
key=str(host_request.conversation_id),
data=notification_data_pb2.HostRequestReminder(
host_request=host_request_to_pb(host_request, session, context),
surfer=user_model_to_pb(host_request.surfer, session, context),
surfer=user_model_to_pb(host_request.initiator, session, context),
),
moderation_state_id=host_request.moderation_state_id,
)
Expand Down
6 changes: 4 additions & 2 deletions app/backend/src/couchers/materialized_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,13 @@ def float_(stmt: Any) -> Any:
all_responses = union_all(
# host request responses
sa_select(
HostRequest.host_user_id.label("user_id"),
HostRequest.recipient_user_id.label("user_id"),
(s.c.time - t.c.time).label("response_time"),
)
.join(t, t.c.conversation_id == HostRequest.conversation_id)
.outerjoin(s, and_(s.c.conversation_id == HostRequest.conversation_id, s.c.author_id == HostRequest.host_user_id)),
.outerjoin(
s, and_(s.c.conversation_id == HostRequest.conversation_id, s.c.author_id == HostRequest.recipient_user_id)
),
# activeness probes
sa_select(
ActivenessProbe.user_id,
Expand Down
4 changes: 2 additions & 2 deletions app/backend/src/couchers/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ def f() -> Any:
"couchers_users_sent_request",
"Total number of users who have sent a host request",
(
select(func.count(distinct(HostRequest.surfer_user_id)))
.join(User, User.id == HostRequest.surfer_user_id)
select(func.count(distinct(HostRequest.initiator_user_id)))
.join(User, User.id == HostRequest.initiator_user_id)
.where(User.is_visible)
),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""Rename host_request user columns to initiator/recipient

Revision ID: 0140
Revises: 0139
Create Date: 2026-01-25 12:00:00.000000

"""

from alembic import op

# revision identifiers, used by Alembic.
revision = "0140"
down_revision = "0139"
branch_labels = None
depends_on = None


def upgrade() -> None:
# Rename user ID columns
op.alter_column("host_requests", "surfer_user_id", new_column_name="initiator_user_id")
op.alter_column("host_requests", "host_user_id", new_column_name="recipient_user_id")

# Rename other host/surfer columns
op.alter_column("host_requests", "is_host_archived", new_column_name="is_recipient_archived")
op.alter_column("host_requests", "is_surfer_archived", new_column_name="is_initiator_archived")
op.alter_column("host_requests", "host_last_seen_message_id", new_column_name="recipient_last_seen_message_id")
op.alter_column("host_requests", "surfer_last_seen_message_id", new_column_name="initiator_last_seen_message_id")
op.alter_column(
"host_requests", "host_sent_reference_reminders", new_column_name="recipient_sent_reference_reminders"
)
op.alter_column(
"host_requests", "surfer_sent_reference_reminders", new_column_name="initiator_sent_reference_reminders"
)
op.alter_column("host_requests", "host_sent_request_reminders", new_column_name="recipient_sent_request_reminders")
op.alter_column("host_requests", "host_reason_didnt_meetup", new_column_name="recipient_reason_didnt_meetup")
op.alter_column("host_requests", "surfer_reason_didnt_meetup", new_column_name="initiator_reason_didnt_meetup")

# Rename constraints
op.execute(
"ALTER TABLE host_requests RENAME CONSTRAINT fk_host_requests_surfer_user_id_users "
"TO fk_host_requests_initiator_user_id_users"
)
op.execute(
"ALTER TABLE host_requests RENAME CONSTRAINT fk_host_requests_host_user_id_users "
"TO fk_host_requests_recipient_user_id_users"
)

# Rename indexes
op.execute("ALTER INDEX ix_host_requests_surfer_user_id RENAME TO ix_host_requests_initiator_user_id")
op.execute("ALTER INDEX ix_host_requests_host_user_id RENAME TO ix_host_requests_recipient_user_id")
op.execute("ALTER INDEX ix_host_requests_host_didnt_meetup RENAME TO ix_host_requests_recipient_didnt_meetup")
op.execute("ALTER INDEX ix_host_requests_surfer_didnt_meetup RENAME TO ix_host_requests_initiator_didnt_meetup")


def downgrade() -> None:
# Rename indexes back
op.execute("ALTER INDEX ix_host_requests_initiator_didnt_meetup RENAME TO ix_host_requests_surfer_didnt_meetup")
op.execute("ALTER INDEX ix_host_requests_recipient_didnt_meetup RENAME TO ix_host_requests_host_didnt_meetup")
op.execute("ALTER INDEX ix_host_requests_recipient_user_id RENAME TO ix_host_requests_host_user_id")
op.execute("ALTER INDEX ix_host_requests_initiator_user_id RENAME TO ix_host_requests_surfer_user_id")

# Rename constraints back
op.execute(
"ALTER TABLE host_requests RENAME CONSTRAINT fk_host_requests_recipient_user_id_users "
"TO fk_host_requests_host_user_id_users"
)
op.execute(
"ALTER TABLE host_requests RENAME CONSTRAINT fk_host_requests_initiator_user_id_users "
"TO fk_host_requests_surfer_user_id_users"
)

# Rename other columns back
op.alter_column("host_requests", "initiator_reason_didnt_meetup", new_column_name="surfer_reason_didnt_meetup")
op.alter_column("host_requests", "recipient_reason_didnt_meetup", new_column_name="host_reason_didnt_meetup")
op.alter_column("host_requests", "recipient_sent_request_reminders", new_column_name="host_sent_request_reminders")
op.alter_column(
"host_requests", "initiator_sent_reference_reminders", new_column_name="surfer_sent_reference_reminders"
)
op.alter_column(
"host_requests", "recipient_sent_reference_reminders", new_column_name="host_sent_reference_reminders"
)
op.alter_column("host_requests", "initiator_last_seen_message_id", new_column_name="surfer_last_seen_message_id")
op.alter_column("host_requests", "recipient_last_seen_message_id", new_column_name="host_last_seen_message_id")
op.alter_column("host_requests", "is_initiator_archived", new_column_name="is_surfer_archived")
op.alter_column("host_requests", "is_recipient_archived", new_column_name="is_host_archived")

# Rename user ID columns back
op.alter_column("host_requests", "recipient_user_id", new_column_name="host_user_id")
op.alter_column("host_requests", "initiator_user_id", new_column_name="surfer_user_id")
Loading