Coverage for src/mesh/model/roles/author.py: 86%

74 statements  

« prev     ^ index     » next       coverage.py v7.7.0, created at 2025-04-28 07:45 +0000

1from functools import cached_property 

2from typing import ClassVar 

3 

4from django.db.models import QuerySet 

5 

6# from django.urls import reverse_lazy 

7from django.utils.translation import gettext as _ 

8 

9from mesh.app_settings import BlindMode, app_settings 

10from mesh.models.review_models import Review, ReviewAdditionalFile 

11from mesh.models.submission_models import Submission, SubmissionState, SubmissionVersion 

12from mesh.models.user_models import User 

13 

14from ..submission_status import SubmissionStatus, SubmissionStatusData 

15 

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

17from .base_role import Role, RoleRights 

18 

19 

20class AuthorRights(RoleRights): 

21 """ 

22 Rights implementation for author role. 

23 """ 

24 

25 @cached_property 

26 def submissions(self) -> QuerySet[Submission]: 

27 return Submission.objects.get_submissions().filter(created_by=self.user) 

28 

29 def can_create_submission(self) -> bool: 

30 return True 

31 

32 def can_access_submission(self, submission: Submission) -> bool: 

33 return submission in self.submissions 

34 

35 def can_edit_submission(self, submission: Submission) -> bool: 

36 result = self.can_access_submission(submission) 

37 result = ( 

38 result 

39 and submission.is_draft 

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

41 ) 

42 return result 

43 

44 def can_create_version(self, submission: Submission) -> bool: 

45 if not self.can_access_submission(submission): 45 ↛ 46line 45 didn't jump to line 46 because the condition on line 45 was never true

46 return False 

47 current_version = submission.current_version 

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

49 # with the submission version. 

50 if submission.is_draft and current_version is None: 

51 return submission.authors.exists() # type:ignore 

52 return ( 

53 submission.state == SubmissionState.REVISION_REQUESTED.value 

54 and current_version is not None 

55 and current_version.submitted 

56 ) 

57 

58 def can_access_version(self, version: SubmissionVersion) -> bool: 

59 return self.can_access_submission(version.submission) 

60 

61 def can_edit_version(self, version: SubmissionVersion) -> bool: 

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

63 

64 def can_access_submission_author(self, submission: Submission) -> bool: 

65 return self.can_access_submission(submission) 

66 

67 def can_access_reviews(self, version: SubmissionVersion) -> bool: 

68 """ 

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

70 """ 

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

72 

73 def can_access_review(self, review: Review) -> bool: 

74 """ 

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

76 """ 

77 return ( 

78 self.can_access_reviews(review.version) 

79 and review.submitted 

80 and review.recommendation is not None 

81 and any( 

82 self.can_access_review_file(f) 

83 for f in review.additional_files.all() # type:ignore 

84 ) 

85 ) 

86 

87 def can_access_review_author(self, review: Review) -> bool: 

88 return ( 

89 app_settings.BLIND_MODE == BlindMode.NO_BLIND 

90 and review.version.submission in self.submissions 

91 ) 

92 

93 def can_access_review_file(self, file: ReviewAdditionalFile) -> bool: 

94 return file.author_access 

95 

96 def get_submission_status(self, submission: Submission) -> SubmissionStatusData: 

97 """ 

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

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

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

101 - "Submission accepted", False - when STATUS_ACCEPTED 

102 - "Submission declined", False - when STATUS_DECLINED 

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

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

105 """ 

106 # Should not happen ? 

107 if not self.can_access_submission(submission): 

108 return SubmissionStatusData( 

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

110 ) 

111 

112 if submission.state in [ 

113 SubmissionState.SUBMITTED.value, 

114 SubmissionState.REVISION_SUBMITTED.value, 

115 ]: 

116 return SubmissionStatusData( 

117 submission=submission, 

118 status=SubmissionStatus.WAITING, 

119 description=_("Under review"), 

120 ) 

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

122 status_data = SubmissionStatusData( 

123 submission=submission, 

124 status=SubmissionStatus.TODO, 

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

126 ) 

127 return status_data 

128 

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

130 status_data = SubmissionStatusData( 

131 submission=submission, 

132 status=SubmissionStatus.TODO, 

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

134 ) 

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

136 # submission 

137 # ): 

138 # status_data.shortcut_actions.append( 

139 # Button( 

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

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

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

143 # attrs={ 

144 # "href": [ 

145 # reverse_lazy( 

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

147 # ) 

148 # ], 

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

150 # }, 

151 # ) 

152 # ) 

153 # elif ( 

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

155 # and self.can_create_version(submission) 

156 # ): 

157 # status_data.shortcut_actions.append( 

158 # Button( 

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

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

161 # icon_class="fa-plus", 

162 # attrs={ 

163 # "href": [ 

164 # reverse_lazy( 

165 # "mesh:submission_version_create", 

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

167 # ) 

168 # ], 

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

170 # }, 

171 # ) 

172 # ) 

173 # elif ( 

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

175 # and submission.current_version 

176 # and not submission.current_version.submitted 

177 # ): 

178 # status_data.shortcut_actions.append( 

179 # Button( 

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

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

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

183 # attrs={ 

184 # "href": [ 

185 # reverse_lazy( 

186 # "mesh:submission_version_update", 

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

188 # ) 

189 # ], 

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

191 # }, 

192 # ) 

193 # ) 

194 return status_data 

195 

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

197 return SubmissionStatusData( 

198 submission=submission, 

199 status=SubmissionStatus.WAITING, 

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

201 ) 

202 elif submission.state in [ 202 ↛ 212line 202 didn't jump to line 212 because the condition on line 202 was always true

203 SubmissionState.ACCEPTED.value, 

204 SubmissionState.REJECTED.value, 

205 ]: 

206 return SubmissionStatusData( 

207 submission=submission, 

208 status=SubmissionStatus.DONE, 

209 description=_("Submission") + " " + submission.get_state_display(), # type:ignore 

210 ) 

211 

212 return SubmissionStatusData( 

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

214 ) 

215 

216 def can_access_last_activity(self) -> bool: 

217 return False 

218 

219 

220class Author(Role): 

221 """ 

222 Author role. 

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

224 """ 

225 

226 _CODE: ClassVar[str] = "author" 

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

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

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

230 rights: AuthorRights 

231 

232 def __init__(self, user: User) -> None: 

233 super().__init__(user) 

234 

235 @property 

236 def active(self) -> bool: 

237 return True 

238 

239 def get_rights(self) -> AuthorRights: 

240 return AuthorRights(self.user)