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
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +0000
1from __future__ import annotations
3from abc import ABC, abstractmethod
4from dataclasses import asdict, dataclass
5from typing import TYPE_CHECKING, ClassVar
7from django.db.models import QuerySet
8from django.utils.translation import gettext
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
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
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 """
25 user: User
27 def __init__(self, user: User) -> None:
28 self.user = user
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
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
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
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()
82 def can_create_submission(self) -> bool:
83 """
84 Wether the user role has rights to create a new submission.
85 """
86 return False
88 def can_access_submission(self, submission: Submission) -> bool:
89 """
90 Wether the user role can access the given submission.
91 """
92 return False
94 def can_edit_submission(self, submission: Submission) -> bool:
95 """
96 Whether the user role can edit the given submission.
97 """
98 return True
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
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
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
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
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
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
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
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
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
156 def can_edit_review(self, review: Review) -> bool:
157 """
158 Whether the user role can edit the given review.
159 """
160 return False
162 def can_submit_review(self, review: Review) -> bool:
163 """
164 Whether the user role can submit the given review.
165 """
166 return False
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
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
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
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
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
198 def can_impersonate(self) -> bool:
199 """
200 Whether the user role can impersonate other users.
201 """
202 return False
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
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
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
222 def can_access_journal_sections(self) -> bool:
223 """
224 Whether the user can access the submission journal_sections views.
225 """
226 return False
228 def can_edit_journal_sections(self) -> bool:
229 """
230 Whether the user can edit the submission journal_sections.
231 """
232 return False
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
240 def can_access_last_activity(self) -> bool:
241 """
242 Whether the user role can view the last activity.
243 """
244 return True
246 def can_access_shortcut_actions(self) -> bool:
247 return False
250@dataclass
251class RoleSummary:
252 code: str
253 name: str
254 icon_class: str
255 submission_list_title: str
257 def serialize(self) -> dict:
258 return asdict(self)
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 """
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
285 def __init__(self, user: User) -> None:
286 self.user = user
287 self.rights = self.get_rights()
289 @property
290 @abstractmethod
291 def active(self) -> bool:
292 """
293 Wether the role is active for the user.
294 """
295 return False
297 @classmethod
298 def code(cls) -> str:
299 """
300 Returns the role's code.
301 """
302 return cls._CODE
304 @classmethod
305 def name(cls) -> str:
306 """
307 Returns the role's display name.
308 """
309 return cls._NAME
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
318 @classmethod
319 def submissions_list_title(cls) -> str:
320 return cls._SUBMISSION_LIST_TITLE
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 )
331 @abstractmethod
332 def get_rights(self) -> RoleRights:
333 """
334 Returns the rights object associated to the role.
335 """
336 pass
338 def accept(self, visitor, submission, *args, **kwargs):
339 return visitor.visit(self, submission, *args, **kwargs)