def test_host_request_email(db): with session_scope() as session: to_user, api_token_to = generate_user() # little trick here to get the upload correctly without invalidating users key = random_hex(32) filename = random_hex(32) + ".jpg" session.add( Upload( key=key, filename=filename, creator_user_id=to_user.id, )) session.commit() from_user, api_token_from = generate_user(avatar_key=key) from_date = "2020-01-01" to_date = "2020-01-05" conversation = Conversation() message = Message( conversation=conversation, author_id=from_user.id, text=random_hex(64), message_type=MessageType.text, ) host_request = HostRequest( conversation=conversation, from_user=from_user, to_user=to_user, from_date=from_date, to_date=to_date, status=HostRequestStatus.pending, from_last_seen_message_id=message.id, ) session.add(host_request) with patch("couchers.email.queue_email") as mock: send_host_request_email(host_request) assert mock.call_count == 1 (sender_name, sender_email, recipient, subject, plain, html), _ = mock.call_args assert recipient == to_user.email assert "host request" in subject.lower() assert to_user.name in plain assert to_user.name in html assert from_user.name in plain assert from_user.name in html assert from_date in plain assert from_date in html assert to_date in plain assert to_date in html assert from_user.avatar.thumbnail_url not in plain assert from_user.avatar.thumbnail_url in html assert f"{config['BASE_URL']}/messages/hosting/" in plain assert f"{config['BASE_URL']}/messages/hosting/" in html
def test_host_request_email(db): with session_scope(db) as session: from_user, api_token_from = generate_user(db) to_user, api_token_to = generate_user(db) from_date = "2020-01-01" to_date = "2020-01-05" conversation = Conversation() message = Message() message.conversation_id = conversation.id message.author_id = from_user.id message.text = random_hex(64) host_request = HostRequest( conversation_id=conversation.id, from_user=from_user, to_user=to_user, from_date=from_date, to_date=to_date, status=HostRequestStatus.pending, from_last_seen_message_id=message.id, ) message_id = random_hex(64) @create_autospec def mock_send_email(sender_name, sender_email, recipient, subject, plain, html): assert recipient == to_user.email assert "host request" in subject.lower() assert to_user.name in plain assert to_user.name in html assert from_user.name in plain assert from_user.name in html assert from_date in plain assert from_date in html assert to_date in plain assert to_date in html assert from_user.avatar_url not in plain assert from_user.avatar_url in html assert f"{config['BASE_URL']}/hostrequests/" in plain assert f"{config['BASE_URL']}/hostrequests/" in html return message_id with patch("couchers.email.send_email", mock_send_email) as mock: send_host_request_email(host_request) assert mock.call_count == 1
def CreateHostRequest(self, request, context): with session_scope(self._Session) as session: if request.to_user_id == context.user_id: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.CANT_REQUEST_SELF) # just to check the host exists host = session.query(User).filter(User.id == request.to_user_id).one_or_none() if not host: context.abort(grpc.StatusCode.NOT_FOUND, errors.USER_NOT_FOUND) if not is_valid_date(request.from_date) or not is_valid_date(request.to_date): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_DATE) if request.from_date >= request.to_date: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_TO) if request.to_date < least_current_date(): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_TO_BEFORE_TODAY) conversation = Conversation() session.add(conversation) session.flush() created_message = Message() created_message.conversation_id = conversation.id created_message.author_id = context.user_id created_message.message_type = MessageType.chat_created session.add(created_message) session.commit() message = Message() message.conversation_id = conversation.id message.author_id = context.user_id message.text = request.text message.message_type = MessageType.text session.add(message) session.flush() host_request = HostRequest() host_request.conversation_id = conversation.id host_request.from_user_id = context.user_id host_request.to_user_id = host.id host_request.from_date = request.from_date host_request.to_date = request.to_date host_request.status = HostRequestStatus.pending host_request.from_last_seen_message_id = message.id session.add(host_request) return requests_pb2.CreateHostRequestRes(host_request_id=host_request.conversation_id)
def create_host_request(session, from_user_id, to_user_id, host_request_age=timedelta(days=15), status=HostRequestStatus.confirmed): """ Create a host request that's `host_request_age` old """ from_date = today() - host_request_age - timedelta(days=2) to_date = today() - host_request_age fake_created = now() - host_request_age - timedelta(days=3) conversation = Conversation() session.add(conversation) session.flush() session.add( Message( time=fake_created + timedelta(seconds=1), conversation_id=conversation.id, author_id=from_user_id, message_type=MessageType.chat_created, )) message = Message( time=fake_created + timedelta(seconds=2), conversation_id=conversation.id, author_id=from_user_id, text="Hi, I'm requesting to be hosted.", message_type=MessageType.text, ) session.add(message) session.flush() host_request = HostRequest( conversation_id=conversation.id, from_user_id=from_user_id, to_user_id=to_user_id, from_date=from_date, to_date=to_date, status=status, from_last_seen_message_id=message.id, ) session.add(host_request) session.commit() # send_host_request_email(host_request) return host_request.conversation_id
def CreateGroupChat(self, request, context): with session_scope() as session: recipient_user_ids = [ user_id for user_id in ( session.execute( select(User.id).where_users_visible(context).where(User.id.in_(request.recipient_user_ids)) ) .scalars() .all() ) ] # make sure all requested users are visible if len(recipient_user_ids) != len(request.recipient_user_ids): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.USER_NOT_FOUND) if not recipient_user_ids: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.NO_RECIPIENTS) if len(recipient_user_ids) != len(set(recipient_user_ids)): # make sure there's no duplicate users context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_RECIPIENTS) if context.user_id in recipient_user_ids: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.CANT_ADD_SELF) if len(recipient_user_ids) == 1: # can only have one DM at a time between any two users other_user_id = recipient_user_ids[0] # the following sql statement selects subscriptions that are DMs and have the same group_chat_id, and have # user_id either this user or the recipient user. If you find two subscriptions to the same DM group # chat, you know they already have a shared group chat count = func.count(GroupChatSubscription.id).label("count") if session.execute( select(count) .where( or_( GroupChatSubscription.user_id == context.user_id, GroupChatSubscription.user_id == other_user_id, ) ) .where(GroupChatSubscription.left == None) .join(GroupChat, GroupChat.conversation_id == GroupChatSubscription.group_chat_id) .where(GroupChat.is_dm == True) .group_by(GroupChatSubscription.group_chat_id) .having(count == 2) ).scalar_one_or_none(): context.abort( grpc.StatusCode.FAILED_PRECONDITION, "You already have a direct message chat with this user." ) conversation = Conversation() session.add(conversation) group_chat = GroupChat( conversation=conversation, title=request.title.value, creator_id=context.user_id, is_dm=True if len(recipient_user_ids) == 1 else False, # TODO ) session.add(group_chat) your_subscription = GroupChatSubscription( user_id=context.user_id, group_chat=group_chat, role=GroupChatRole.admin, ) session.add(your_subscription) for recipient_id in request.recipient_user_ids: subscription = GroupChatSubscription( user_id=recipient_id, group_chat=group_chat, role=GroupChatRole.participant, ) session.add(subscription) _add_message_to_subscription(session, your_subscription, message_type=MessageType.chat_created) session.flush() return conversations_pb2.GroupChat( group_chat_id=group_chat.conversation_id, title=group_chat.title, member_user_ids=_get_visible_members_for_subscription(your_subscription), admin_user_ids=_get_visible_admins_for_subscription(your_subscription), only_admins_invite=group_chat.only_admins_invite, is_dm=group_chat.is_dm, created=Timestamp_from_datetime(group_chat.conversation.created), mute_info=_mute_info(your_subscription), )
def CreateGroupChat(self, request, context): if len(request.recipient_user_ids) < 1: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.NO_RECIPIENTS) if len(request.recipient_user_ids) != len( set(request.recipient_user_ids)): # make sure there's no duplicate users context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_RECIPIENTS) if context.user_id in request.recipient_user_ids: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.CANT_ADD_SELF) with session_scope(self._Session) as session: if len(request.recipient_user_ids) == 1: # can only have one DM at a time between any two users other_user_id = request.recipient_user_ids[0] # the following query selects subscriptions that are DMs and have the same group_chat_id, and have # user_id either this user or the recipient user. If you find two subscriptions to the same DM group # chat, you know they already have a shared group chat count = func.count(GroupChatSubscription.id).label("count") if (session.query(count).filter( or_( GroupChatSubscription.user_id == context.user_id, GroupChatSubscription.user_id == other_user_id, )).filter(GroupChatSubscription.left == None).join( GroupChat, GroupChat.conversation_id == GroupChatSubscription.group_chat_id). filter(GroupChat.is_dm == True).group_by( GroupChatSubscription.group_chat_id).having( count == 2).one_or_none()): context.abort( grpc.StatusCode.FAILED_PRECONDITION, "You already have a direct message chat with this user." ) conversation = Conversation() session.add(conversation) group_chat = GroupChat( conversation=conversation, title=request.title.value, creator_id=context.user_id, is_dm=True if len(request.recipient_user_ids) == 1 else False, # TODO ) session.add(group_chat) your_subscription = GroupChatSubscription( user_id=context.user_id, group_chat=group_chat, role=GroupChatRole.admin, ) session.add(your_subscription) for recipient in request.recipient_user_ids: if get_friends_status( session, context.user_id, recipient) != api_pb2.User.FriendshipStatus.FRIENDS: if len(request.recipient_user_ids) > 1: context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.GROUP_CHAT_ONLY_ADD_FRIENDS) else: context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.DIRECT_MESSAGE_ONLY_FRIENDS) subscription = GroupChatSubscription( user_id=recipient, group_chat=group_chat, role=GroupChatRole.participant, ) session.add(subscription) _add_message_to_subscription(session, your_subscription, message_type=MessageType.chat_created) session.flush() return conversations_pb2.GroupChat( group_chat_id=group_chat.conversation_id, title=group_chat.title, member_user_ids=_get_visible_members_for_subscription( your_subscription), admin_user_ids=_get_visible_admins_for_subscription( your_subscription), only_admins_invite=group_chat.only_admins_invite, is_dm=group_chat.is_dm, created=Timestamp_from_datetime( group_chat.conversation.created), )
def add_dummy_users(): try: logger.info(f"Adding dummy users") with session_scope() as session: with open("src/data/dummy_users.json", "r") as file: data = json.loads(file.read()) for user in data["users"]: new_user = User( username=user["username"], email=user["email"], hashed_password=hash_password(user["password"]) if user["password"] else None, name=user["name"], city=user["location"]["city"], geom=create_coordinate(user["location"]["lat"], user["location"]["lng"]), geom_radius=user["location"]["radius"], verification=user["verification"], community_standing=user["community_standing"], birthdate=date(year=user["birthdate"]["year"], month=user["birthdate"]["month"], day=user["birthdate"]["day"]), gender=user["gender"], languages="|".join(user["languages"]), occupation=user["occupation"], about_me=user["about_me"], about_place=user["about_place"], countries_visited="|".join(user["countries_visited"]), countries_lived="|".join(user["countries_lived"]), hosting_status=hostingstatus2sql[HostingStatus.Value( user["hosting_status"])] if "hosting_status" in user else None, ) session.add(new_user) session.commit() for username1, username2 in data["friendships"]: friend_relationship = FriendRelationship( from_user_id=get_user_by_field(session, username1).id, to_user_id=get_user_by_field(session, username2).id, status=FriendStatus.accepted, ) session.add(friend_relationship) session.commit() for reference in data["references"]: reference_type = (ReferenceType.HOSTED if reference["type"] == "hosted" else (ReferenceType.SURFED if reference["type"] == "surfed" else ReferenceType.FRIEND)) new_reference = Reference( from_user_id=get_user_by_field(session, reference["from"]).id, to_user_id=get_user_by_field(session, reference["to"]).id, reference_type=reference_type, text=reference["text"], rating=reference["rating"], was_safe=reference["was_safe"], ) session.add(new_reference) session.commit() for group_chat in data["group_chats"]: # Create the chat creator = group_chat["creator"] conversation = Conversation() session.add(conversation) chat = GroupChat( conversation=conversation, title=group_chat["title"], creator_id=get_user_by_field(session, creator).id, is_dm=group_chat["is_dm"], ) session.add(chat) for participant in group_chat["participants"]: subscription = GroupChatSubscription( user_id=get_user_by_field(session, participant["username"]).id, group_chat=chat, role=GroupChatRole.admin if participant["username"] == creator else GroupChatRole.participant, joined=parser.isoparse(participant["joined"]), ) session.add(subscription) for message in group_chat["messages"]: session.add( Message( message_type=MessageType.text, conversation=chat.conversation, author_id=get_user_by_field( session, message["author"]).id, time=parser.isoparse(message["time"]), text=message["message"], )) session.commit() except IntegrityError: logger.error("Failed to insert dummy users, is it already inserted?")
def CreateHostRequest(self, request, context): with session_scope() as session: if request.to_user_id == context.user_id: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.CANT_REQUEST_SELF) # just to check the host exists host = session.query(User).filter( User.id == request.to_user_id).one_or_none() if not host: context.abort(grpc.StatusCode.NOT_FOUND, errors.USER_NOT_FOUND) from_date = parse_date(request.from_date) to_date = parse_date(request.to_date) if not from_date or not to_date: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_DATE) today = today_in_timezone(host.timezone) # request starts from the past if from_date < today: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_BEFORE_TODAY) # from_date is not >= to_date if from_date >= to_date: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_TO) # No need to check today > to_date if from_date - today > timedelta(days=365): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_ONE_YEAR) if to_date - from_date > timedelta(days=365): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_TO_AFTER_ONE_YEAR) conversation = Conversation() session.add(conversation) session.flush() session.add( Message( conversation_id=conversation.id, author_id=context.user_id, message_type=MessageType.chat_created, )) message = Message( conversation_id=conversation.id, author_id=context.user_id, text=request.text, message_type=MessageType.text, ) session.add(message) session.flush() host_request = HostRequest( conversation_id=conversation.id, from_user_id=context.user_id, to_user_id=host.id, from_date=from_date, to_date=to_date, status=HostRequestStatus.pending, from_last_seen_message_id=message.id, # TODO: tz # timezone=host.timezone, ) session.add(host_request) session.flush() send_host_request_email(host_request) return requests_pb2.CreateHostRequestRes( host_request_id=host_request.conversation_id)
def add_dummy_users(): logger.info(f"Adding dummy users") with session_scope() as session: if session.execute(select( func.count()).select_from(User)).scalar_one() > 0: logger.info("Users not empty, not adding dummy users") return with open(SRC_DIR + "/data/dummy_users.json", "r") as f: data = json.loads(f.read()) for user in data["users"]: new_user = User( username=user["username"], email=user["email"], hashed_password=hash_password(user["password"]) if user["password"] else None, name=user["name"], city=user["location"]["city"], geom=create_coordinate(user["location"]["lat"], user["location"]["lng"]), geom_radius=user["location"]["radius"], community_standing=user["community_standing"], birthdate=date(year=user["birthdate"]["year"], month=user["birthdate"]["month"], day=user["birthdate"]["day"]), gender=user["gender"], occupation=user["occupation"], about_me=user["about_me"], about_place=user["about_place"], hosting_status=hostingstatus2sql[HostingStatus.Value( user["hosting_status"] if "hosting_status" in user else "HOSTING_STATUS_CANT_HOST")], new_notifications_enabled=True, accepted_tos=TOS_VERSION, accepted_community_guidelines=GUIDELINES_VERSION, ) session.add(new_user) session.flush() for language in user["languages"]: session.add( LanguageAbility(user_id=new_user.id, language_code=language[0], fluency=LanguageFluency[language[1]])) for region in user["regions_visited"]: session.add( RegionVisited(user_id=new_user.id, region_code=region)) for region in user["regions_lived"]: session.add( RegionLived(user_id=new_user.id, region_code=region)) session.commit() for username1, username2 in data["friendships"]: friend_relationship = FriendRelationship( from_user_id=session.execute( select(User).where( User.username == username1)).scalar_one().id, to_user_id=session.execute( select(User).where( User.username == username2)).scalar_one().id, status=FriendStatus.accepted, ) session.add(friend_relationship) session.commit() for reference in data["references"]: reference_type = (ReferenceType.hosted if reference["type"] == "hosted" else (ReferenceType.surfed if reference["type"] == "surfed" else ReferenceType.friend)) new_reference = Reference( from_user_id=session.execute( select(User).where( User.username == reference["from"])).scalar_one().id, to_user_id=session.execute( select(User).where( User.username == reference["to"])).scalar_one().id, reference_type=reference_type, text=reference["text"], rating=reference["rating"], was_appropriate=reference["was_appropriate"], ) session.add(new_reference) session.commit() for group_chat in data["group_chats"]: # Create the chat creator = group_chat["creator"] conversation = Conversation() session.add(conversation) chat = GroupChat( conversation=conversation, title=group_chat["title"], creator_id=session.execute( select(User).where( User.username == creator)).scalar_one().id, is_dm=group_chat["is_dm"], ) session.add(chat) for participant in group_chat["participants"]: subscription = GroupChatSubscription( user_id=session.execute( select(User).where( User.username == participant["username"])).scalar_one().id, group_chat=chat, role=GroupChatRole.admin if participant["username"] == creator else GroupChatRole.participant, joined=parser.isoparse(participant["joined"]), ) session.add(subscription) for message in group_chat["messages"]: session.add( Message( message_type=MessageType.text, conversation=chat.conversation, author_id=session.execute( select(User).where( User.username == message["author"])).scalar_one().id, time=parser.isoparse(message["time"]), text=message["message"], )) session.commit()
def CreateHostRequest(self, request, context): with session_scope() as session: if request.to_user_id == context.user_id: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.CANT_REQUEST_SELF) # just to check the host exists host = session.query(User).filter( User.id == request.to_user_id).one_or_none() if not host: context.abort(grpc.StatusCode.NOT_FOUND, errors.USER_NOT_FOUND) if not is_valid_date(request.from_date) or not is_valid_date( request.to_date): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_DATE) # today is not > from_date if least_current_date() > request.from_date: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_BEFORE_TODAY) # from_date is not >= to_date if request.from_date >= request.to_date: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_TO) # No need to check today > to_date today = date.fromisoformat(largest_current_date()) today_plus_one_year = today.replace(year=today.year + 1).isoformat() if request.from_date > today_plus_one_year: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_ONE_YEAR) from_date = date.fromisoformat(request.from_date) from_date_plus_one_year = (from_date.replace(year=from_date.year + 1)).isoformat() if request.to_date > from_date_plus_one_year: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_TO_AFTER_ONE_YEAR) conversation = Conversation() session.add(conversation) session.flush() created_message = Message() created_message.conversation_id = conversation.id created_message.author_id = context.user_id created_message.message_type = MessageType.chat_created session.add(created_message) session.commit() message = Message() message.conversation_id = conversation.id message.author_id = context.user_id message.text = request.text message.message_type = MessageType.text session.add(message) session.flush() host_request = HostRequest() host_request.conversation_id = conversation.id host_request.from_user_id = context.user_id host_request.to_user_id = host.id host_request.from_date = request.from_date host_request.to_date = request.to_date host_request.status = HostRequestStatus.pending host_request.from_last_seen_message_id = message.id session.add(host_request) session.flush() send_host_request_email(host_request) return requests_pb2.CreateHostRequestRes( host_request_id=host_request.conversation_id)
def CreateHostRequest(self, request, context): with session_scope() as session: if request.host_user_id == context.user_id: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.CANT_REQUEST_SELF) # just to check host exists and is visible host = session.execute( select(User).where_users_visible(context).where( User.id == request.host_user_id)).scalar_one_or_none() if not host: context.abort(grpc.StatusCode.NOT_FOUND, errors.USER_NOT_FOUND) from_date = parse_date(request.from_date) to_date = parse_date(request.to_date) if not from_date or not to_date: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_DATE) today = today_in_timezone(host.timezone) # request starts from the past if from_date < today: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_BEFORE_TODAY) # from_date is not >= to_date if from_date >= to_date: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_TO) # No need to check today > to_date if from_date - today > timedelta(days=365): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_ONE_YEAR) if to_date - from_date > timedelta(days=365): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_TO_AFTER_ONE_YEAR) conversation = Conversation() session.add(conversation) session.flush() session.add( Message( conversation_id=conversation.id, author_id=context.user_id, message_type=MessageType.chat_created, )) message = Message( conversation_id=conversation.id, author_id=context.user_id, text=request.text, message_type=MessageType.text, ) session.add(message) session.flush() host_request = HostRequest( conversation_id=conversation.id, surfer_user_id=context.user_id, host_user_id=host.id, from_date=from_date, to_date=to_date, status=HostRequestStatus.pending, surfer_last_seen_message_id=message.id, # TODO: tz # timezone=host.timezone, ) session.add(host_request) session.commit() send_new_host_request_email(host_request) notify( user_id=host_request.host_user_id, topic="host_request", action="create", key=str(host_request.surfer_user_id), avatar_key=host_request.surfer.avatar.thumbnail_url if host_request.surfer.avatar else None, title= f"**{host_request.surfer.name}** sent you a hosting request", content=request.text, link=urls.host_request_link_host(), ) return requests_pb2.CreateHostRequestRes( host_request_id=host_request.conversation_id)