Coverage for src / mesh / crud / discussion.py: 21%

105 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-10-22 07:20 +0000

1from datetime import timedelta 

2 

3from django.utils import timezone 

4 

5from mesh.crud.comments import submission_roles 

6from mesh.models import User 

7from mesh.models.orm.comment_models import Comment 

8from mesh.models.orm.discussion_models import Discussion, DiscussionRead 

9from mesh.models.orm.inbox_model import Inbox 

10from mesh.models.roles.base_role import Role 

11from mesh.views.components.button import build_comment_actions 

12from mesh.views.components.ckeditor_config import sanitize_html_input 

13from mesh.views.components.icon import build_icon 

14 

15 

16def role_icon(role: str): 

17 role_icon = ( 

18 (role == "editor") * "fa-newspaper" 

19 + (role == "author") * "fa-user-pen" 

20 + (role == "reviewer") * "fa-microscope" 

21 ) 

22 return role_icon 

23 

24 

25def create_comment(discussion: Discussion, content, user, parent=None): 

26 def role_of(user: User, discussion: Discussion): 

27 roles = submission_roles(user=user, submission=discussion.submission) 

28 for r in ("editor", "reviewer", "author"): 

29 if r in roles: 

30 return r 

31 

32 comment = Comment.objects.create( 

33 discussion=discussion, 

34 raw_html=content, 

35 sanitized_html=sanitize_html_input(content), 

36 author=user, 

37 parent=parent, 

38 role=role_of(user=user, discussion=discussion), 

39 ) 

40 update_date_last_seen(discussion=discussion, user=user) 

41 add_inbox_item(comment, comment_user=user) 

42 

43 

44def edit_comment(discussion: Discussion, user: User, content, comment_pk): 

45 comment = Comment.objects.get( 

46 pk=comment_pk, 

47 discussion=discussion, 

48 ) 

49 comment.raw_html = content 

50 comment.sanitized_html = sanitize_html_input(content) 

51 comment.save() 

52 update_date_last_seen(discussion=discussion, user=user) 

53 add_inbox_item(comment, comment_user=user) 

54 

55 

56def get_comment(discussion: Discussion, comment_pk): 

57 return Comment.objects.select_related("author", "parent").get( 

58 pk=comment_pk, discussion=discussion 

59 ) 

60 

61 

62def get_comments(discussion: Discussion, user: User, role: Role, nesting: bool = True): 

63 comments = Comment.objects.filter(discussion=discussion, is_deleted=False) 

64 discussion_read, _created = DiscussionRead.objects.get_or_create( 

65 discussion=discussion, user=user 

66 ) 

67 date_last_seen = discussion_read.date_last_seen 

68 

69 def has_seen(comment): 

70 if date_last_seen is None: 

71 return False 

72 return date_last_seen >= comment.date_last_modified 

73 

74 def entry_of(comment: Comment): 

75 role_str = comment.role 

76 return { 

77 "content": comment, 

78 "role": role_str, 

79 "role_icon": comment.role_icon_class, 

80 "icon": build_icon(comment.role_icon_class, icon_attrs={"data-tooltip": [role_str]}), 

81 "role_color": comment.role_color, 

82 "current_user_comment": comment.author == user, 

83 "can_edit": role.can_edit_comment(comment), 

84 "has_seen": has_seen(comment), 

85 "comment_actions": build_comment_actions(comment, user), 

86 } 

87 

88 ordered = [entry_of(comment) for comment in comments] 

89 return ordered 

90 

91 

92def update_date_last_seen(discussion: Discussion, user: User, date_last_seen=None): 

93 if date_last_seen is None: 

94 date_last_seen = timezone.now() 

95 user_discussion_read = DiscussionRead.objects.get(discussion=discussion, user=user) 

96 user_discussion_read.date_last_seen = date_last_seen 

97 user_discussion_read.save() 

98 

99 

100def add_inbox_item(comment: Comment, comment_user: User): 

101 discussion_users = comment.discussion.members.all() 

102 for user in discussion_users: 

103 if user != comment_user: 

104 user_inbox, _ = Inbox.objects.get_or_create(user=user) 

105 user_inbox.comments.add(comment) 

106 

107 

108def remove_inbox_item(user: User, comment: Comment = None, discussion: Discussion = None): 

109 try: 

110 inbox = Inbox.objects.get(user=user) 

111 except Inbox.DoesNotExist: 

112 return 

113 

114 def remove_inbox_comment(ibx: Inbox, com: Comment): 

115 inbox.comments.remove(com) 

116 if not ibx.comments.exists(): 

117 ibx.delete() 

118 

119 if discussion: 

120 comments = inbox.comments.filter(discussion=discussion) 

121 [remove_inbox_comment(ibx=inbox, com=com) for com in comments] 

122 

123 if comment: 

124 remove_inbox_comment(ibx=inbox, com=comment) 

125 

126 

127def delete_comment(discussion: Discussion, comment: Comment, user: User, role: Role): 

128 comment.is_deleted = True 

129 comment.save(update_fields=["is_deleted"]) 

130 comment.refresh_from_db() 

131 

132 # delete responses 

133 discussion_comments = get_comments(discussion=discussion, user=user, role=role) 

134 for child_comment in discussion_comments: 

135 child_comment = child_comment["content"] 

136 if child_comment.parent and child_comment.parent.pk == comment.pk: 

137 child_comment.is_deleted = True 

138 child_comment.save(update_fields=["is_deleted"]) 

139 child_comment.refresh_from_db() 

140 return True 

141 

142 

143def get_inbox_comments(user: User): 

144 def get_comment_preview(comment: Comment): 

145 role_str = comment.role 

146 

147 return { 

148 "content": comment, 

149 "role": role_str, 

150 "role_icon": comment.role_icon_class, 

151 "submission_name": comment.discussion.submission.name, 

152 "discussion_title": comment.discussion.title, 

153 "submission_pk": comment.discussion.submission.pk, 

154 "discussion_pk": comment.discussion.pk, 

155 } 

156 

157 try: 

158 user_inbox = Inbox.objects.get(user=user) 

159 comments = user_inbox.comments.all() 

160 ordered_comments = [get_comment_preview(comment) for comment in comments[::-1]] 

161 return ordered_comments 

162 except Inbox.DoesNotExist: 

163 return None 

164 

165 

166def make_discussion_unread(discussion: Discussion, user: User, days_to_subtract=1): 

167 discussion_last_comment = ( 

168 Comment.objects.filter(discussion=discussion).order_by("date_last_modified").first() 

169 ) 

170 

171 if discussion_last_comment is None: 

172 return 

173 

174 # mark discussion last comment as unreaden 

175 discussion_last_comment_date = discussion_last_comment.date_last_modified 

176 update_date_last_seen( 

177 discussion=discussion, 

178 user=user, 

179 date_last_seen=discussion_last_comment_date - timedelta(days=days_to_subtract), 

180 ) 

181 

182 # add discussion last comment to inbox 

183 user_inbox, inbox_created = Inbox.objects.get_or_create(user=user) 

184 if inbox_created: 

185 return 

186 user_inbox.comments.add(discussion_last_comment) 

187 user_inbox.save()