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

110 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-05-04 12:41 +0000

1from __future__ import annotations 

2 

3from dataclasses import dataclass, field 

4from typing import TYPE_CHECKING 

5 

6from django.urls import reverse 

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: 

14 from mesh.models.orm.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( 

94 "mesh:submission_update_vuejs", kwargs={"submission_pk": self.submission.pk} 

95 ) 

96 ] 

97 

98 if next_step is not None: 

99 current_step = self.get_step(step_id) 

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

101 button = add_stepper_button( 

102 id=id, 

103 title="Next", 

104 icon_class="fa-right-long", 

105 name=FormAction.NEXT.value, 

106 href=href, 

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

108 data_attrs=data_attrs, 

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

110 ) 

111 return button 

112 

113 def get_previous_button(self): 

114 step_id = self.active 

115 button = None 

116 previous_step = self.get_previous_step(step_id) 

117 if previous_step is not None: 

118 button = add_stepper_button( 

119 id="previous", 

120 title="Previous", 

121 icon_class="fa-left-long", 

122 name=FormAction.PREVIOUS.value, 

123 href=previous_step.href, 

124 ) 

125 return button 

126 

127 

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

129 """ 

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

131 The active step is not set. 

132 """ 

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

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

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

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

137 

138 preprint_step.can_navigate = True 

139 preprint_step.href = reverse("mesh:submission_create") 

140 

141 if submission: 

142 preprint_step.completed = True 

143 preprint_step.href = reverse( 

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

145 ) 

146 metadata_step.can_navigate = True 

147 metadata_step.href = reverse( 

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

149 ) 

150 

151 if hasattr(submission.get_current_version(), "main_file"): 

152 metadata_step.completed = True 

153 info_step.can_navigate = True 

154 info_step.href = reverse( 

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

156 ) 

157 

158 if submission.author_agreement: 

159 info_step.completed = True 

160 confirm_step.can_navigate = True 

161 confirm_step.href = reverse( 

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

163 ) 

164 

165 return StepperConfig( 

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

167 title=_("Submit process"), 

168 submission=submission, 

169 ) 

170 

171 

172def add_stepper_button( 

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

174): 

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

176 if href is None: 

177 if submit_button: 

178 attrs["type"] = ["submit"] 

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

180 btn_classes.append("save-button") 

181 attrs["class"] = btn_classes 

182 else: 

183 attrs["href"] = [href] 

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

185 attrs["class"] = btn_classes 

186 

187 return Button( 

188 id=id, 

189 title=_(title), 

190 icon_class=icon_class, 

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

192 attrs=attrs, 

193 )