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
« 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
4from django.db.models import QuerySet
6# from django.urls import reverse_lazy
7from django.utils.translation import gettext as _
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
14from ..submission_status import SubmissionStatus, SubmissionStatusData
16# from ..views.components.button import Button
17from .base_role import Role, RoleRights
20class AuthorRights(RoleRights):
21 """
22 Rights implementation for author role.
23 """
25 @cached_property
26 def submissions(self) -> QuerySet[Submission]:
27 return Submission.objects.get_submissions().filter(created_by=self.user)
29 def can_create_submission(self) -> bool:
30 return True
32 def can_access_submission(self, submission: Submission) -> bool:
33 return submission in self.submissions
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
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 )
58 def can_access_version(self, version: SubmissionVersion) -> bool:
59 return self.can_access_submission(version.submission)
61 def can_edit_version(self, version: SubmissionVersion) -> bool:
62 return not version.submitted and self.can_access_submission(version.submission)
64 def can_access_submission_author(self, submission: Submission) -> bool:
65 return self.can_access_submission(submission)
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)
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 )
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 )
93 def can_access_review_file(self, file: ReviewAdditionalFile) -> bool:
94 return file.author_access
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 )
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
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
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 )
212 return SubmissionStatusData(
213 submission=submission, status=SubmissionStatus.ERROR, description=""
214 )
216 def can_access_last_activity(self) -> bool:
217 return False
220class Author(Role):
221 """
222 Author role.
223 This is the base role of mesh app, always active.
224 """
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
232 def __init__(self, user: User) -> None:
233 super().__init__(user)
235 @property
236 def active(self) -> bool:
237 return True
239 def get_rights(self) -> AuthorRights:
240 return AuthorRights(self.user)