Coverage for src / mesh / models / roles / author.py: 96%

70 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-05-04 12:41 +0000

1from typing import ClassVar 

2 

3# from django.urls import reverse_lazy 

4from django.db.models import Value 

5from django.utils.translation import gettext as _ 

6from opentelemetry import trace 

7 

8from mesh.app_settings import BlindMode, app_settings 

9from mesh.models.orm.review_models import Review 

10from mesh.models.orm.submission_models import Submission, SubmissionState, SubmissionVersion 

11 

12# from ..views.components.button import Button 

13from mesh.models.roles.base_role import Role 

14from mesh.models.submission_status import SubmissionStatus, SubmissionStatusData 

15 

16tracer = trace.get_tracer(__name__) 

17 

18 

19class Author(Role): 

20 """ 

21 Author role. 

22 This is the base role of mesh app, always active. 

23 """ 

24 

25 _CODE: ClassVar[str] = "author" 

26 _NAME: ClassVar[str] = _("Author") 

27 _ICON_CLASS: ClassVar[str] = "fa-pen-nib" 

28 _SUBMISSION_LIST_TITLE: ClassVar[str] = _("My submissions") 

29 

30 @tracer.start_as_current_span("Author.__init__") 

31 def __init__(self, user) -> None: 

32 super().__init__(user) 

33 self.reviews_queryset = Review.objects.filter( 

34 version__review_open=False, 

35 submitted=True, 

36 accepted=True, 

37 recommendation__isnull=False, 

38 additional_files__author_access=True, 

39 ) 

40 

41 if app_settings.BLIND_MODE != BlindMode.NO_BLIND: 

42 self.reviewer_censored_annotate = Value("****, ****") 

43 

44 self.versions_queryset = SubmissionVersion.objects.all() 

45 self.submissions_queryset = Submission.objects.filter(created_by=self.user) 

46 

47 def _get_is_active(self): 

48 return True 

49 

50 def get_submissions(self): 

51 return self._annotate_submission_query(self.submissions_queryset).annotate( 

52 user_is_editor=Value(False) 

53 ) 

54 

55 def can_access_submission(self, submission): 

56 return submission.created_by == self.user 

57 

58 def can_edit_submission(self, submission): 

59 result = self.can_access_submission(submission) 

60 result = ( 

61 result 

62 and submission.is_draft 

63 or submission.state == SubmissionState.REVISION_REQUESTED.value 

64 ) 

65 return result 

66 

67 def can_create_submission(self): 

68 return True 

69 

70 def can_create_version(self, submission): 

71 if not self.can_access_submission(submission): 

72 return False 

73 current_version = submission.versions.first() 

74 # The author must have entered at least 1 submission author to proceed 

75 # with the submission version. 

76 if submission.is_draft and current_version is None: 

77 return submission.authors.exists() 

78 return ( 

79 submission.state == SubmissionState.REVISION_REQUESTED.value 

80 and current_version is not None 

81 and current_version.submitted 

82 ) 

83 

84 def can_access_version(self, version): 

85 return self.can_access_submission(version.submission) 

86 

87 def can_edit_version(self, version): 

88 return not version.submitted and self.can_access_submission(version.submission) 

89 

90 def can_access_reviews(self, version): 

91 """ 

92 An author can access reviews when the review process is closed. 

93 """ 

94 return not version.review_open and self.can_access_submission(version.submission) 

95 

96 def can_access_review(self, submission, review): 

97 """ 

98 An author can access review with at least one accessible file. 

99 """ 

100 return ( 

101 self.can_access_reviews(review.version) 

102 and review.submitted 

103 and review.recommendation is not None 

104 and any(self.can_access_review_file(f) for f in review.additional_files.all()) 

105 ) 

106 

107 def can_access_review_file(self, file): 

108 return file.author_access 

109 

110 def get_submission_status(self, submission): 

111 """ 

112 For an author, the submission status is one of the following: 

113 - "Submission is still in draft", True - when STATUS_OPENED 

114 - "A new version must be submitted", True - when STATUS_REVISIONS_REQUESTED 

115 - "Submission accepted", False - when STATUS_ACCEPTED 

116 - "Submission declined", False - when STATUS_DECLINED 

117 - "Waiting for editorial actions" - when STATUS_SUBMITTED or STATUS_REVISIONS_SUBMITTED 

118 - "Under review", False - For other state 

119 """ 

120 # Should not happen ? 

121 if not self.can_access_submission(submission): 

122 return SubmissionStatusData( 

123 submission=submission, status=SubmissionStatus.ERROR, description="" 

124 ) 

125 

126 if submission.state in [ 

127 SubmissionState.SUBMITTED.value, 

128 SubmissionState.REVISION_SUBMITTED.value, 

129 ]: 

130 return SubmissionStatusData( 

131 submission=submission, 

132 status=SubmissionStatus.WAITING, 

133 description=_("Under review"), 

134 ) 

135 elif submission.state == SubmissionState.OPENED.value: 

136 status_data = SubmissionStatusData( 

137 submission=submission, 

138 status=SubmissionStatus.TODO, 

139 description=_("Complete your submission"), 

140 ) 

141 return status_data 

142 

143 elif submission.state == SubmissionState.REVISION_REQUESTED.value: 

144 status_data = SubmissionStatusData( 

145 submission=submission, 

146 status=SubmissionStatus.TODO, 

147 description=_("Submit a new version"), 

148 ) 

149 # if submission.state == SubmissionState.OPENED.value and self.can_submit_submission( 

150 # submission 

151 # ): 

152 # status_data.shortcut_actions.append( 

153 # Button( 

154 # id=f"submission-submit-{submission.pk}", 

155 # title=_("Resume submission"), 

156 # icon_class="fa-list-check", 

157 # attrs={ 

158 # "href": [ 

159 # reverse_lazy( 

160 # "mesh:submission_resume", kwargs={"pk": submission.pk} 

161 # ) 

162 # ], 

163 # "class": ["as-button"], 

164 # }, 

165 # ) 

166 # ) 

167 # elif ( 

168 # submission.state == SubmissionState.REVISION_REQUESTED.value 

169 # and self.can_create_version(submission) 

170 # ): 

171 # status_data.shortcut_actions.append( 

172 # Button( 

173 # id=f"submission-submit-{submission.pk}", 

174 # title=_("Submit revisions"), 

175 # icon_class="fa-plus", 

176 # attrs={ 

177 # "href": [ 

178 # reverse_lazy( 

179 # "mesh:submission_version_create", 

180 # kwargs={"submission_pk": submission.pk}, 

181 # ) 

182 # ], 

183 # "class": ["as-button"], 

184 # }, 

185 # ) 

186 # ) 

187 # elif ( 

188 # submission.state == SubmissionState.REVISION_REQUESTED.value 

189 # and submission.current_version 

190 # and not submission.current_version.submitted 

191 # ): 

192 # status_data.shortcut_actions.append( 

193 # Button( 

194 # id=f"submission-submit-{submission.pk}", 

195 # title=_("Resume revisions"), 

196 # icon_class="fa-list-check", 

197 # attrs={ 

198 # "href": [ 

199 # reverse_lazy( 

200 # "mesh:submission_version_update", 

201 # kwargs={"pk": submission.current_version.pk}, 

202 # ) 

203 # ], 

204 # "class": ["as-button"], 

205 # }, 

206 # ) 

207 # ) 

208 return status_data 

209 

210 elif submission.state == SubmissionState.ON_REVIEW.value: 

211 return SubmissionStatusData( 

212 submission=submission, 

213 status=SubmissionStatus.WAITING, 

214 description=_("Submission is under review"), 

215 ) 

216 elif submission.state in [ 

217 SubmissionState.ACCEPTED.value, 

218 SubmissionState.REJECTED.value, 

219 ]: 

220 return SubmissionStatusData( 

221 submission=submission, 

222 status=SubmissionStatus.DONE, 

223 description=_("Submission") + " " + submission.get_state_display(), 

224 ) 

225 

226 return SubmissionStatusData( 

227 submission=submission, status=SubmissionStatus.ERROR, description="" 

228 ) 

229 

230 def can_access_last_activity(self) -> bool: 

231 return False