Coverage for src/mesh/views/components/stepper.py: 24%

112 statements  

« prev     ^ index     » next       coverage.py v7.9.0, created at 2025-09-10 11:20 +0000

1from __future__ import annotations 

2 

3from dataclasses import dataclass, field 

4from typing import TYPE_CHECKING 

5 

6from django.urls import reverse_lazy 

7from django.utils.translation import gettext_lazy as _ 

8 

9from mesh.views.forms.base_forms import FormAction 

10 

11from .button import Button 

12 

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

14 from ...models.submission_models import Submission 

15 

16 

17@dataclass 

18class StepConfig: 

19 id: str 

20 title: str 

21 href: str = field(default="") 

22 description: str = field(default="") 

23 tooltip: str = field(default="") 

24 # Whether the step has already been completed 

25 completed: bool = field(default=False) 

26 # Whether this is the currently displayed/selected step. 

27 active: bool = field(default=False) 

28 # Whether this is a step below the currently displayed/selected step. 

29 below_active: bool = field(default=False) 

30 # Whether the step config is navigable. 

31 # It can be True for steps after active one if you have used the Previous button or a direct link to a step. 

32 can_navigate: bool = field(default=False) 

33 # For some steps, the Next button is the form submit button 

34 # For others (use_href_for_next=True), the Next button is a simple link 

35 use_href_for_next_button: bool = field(default=False) 

36 

37 

38@dataclass 

39class StepperConfig: 

40 steps: list[StepConfig] = field(default_factory=list) 

41 title: str = field(default="") 

42 # Id of the active step 

43 active: str = field(default="") 

44 submission: Submission = None 

45 

46 def get_step(self, step_id: str) -> StepConfig | None: 

47 return next((s for s in self.steps if s.id == step_id), None) 

48 

49 def set_active_step(self, step_id: str): 

50 below_active_step = True 

51 

52 for s in self.steps: 

53 s.below_active = below_active_step 

54 

55 if s.id == step_id: 

56 below_active_step = False 

57 s.active = True 

58 self.active = step_id 

59 

60 def get_next_step(self, step_id): 

61 next_step = None 

62 i = 0 

63 while next_step is None and i < len(self.steps): 

64 if self.steps[i].id == step_id: 

65 if i + 1 < len(self.steps): 

66 next_step = self.steps[i + 1] 

67 i += 1 

68 return next_step 

69 

70 def get_previous_step(self, step_id): 

71 previous_step = None 

72 i = 0 

73 while previous_step is None and i < len(self.steps): 

74 if self.steps[i].id == step_id: 

75 if i - 1 > -1: 

76 previous_step = self.steps[i - 1] 

77 i += 1 

78 return previous_step 

79 

80 def set_completed_steps(self, step_ids: list[str]): 

81 for step in self.steps: 

82 step.completed = step.id in step_ids 

83 

84 def get_next_button(self): 

85 step_id = self.active 

86 button = None 

87 next_step = self.get_next_step(step_id) 

88 current_step = self.get_step(step_id) 

89 id = "next" # if step_id != "metadata" else "next-from-mesh" 

90 data_attrs = {} 

91 if step_id == "metadata": 

92 data_attrs["data-url"] = [ 

93 reverse_lazy("mesh:submission_update_vuejs", kwargs={"pk": self.submission.pk}) 

94 ] 

95 

96 if next_step is not None: 

97 current_step = self.get_step(step_id) 

98 href = next_step.href if current_step.use_href_for_next_button else None 

99 button = add_stepper_button( 

100 id=id, 

101 title="Next", 

102 icon_class="fa-right-long", 

103 name=FormAction.NEXT.value, 

104 href=href, 

105 btn_classes=["button-highlight", "next-button-to-watch"], 

106 data_attrs=data_attrs, 

107 submit_button=True if step_id != "metadata" else False, 

108 ) 

109 return button 

110 

111 def get_previous_button(self): 

112 step_id = self.active 

113 button = None 

114 previous_step = self.get_previous_step(step_id) 

115 if previous_step is not None: 

116 button = add_stepper_button( 

117 id="previous", 

118 title="Previous", 

119 icon_class="fa-left-long", 

120 name=FormAction.PREVIOUS.value, 

121 href=previous_step.href, 

122 ) 

123 return button 

124 

125 

126def get_submission_stepper(submission: Submission | None) -> StepperConfig: 

127 """ 

128 Returns the submission stepper config adapted with the given submission. 

129 The active step is not set. 

130 """ 

131 preprint_step = StepConfig(id="preprint", title=_("Init")) 

132 metadata_step = StepConfig(id="metadata", title=_("Article data")) 

133 info_step = StepConfig(id="info", title=_("Submission info")) 

134 confirm_step = StepConfig(id="confirm", title=_("Confirm")) 

135 

136 preprint_step.can_navigate = True 

137 preprint_step.href = reverse_lazy("mesh:submission_create") 

138 

139 if submission: 

140 preprint_step.completed = True 

141 preprint_step.href = reverse_lazy( 

142 "mesh:submission_update_preprint", kwargs={"pk": submission.pk} 

143 ) 

144 metadata_step.can_navigate = True 

145 metadata_step.href = reverse_lazy( 

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

147 ) 

148 

149 if hasattr(submission.current_version, "main_file"): 

150 metadata_step.completed = True 

151 info_step.can_navigate = True 

152 info_step.href = reverse_lazy( 

153 "mesh:submission_info_update", kwargs={"pk": submission.pk} 

154 ) 

155 

156 if submission.author_agreement: 

157 info_step.completed = True 

158 confirm_step.can_navigate = True 

159 confirm_step.href = reverse_lazy( 

160 "mesh:submission_confirm", kwargs={"pk": submission.pk} 

161 ) 

162 

163 return StepperConfig( 

164 steps=[preprint_step, metadata_step, info_step, confirm_step], 

165 title=_("Submit process"), 

166 submission=submission, 

167 ) 

168 

169 

170def add_stepper_button( 

171 id, title, icon_class, name, href=None, btn_classes=[], data_attrs={}, submit_button=True 

172): 

173 attrs = {"name": [name]} | data_attrs 

174 if href is None: 

175 if submit_button: 

176 attrs["type"] = ["submit"] 

177 attrs["form"] = ["id-form-fullpage"] 

178 btn_classes.append("save-button") 

179 attrs["class"] = btn_classes 

180 else: 

181 attrs["href"] = [href] 

182 btn_classes.extend(["as-button", "save-button"]) 

183 attrs["class"] = btn_classes 

184 

185 return Button( 

186 id=id, 

187 title=_(title), 

188 icon_class=icon_class, 

189 # attrs={"href": [author_step.href], "class": ["as-button"]}, 

190 attrs=attrs, 

191 )