Coverage for src/mesh/model/roles/base_role.py: 76%

123 statements  

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

1from __future__ import annotations 

2 

3from abc import ABC, abstractmethod 

4from dataclasses import asdict, dataclass 

5from typing import TYPE_CHECKING, ClassVar 

6 

7from django.db.models import QuerySet 

8from django.utils.translation import gettext 

9 

10from mesh.models.editorial_models import EditorialDecision 

11from mesh.models.review_models import Review, ReviewAdditionalFile 

12from mesh.models.submission_models import Submission, SubmissionVersion 

13from mesh.models.user_models import User 

14 

15if TYPE_CHECKING: 15 ↛ 16line 15 didn't jump to line 16 because the condition on line 15 was never true

16 from ..submission_status import SubmissionStatusData 

17 

18 

19class RoleRights(ABC): 

20 """ 

21 Base interface for rights management. 

22 Contains the actual data used to figure out the rights over a given object/entity. 

23 """ 

24 

25 user: User 

26 

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

28 self.user = user 

29 

30 @property 

31 @abstractmethod 

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

33 """ 

34 Returns the queryset of submissions the user has access to. 

35 """ 

36 pass 

37 

38 def get_current_open_review(self, submission: Submission) -> Review | None: 

39 """ 

40 Returns the current open review for the given submission, if any. 

41 Current review = Round not closed + review not submitted. 

42 """ 

43 return None 

44 

45 @abstractmethod 

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

47 """ 

48 Returns the submission status according to the user role + an optional string 

49 describing the submission status. 

50 Ex: (WAITING, "X reports missing") for an editor+ 

51 (WAITING, "Under review") for the author 

52 (TODO, "Reports due for {{date}}") for a reviewer 

53 """ 

54 pass 

55 

56 # def get_submission_list_config(self) -> list[SubmissionListConfig]: 

57 # """ 

58 # Returns the config to display the submissions for the user role. 

59 # """ 

60 # return [ 

61 # SubmissionListConfig( 

62 # key=SubmissionStatus.TODO, title=_("Requires action"), html_classes="todo" 

63 # ), 

64 # SubmissionListConfig( 

65 # key=SubmissionStatus.WAITING, 

66 # title=_("Waiting for other's input"), 

67 # html_classes="waiting", 

68 # ), 

69 # SubmissionListConfig( 

70 # key=SubmissionStatus.ARCHIVED, 

71 # title=_("Closed / Archived"), 

72 # html_classes="archived", 

73 # ), 

74 # ] 

75 # 

76 # def get_archived_submission_list_config(self) -> list[SubmissionListConfig]: 

77 # """ 

78 # Returns the config to display only the Archived submissions. 

79 # """ 

80 # return self.get_archived_submission_list_config() 

81 

82 def can_create_submission(self) -> bool: 

83 """ 

84 Wether the user role has rights to create a new submission. 

85 """ 

86 return False 

87 

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

89 """ 

90 Wether the user role can access the given submission. 

91 """ 

92 return False 

93 

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

95 """ 

96 Whether the user role can edit the given submission. 

97 """ 

98 return True 

99 

100 def can_submit_submission(self, submission: Submission) -> bool: 

101 """ 

102 Whether the user role can submit the given submission. 

103 It doesn't check whether the submission has the required fields to be submitted. 

104 """ 

105 return self.can_edit_submission(submission) and submission.is_draft 

106 

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

108 """ 

109 Whether the user role can submit a new version for the given submission 

110 """ 

111 return False 

112 

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

114 """ 

115 Whether the user role can edit the given submission version. 

116 """ 

117 return False 

118 

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

120 """ 

121 Whether the user role can view the data of the given submission version. 

122 """ 

123 return False 

124 

125 def can_start_review_process(self, submission: Submission) -> bool: 

126 """ 

127 Whether the user role can send the submission into the review process. 

128 """ 

129 return False 

130 

131 def can_create_editorial_decision(self, submission: Submission) -> bool: 

132 """ 

133 Whether the user role can create an editorial decision for the given submission. 

134 """ 

135 return False 

136 

137 def can_edit_editorial_decision(self, decision: EditorialDecision) -> bool: 

138 """ 

139 Whether the user role can edit the given editorial decision. 

140 """ 

141 return False 

142 

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

144 """ 

145 Whether the user role can view the reviews section of the given submission 

146 version. 

147 """ 

148 return False 

149 

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

151 """ 

152 Whether the user role can view the data of the given review. 

153 """ 

154 return False 

155 

156 def can_edit_review(self, review: Review) -> bool: 

157 """ 

158 Whether the user role can edit the given review. 

159 """ 

160 return False 

161 

162 def can_submit_review(self, review: Review) -> bool: 

163 """ 

164 Whether the user role can submit the given review. 

165 """ 

166 return False 

167 

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

169 """ 

170 Whether the user role can view the review's author name 

171 """ 

172 return False 

173 

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

175 """ 

176 Whether the user role can access the given review's file. 

177 """ 

178 return False 

179 

180 def can_access_review_details(self, review: Review) -> bool: 

181 """ 

182 Whether the user role can access the review details. 

183 """ 

184 return False 

185 

186 def can_invite_reviewer(self, version: SubmissionVersion) -> bool: 

187 """ 

188 Whether the user role can invite reviewer for the given submission version. 

189 """ 

190 return False 

191 

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

193 """ 

194 Wether the user role can access the author name of the given submission. 

195 """ 

196 return False 

197 

198 def can_impersonate(self) -> bool: 

199 """ 

200 Whether the user role can impersonate other users. 

201 """ 

202 return False 

203 

204 def can_access_submission_log(self, submission: Submission) -> bool: 

205 """ 

206 Whether the user role can view the submission log. 

207 """ 

208 return False 

209 

210 def can_assign_editor(self, submission: Submission) -> bool: 

211 """ 

212 Whether the user role can assign an editor to the given submission. 

213 """ 

214 return False 

215 

216 def can_filter_submissions(self) -> bool: 

217 """ 

218 Whether the user role can use filters on the submission list dashboard. 

219 """ 

220 return False 

221 

222 def can_access_journal_sections(self) -> bool: 

223 """ 

224 Whether the user can access the submission journal_sections views. 

225 """ 

226 return False 

227 

228 def can_edit_journal_sections(self) -> bool: 

229 """ 

230 Whether the user can edit the submission journal_sections. 

231 """ 

232 return False 

233 

234 def can_edit_review_file_right(self, review: Review) -> bool: 

235 """ 

236 Whether the user can edit the review file access right. 

237 """ 

238 return False 

239 

240 def can_access_last_activity(self) -> bool: 

241 """ 

242 Whether the user role can view the last activity. 

243 """ 

244 return True 

245 

246 def can_access_shortcut_actions(self) -> bool: 

247 return False 

248 

249 

250@dataclass 

251class RoleSummary: 

252 code: str 

253 name: str 

254 icon_class: str 

255 submission_list_title: str 

256 

257 def serialize(self) -> dict: 

258 return asdict(self) 

259 

260 

261class Role(ABC): 

262 """ 

263 Base interface for a role object. 

264 TODO: Is the split Role & RoleRights logically relevant ? Wouldn't it be simpler 

265 to merge them in a single object ? 

266 -----> Yes it is. It's the basis that will enable doing something like OJS with 

267 both role and permission level: any number of app-dependent role can be created but 

268 the available permission levels are the implemented RoleRights. 

269 https://docs.pkp.sfu.ca/learning-ojs/en/users-and-roles#permissions-and-roles 

270 This will require quite some work to enable freely creating role with our 

271 role handling system. 

272 """ 

273 

274 # Role code - Stored in user table keep track of the user current role. 

275 _CODE: ClassVar[str] 

276 # Role name 

277 _NAME: ClassVar[str] 

278 # Font-awesome 6 icon class (ex: "fa-user") used to represent the role. 

279 _ICON_CLASS: ClassVar[str] 

280 # Title for the "mesh:submission_list" view 

281 _SUBMISSION_LIST_TITLE: ClassVar[str] = gettext("My submissions") 

282 user: User 

283 rights: RoleRights 

284 

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

286 self.user = user 

287 self.rights = self.get_rights() 

288 

289 @property 

290 @abstractmethod 

291 def active(self) -> bool: 

292 """ 

293 Wether the role is active for the user. 

294 """ 

295 return False 

296 

297 @classmethod 

298 def code(cls) -> str: 

299 """ 

300 Returns the role's code. 

301 """ 

302 return cls._CODE 

303 

304 @classmethod 

305 def name(cls) -> str: 

306 """ 

307 Returns the role's display name. 

308 """ 

309 return cls._NAME 

310 

311 @classmethod 

312 def icon_class(cls) -> str: 

313 """ 

314 Returns the role's icon HTML tag (it uses font awesome 6). 

315 """ 

316 return cls._ICON_CLASS 

317 

318 @classmethod 

319 def submissions_list_title(cls) -> str: 

320 return cls._SUBMISSION_LIST_TITLE 

321 

322 @classmethod 

323 def summary(cls) -> RoleSummary: 

324 return RoleSummary( 

325 code=cls.code(), 

326 name=cls.name(), 

327 icon_class=cls.icon_class(), 

328 submission_list_title=cls.submissions_list_title(), 

329 ) 

330 

331 @abstractmethod 

332 def get_rights(self) -> RoleRights: 

333 """ 

334 Returns the rights object associated to the role. 

335 """ 

336 pass 

337 

338 def accept(self, visitor, submission, *args, **kwargs): 

339 return visitor.visit(self, submission, *args, **kwargs)