Coverage for src/couchers/models/activeness_probe.py: 100%

23 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-08 00:20 +0000

1import enum 

2 

3from sqlalchemy import BigInteger, CheckConstraint, Column, DateTime, Enum, ForeignKey, Index, Integer, func 

4from sqlalchemy.ext.hybrid import hybrid_property 

5from sqlalchemy.orm import relationship 

6 

7from couchers.models.base import Base 

8 

9 

10class ActivenessProbeStatus(enum.Enum): 

11 # no response yet 

12 pending = enum.auto() 

13 

14 # didn't respond on time 

15 expired = enum.auto() 

16 

17 # responded that they're still active 

18 still_active = enum.auto() 

19 

20 # responded that they're no longer active 

21 no_longer_active = enum.auto() 

22 

23 

24class ActivenessProbe(Base): 

25 """ 

26 Activeness probes are used to gauge if users are still active: we send them a notification and ask them to respond, 

27 we use this data both to help indicate response rate, as well as to make sure only those who are actively hosting 

28 show up as such. 

29 """ 

30 

31 __tablename__ = "activeness_probes" 

32 

33 id = Column(BigInteger, primary_key=True) 

34 

35 user_id = Column(ForeignKey("users.id"), nullable=False, index=True) 

36 # the time this probe was initiated 

37 probe_initiated = Column(DateTime(timezone=True), nullable=False, server_default=func.now()) 

38 # the number of reminders sent for this probe 

39 notifications_sent = Column(Integer, nullable=False, server_default="0") 

40 

41 # the time of response 

42 responded = Column(DateTime(timezone=True), nullable=True, default=None) 

43 # the response value 

44 response = Column(Enum(ActivenessProbeStatus), nullable=False, default=ActivenessProbeStatus.pending) 

45 

46 @hybrid_property 

47 def is_pending(self): 

48 return self.responded == None 

49 

50 user = relationship("User", back_populates="pending_activeness_probe") 

51 

52 __table_args__ = ( 

53 # a user can have at most one pending activeness probe at a time 

54 Index( 

55 "ix_activeness_probe_unique_pending_response", 

56 user_id, 

57 unique=True, 

58 postgresql_where=responded == None, 

59 ), 

60 # response time is none iff response is pending 

61 CheckConstraint( 

62 "(responded IS NULL AND response = 'pending') OR (responded IS NOT NULL AND response != 'pending')", 

63 name="pending_has_no_responded", 

64 ), 

65 )