Coverage for app / backend / src / couchers / models / public_trips.py: 100%

25 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-26 17:16 +0000

1import enum 

2from datetime import date, datetime 

3from typing import TYPE_CHECKING 

4 

5from sqlalchemy import BigInteger, Boolean, CheckConstraint, Date, DateTime, Enum, ForeignKey, Index, String, func 

6from sqlalchemy.orm import Mapped, mapped_column, relationship 

7from sqlalchemy.sql import expression 

8 

9from couchers.models.base import Base 

10 

11if TYPE_CHECKING: 

12 from couchers.models import Node, User 

13 from couchers.models.host_requests import HostRequest 

14 

15 

16class PublicTripStatus(enum.Enum): 

17 searching_for_host = enum.auto() 

18 closed = enum.auto() 

19 

20 

21class PublicTrip(Base, kw_only=True): 

22 """ 

23 A public trip posted by a traveler looking for a host in a community. 

24 """ 

25 

26 __tablename__ = "public_trips" 

27 

28 id: Mapped[int] = mapped_column(BigInteger, primary_key=True, init=False) 

29 

30 # The traveler posting the trip 

31 user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True) 

32 

33 # The community/location (city-level node) 

34 node_id: Mapped[int] = mapped_column(ForeignKey("nodes.id"), index=True) 

35 

36 # Trip dates 

37 from_date: Mapped[date] = mapped_column(Date) 

38 to_date: Mapped[date] = mapped_column(Date) 

39 

40 # User's message about their trip 

41 description: Mapped[str] = mapped_column(String) 

42 

43 # Current status 

44 status: Mapped[PublicTripStatus] = mapped_column( 

45 Enum(PublicTripStatus), default=PublicTripStatus.searching_for_host 

46 ) 

47 

48 # If true, only users with the same gender as the poster can see this trip 

49 same_gender_only: Mapped[bool] = mapped_column(Boolean, default=False, server_default=expression.false()) 

50 

51 # Timestamps 

52 created: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), init=False) 

53 

54 # Relationships 

55 user: Mapped[User] = relationship(init=False, back_populates="public_trips") 

56 node: Mapped[Node] = relationship(init=False, back_populates="public_trips") 

57 host_requests: Mapped[list[HostRequest]] = relationship(init=False, back_populates="public_trip") 

58 

59 __table_args__ = ( 

60 # Ensure from_date is not after to_date 

61 CheckConstraint("from_date <= to_date", name="valid_date_range"), 

62 # Index for querying active trips in a community 

63 # Using partial index since we mostly query for active trips 

64 Index( 

65 "ix_public_trips_node_from_date_active", 

66 node_id, 

67 from_date, 

68 postgresql_where=status == PublicTripStatus.searching_for_host, 

69 ), 

70 )