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

59 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-12-25 10:58 +0000

1from datetime import datetime 

2 

3from sqlalchemy import BigInteger, DateTime, ForeignKey, String, UniqueConstraint, func 

4from sqlalchemy.orm import Mapped, backref, column_property, mapped_column, relationship 

5 

6from couchers.models.base import Base, communities_seq 

7 

8 

9class Discussion(Base): 

10 """ 

11 forum board 

12 """ 

13 

14 __tablename__ = "discussions" 

15 

16 id: Mapped[int] = mapped_column( 

17 BigInteger, communities_seq, primary_key=True, server_default=communities_seq.next_value() 

18 ) 

19 

20 title: Mapped[str] = mapped_column(String) 

21 content: Mapped[str] = mapped_column(String) 

22 thread_id: Mapped[int] = mapped_column(ForeignKey("threads.id"), unique=True) 

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

24 

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

26 owner_cluster_id: Mapped[int] = mapped_column(ForeignKey("clusters.id"), index=True) 

27 

28 slug = column_property(func.slugify(title)) 

29 

30 thread = relationship("Thread", backref="discussion", uselist=False) 

31 

32 subscribers = relationship("User", backref="discussions", secondary="discussion_subscriptions", viewonly=True) 

33 

34 creator_user = relationship("User", backref="created_discussions", foreign_keys="Discussion.creator_user_id") 

35 owner_cluster = relationship("Cluster", backref=backref("owned_discussions", lazy="dynamic"), uselist=False) 

36 

37 

38class DiscussionSubscription(Base): 

39 """ 

40 users subscriptions to discussions 

41 """ 

42 

43 __tablename__ = "discussion_subscriptions" 

44 __table_args__ = (UniqueConstraint("discussion_id", "user_id"),) 

45 

46 id: Mapped[int] = mapped_column(BigInteger, primary_key=True) 

47 

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

49 discussion_id: Mapped[int] = mapped_column(ForeignKey("discussions.id"), index=True) 

50 joined: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) 

51 left: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) 

52 

53 user = relationship("User", backref="discussion_subscriptions") 

54 discussion = relationship("Discussion", backref="discussion_subscriptions") 

55 

56 

57class Thread(Base): 

58 """ 

59 Thread 

60 """ 

61 

62 __tablename__ = "threads" 

63 

64 id: Mapped[int] = mapped_column(BigInteger, primary_key=True) 

65 

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

67 deleted: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) 

68 

69 

70class Comment(Base): 

71 """ 

72 Comment 

73 """ 

74 

75 __tablename__ = "comments" 

76 

77 id: Mapped[int] = mapped_column(BigInteger, primary_key=True) 

78 

79 thread_id: Mapped[int] = mapped_column(ForeignKey("threads.id"), index=True) 

80 author_user_id: Mapped[int] = mapped_column(ForeignKey("users.id")) 

81 content: Mapped[str] = mapped_column(String) # CommonMark without images 

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

83 deleted: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) 

84 

85 thread = relationship("Thread", backref="comments") 

86 

87 

88class Reply(Base): 

89 """ 

90 Reply 

91 """ 

92 

93 __tablename__ = "replies" 

94 

95 id: Mapped[int] = mapped_column(BigInteger, primary_key=True) 

96 

97 comment_id: Mapped[int] = mapped_column(ForeignKey("comments.id"), index=True) 

98 author_user_id: Mapped[int] = mapped_column(ForeignKey("users.id")) 

99 content: Mapped[str] = mapped_column(String) # CommonMark without images 

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

101 deleted: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) 

102 

103 comment = relationship("Comment", backref="replies") 

104 

105 

106class ClusterDiscussionAssociation(Base): 

107 """ 

108 discussions related to clusters 

109 """ 

110 

111 __tablename__ = "cluster_discussion_associations" 

112 __table_args__ = (UniqueConstraint("discussion_id", "cluster_id"),) 

113 

114 id: Mapped[int] = mapped_column(BigInteger, primary_key=True) 

115 

116 discussion_id: Mapped[int] = mapped_column(ForeignKey("discussions.id"), index=True) 

117 cluster_id: Mapped[int] = mapped_column(ForeignKey("clusters.id"), index=True) 

118 

119 discussion = relationship("Discussion", backref="cluster_discussion_associations") 

120 cluster = relationship("Cluster", backref="cluster_discussion_associations")