Coverage for src/mesh/views/components/button.py: 57%

81 statements  

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

1from dataclasses import dataclass, field 

2 

3from django import forms 

4from django.urls import reverse, reverse_lazy 

5from django.utils.translation import gettext_lazy as _ 

6from ptf.url_utils import add_query_parameters_to_url 

7 

8from mesh.model.roles.role_handler import RoleHandler 

9 

10from ...models.review_models import Review, ReviewState 

11from ...models.submission_models import Submission 

12 

13 

14@dataclass 

15class Button: 

16 """ 

17 Interface for a button component. 

18 """ 

19 

20 # Unique ID for the button - not used for now 

21 id: str 

22 # Title to display as text in the button 

23 title: str 

24 # Optional font-awesome 6 icon code/class to be added next to the title. 

25 icon_class: str = field(default="") 

26 # Additional HTML attributes (ex: `{"type": ["submit"]})` 

27 attrs: dict[str, list[str]] = field(default_factory=dict) 

28 # A Django form - If not None, the button will be rendered as a full form 

29 # with this unique button. You must provide the "href" HTML attribute for the form 

30 # to be considered as one. 

31 form: forms.Form | None = field(default=None) 

32 

33 def is_form(self) -> bool: 

34 """ 

35 Whether the button should be displayed as a form. 

36 """ 

37 return bool(self.form) and bool(self.attrs.get("href", False)) 

38 

39 def is_link(self) -> bool: 

40 """ 

41 Whether the button is a link. 

42 """ 

43 return bool(self.attrs.get("href", False) or self.attrs.get("data-modal-href", False)) 

44 

45 def is_modal_link(self): 

46 """ 

47 Whether the button is a link. 

48 """ 

49 return bool(self.attrs.get("data-modal-href", False)) 

50 

51 def add_attr(self, attr: str, values: list[str]): 

52 """ 

53 Add the given value to the given attribute values array. 

54 

55 Ex: 

56 `button.add_attr("class", ["btn", "btn-primary"])` 

57 `button.add_attr("class", ["btn"])` 

58 """ 

59 if attr not in self.attrs: 

60 self.attrs[attr] = [] 

61 

62 for value in values: 

63 if value in self.attrs[attr]: 

64 continue 

65 self.attrs[attr].append(value) 

66 

67 def set_attr(self, attr: str, values: list[str]): 

68 """ 

69 Set the given value for the given attribute. 

70 

71 Ex: 

72 `button.set_attr("class", ["btn", "btn-primary"])` 

73 `button.set_attr("href", ["http://myserver"])` 

74 """ 

75 self.attrs[attr] = values 

76 

77 def remove_attr(self, attr: str): 

78 """ 

79 Remove the given attribute from the attributes array. 

80 """ 

81 if attr in self.attrs: 

82 del self.attrs[attr] 

83 

84 

85@dataclass 

86class SubmissionActionList: 

87 title: str 

88 actions: list[Button] = field(default_factory=list) 

89 

90 

91def build_submission_actions( 

92 submission: Submission, role_handler: RoleHandler, *args, **kwargs 

93) -> list[SubmissionActionList]: 

94 """ 

95 Returns the complete list of available actions to the current user. 

96 """ 

97 submission_actions: list[Button] = [] 

98 manage_actions: list[Button] = [] 

99 btn_classes = ["as-button"] if kwargs.get("display_as_btn", False) else [] 

100 shortlist = kwargs.get("shortlist", True) 

101 

102 if role_handler.check_rights("can_access_submission_log", submission) and shortlist: 102 ↛ 119line 102 didn't jump to line 119 because the condition on line 102 was always true

103 manage_actions.append( 

104 Button( 

105 id=f"submission-view-{submission.pk}", 

106 title=_("VIEW SUBMISSION"), 

107 icon_class="fa-eye", 

108 attrs={ 

109 "href": [ 

110 reverse_lazy("mesh:submission_details", kwargs={"pk": submission.pk}) 

111 ], 

112 "class": btn_classes, 

113 }, 

114 ) 

115 ) 

116 

117 #### [BEGIN] Author actions #### 

118 # Resume submit process 

119 can_submit_submission = role_handler.check_rights("can_submit_submission", submission) 

120 if can_submit_submission: 120 ↛ 121line 120 didn't jump to line 121 because the condition on line 120 was never true

121 submission_actions.append( 

122 Button( 

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

124 title=_("RESUME SUBMISSION"), 

125 icon_class="fa-list-check", 

126 attrs={ 

127 "href": [reverse_lazy("mesh:submission_resume", kwargs={"pk": submission.pk})], 

128 "class": btn_classes, 

129 }, 

130 ) 

131 ) 

132 

133 # Edit submission metadata & authors 

134 elif role_handler.check_rights("can_edit_submission", submission): 134 ↛ 164line 134 didn't jump to line 164 because the condition on line 134 was always true

135 if not shortlist or can_submit_submission: 135 ↛ 136line 135 didn't jump to line 136 because the condition on line 135 was never true

136 submission_actions.append( 

137 Button( 

138 id=f"submission-edit-metadata-{submission.pk}", 

139 title=_("EDIT METADATA"), 

140 icon_class="fa-pen-to-square", 

141 attrs={ 

142 "href": [ 

143 reverse_lazy("mesh:submission_update", kwargs={"pk": submission.pk}) 

144 ], 

145 "class": btn_classes, 

146 }, 

147 ) 

148 ) 

149 submission_actions.append( 

150 Button( 

151 id=f"submission-edit-authors-{submission.pk}", 

152 title=_("EDIT AUTHOR"), 

153 icon_class="fa-pen-to-square", 

154 attrs={ 

155 "href": [ 

156 reverse_lazy("mesh:submission_authors", kwargs={"pk": submission.pk}) 

157 ], 

158 "class": btn_classes, 

159 }, 

160 ) 

161 ) 

162 

163 # Create a new submission version 

164 if not can_submit_submission and role_handler.check_rights("can_create_version", submission): 164 ↛ 165line 164 didn't jump to line 165 because the condition on line 164 was never true

165 submission_actions.append( 

166 Button( 

167 id=f"submission-create-version-{submission.pk}", 

168 title=_("SUBMIT REVISIONS"), 

169 icon_class="fa-plus", 

170 attrs={ 

171 "href": [ 

172 reverse_lazy( 

173 "mesh:submission_version_create", 

174 kwargs={"submission_pk": submission.pk}, 

175 ) 

176 ], 

177 "class": btn_classes, 

178 }, 

179 ) 

180 ) 

181 

182 # Edit the current submission version 

183 current_version = submission.current_version 

184 if ( 184 ↛ 189line 184 didn't jump to line 189 because the condition on line 184 was never true

185 not can_submit_submission 

186 and current_version 

187 and role_handler.check_rights("can_edit_version", current_version) 

188 ): 

189 submission_actions.append( 

190 Button( 

191 id=f"submission-edit-version-{submission.pk}", 

192 title=_("RESUME REVISIONS"), 

193 icon_class="fa-list-check", 

194 attrs={ 

195 "href": [ 

196 reverse_lazy( 

197 "mesh:submission_version_update", 

198 kwargs={"pk": current_version.pk}, 

199 ) 

200 ], 

201 "class": btn_classes, 

202 }, 

203 ) 

204 ) 

205 #### [END] Author actions #### 

206 

207 #### [BEGIN] Editor actions #### 

208 if role_handler.check_rights("can_assign_editor", submission): 208 ↛ 245line 208 didn't jump to line 245 because the condition on line 208 was always true

209 """ 

210 <a id="assigned-editors-edit" class="as-button" href="{% url "mesh:submission_editors" pk=submission.pk%}"> 

211 <i class="fa-solid fa-plus"></i> 

212 {% translate "Assign editor" %} 

213 </a> 

214 """ 

215 manage_actions.append( 

216 Button( 

217 id=f"assigned-editors-edit-{submission.pk}", 

218 title=_("ASSIGN EDITOR"), 

219 icon_class="fa-plus", 

220 attrs={ 

221 "class": btn_classes, 

222 "data-modal-href": [ 

223 reverse_lazy("mesh:async_assign_editor", kwargs={"pk": submission.pk}) 

224 ], 

225 "data-reload": ["true"] if kwargs.get("reload_page", False) else ["false"], 

226 "data-reload-id": [kwargs.get("reload_id", "")], 

227 "data-reload-href": ( 

228 [ 

229 reverse_lazy( 

230 "mesh:api_fetch_submission", 

231 kwargs={ 

232 "pk": submission.pk, 

233 "row_id": kwargs.get("reload_id", ""), 

234 }, 

235 ) 

236 ] 

237 if kwargs.get("reload_id", "") != "" 

238 else [""] 

239 ), 

240 }, 

241 ) 

242 ) 

243 

244 # Send the submission version to review 

245 if role_handler.check_rights("can_start_review_process", submission): 245 ↛ 246line 245 didn't jump to line 246 because the condition on line 245 was never true

246 from ...views.forms.editorial_forms import StartReviewProcessForm 

247 

248 form = StartReviewProcessForm(initial={"process": True}) 

249 

250 manage_actions.append( 

251 Button( 

252 id=f"submission-send-to-review-{submission.pk}", 

253 title=_("SEND TO REVIEW"), 

254 icon_class="fa-file-import", 

255 form=form, 

256 attrs={ 

257 "href": [ 

258 reverse_lazy( 

259 "mesh:submission_start_review_process", 

260 kwargs={"pk": submission.pk}, 

261 ) 

262 ], 

263 "class": ["as-link"], 

264 }, 

265 ) 

266 ) 

267 

268 # Make an editorial decision 

269 if role_handler.check_rights("can_create_editorial_decision", submission): 269 ↛ 288line 269 didn't jump to line 288 because the condition on line 269 was always true

270 manage_actions.append( 

271 Button( 

272 id=f"submission-editorial-decision-{submission.pk}", 

273 title=_("EDITORIAL DECISION"), 

274 icon_class="fa-plus", 

275 attrs={ 

276 "href": [ 

277 reverse_lazy( 

278 "mesh:editorial_decision_create", 

279 kwargs={"submission_pk": submission.pk}, 

280 ) 

281 ], 

282 "class": btn_classes, 

283 }, 

284 ) 

285 ) 

286 

287 # Request a new review 

288 if current_version and role_handler.check_rights("can_invite_reviewer", current_version): 288 ↛ 289line 288 didn't jump to line 289 because the condition on line 288 was never true

289 manage_actions.append( 

290 Button( 

291 id=f"submission-referee-request-{submission.pk}", 

292 title=_("REQUEST REVIEW"), 

293 icon_class="fa-user-group", 

294 attrs={ 

295 "href": [ 

296 reverse_lazy( 

297 "mesh:review_create", 

298 kwargs={"version_pk": submission.current_version.pk}, # type:ignore 

299 ) 

300 ], 

301 "class": btn_classes, 

302 }, 

303 ) 

304 ) 

305 

306 # if role_handler.check_rights("can_access_submission_log", submission) and not shortlist: 

307 # manage_actions.append( 

308 # Button( 

309 # id=f"submission-log-{submission.pk}", 

310 # title=_("VIEW HISTORY"), 

311 # icon_class="fa-clock-rotate-left", 

312 # attrs={ 

313 # "href": [reverse_lazy("mesh:submission_log", kwargs={"pk": submission.pk})], 

314 # "class": btn_classes, 

315 # }, 

316 # ) 

317 # ) 

318 #### [END] Editor actions #### 

319 

320 #### [BEGIN] Reviewer actions #### 

321 # Submit / Edit the user current review 

322 current_review: Review | None = role_handler.get_from_rights( 

323 "get_current_open_review", current_version 

324 ) 

325 if current_review is not None and current_review.accepted is None: 325 ↛ 327line 325 didn't jump to line 327 because the condition on line 325 was never true

326 # Display 2 buttons: Accept & Decline if the review has not been accepted yet. 

327 manage_actions.extend( 

328 [ 

329 Button( 

330 id=f"review-accept-{current_review.pk}", 

331 title=_("Accept review"), 

332 icon_class="fa-check", 

333 attrs={ 

334 "href": [ 

335 add_query_parameters_to_url( 

336 reverse( 

337 "mesh:review_accept", 

338 kwargs={"pk": current_review.pk}, 

339 ), 

340 {"accepted": ["true"]}, 

341 ) 

342 ], 

343 "class": ["as-button", "button-success"], 

344 }, 

345 ), 

346 Button( 

347 id=f"review-accept-{current_review.pk}", 

348 title=_("Decline review"), 

349 icon_class="fa-xmark", 

350 attrs={ 

351 "href": [ 

352 add_query_parameters_to_url( 

353 reverse( 

354 "mesh:review_accept", 

355 kwargs={"pk": current_review.pk}, 

356 ), 

357 {"accepted": ["false"]}, 

358 ) 

359 ], 

360 "class": ["as-button", "button-error"], 

361 }, 

362 ), 

363 ] 

364 ) 

365 elif current_review is not None: 365 ↛ 366line 365 didn't jump to line 366 because the condition on line 365 was never true

366 action_name = _("SUBMIT REVIEW") 

367 if current_review.state in [ 

368 ReviewState.SUBMITTED.value, 

369 ReviewState.DECLINED.value, 

370 ]: 

371 action_name = _("EDIT REVIEW") 

372 manage_actions.append( 

373 Button( 

374 id=f"review-{current_review.pk}", 

375 title=action_name, 

376 icon_class="fa-pen-to-square", 

377 attrs={ 

378 "href": [reverse_lazy("mesh:review_update", kwargs={"pk": current_review.pk})], 

379 "class": btn_classes, 

380 }, 

381 ) 

382 ) 

383 #### [END] Reviewer actions #### 

384 action_lists = [] 

385 if submission_actions: 385 ↛ 386line 385 didn't jump to line 386 because the condition on line 385 was never true

386 action_lists.append( 

387 SubmissionActionList(title=_("Submission"), actions=submission_actions) 

388 ) 

389 

390 if manage_actions: 390 ↛ 393line 390 didn't jump to line 393 because the condition on line 390 was always true

391 action_lists.append(SubmissionActionList(title=_("Manage"), actions=manage_actions)) 

392 

393 return action_lists