Coverage for src/django_resume/projects.py: 41%

85 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-23 13:16 +0200

1from typing import Type, Any 

2 

3from django import forms 

4 

5from .plugins import ListPlugin, ListItemFormMixin, ListTemplates, ListInline 

6 

7 

8class ProjectItemForm(ListItemFormMixin, forms.Form): 

9 title = forms.CharField(widget=forms.TextInput()) 

10 url = forms.URLField(widget=forms.URLInput(), required=False, assume_scheme="https") 

11 description = forms.CharField(widget=forms.Textarea()) 

12 badges = forms.CharField(widget=forms.TextInput(), required=False) 

13 position = forms.IntegerField(widget=forms.NumberInput(), required=False) 

14 

15 def __init__(self, *args, **kwargs): 

16 super().__init__(*args, **kwargs) 

17 self.set_initial_badges() 

18 self.set_initial_position() 

19 

20 @staticmethod 

21 def get_initial() -> dict[str, Any]: 

22 """Just some default values.""" 

23 return { 

24 "title": "Project Title", 

25 "url": "https://example.com/project/", 

26 "description": "description", 

27 "badges": "badges", 

28 } 

29 

30 def set_context(self, item: dict, context: dict[str, Any]) -> dict[str, Any]: 

31 context["project"] = { 

32 "id": item["id"], 

33 "url": item["url"], 

34 "title": item["title"], 

35 "description": item["description"], 

36 "badges": item["badges"], 

37 "edit_url": context["edit_url"], 

38 "delete_url": context["delete_url"], 

39 } 

40 return context 

41 

42 def set_initial_badges(self): 

43 """Transform the list of badges into a comma-separated string.""" 

44 if "badges" in self.initial and isinstance(self.initial["badges"], list): 

45 self.initial["badges"] = ",".join(self.initial["badges"]) 

46 

47 @staticmethod 

48 def get_max_position(items): 

49 """Return the maximum position value from the existing items.""" 

50 positions = [item.get("position", 0) for item in items] 

51 return max(positions) if positions else -1 

52 

53 def set_initial_position(self): 

54 """Set the position to the next available position.""" 

55 if "position" not in self.initial: 

56 self.initial["position"] = self.get_max_position(self.existing_items) + 1 

57 

58 def clean_title(self): 

59 title = self.cleaned_data["title"] 

60 if title == "Senor Developer": 

61 print("No Senor! Validation Error!") 

62 raise forms.ValidationError("No Senor!") 

63 return title 

64 

65 def clean_badges(self): 

66 badges = self.cleaned_data.get("badges", "") 

67 # Split the comma-separated values and strip any extra spaces 

68 badge_list = [badge.strip() for badge in badges.split(",")] 

69 return badge_list 

70 

71 def clean_position(self): 

72 position = self.cleaned_data.get("position", 0) 

73 if position < 0: 

74 raise forms.ValidationError("Position must be a positive integer.") 

75 for item in self.existing_items: 

76 if item["id"] == self.cleaned_data["id"]: 

77 # updating the existing item, so we can skip checking its position 

78 continue 

79 if item.get("position") == position: 

80 max_position = self.get_max_position(self.existing_items) 

81 raise forms.ValidationError( 

82 f"Position must be unique - take {max_position + 1} instead." 

83 ) 

84 return position 

85 

86 

87class ProjectFlatForm(forms.Form): 

88 title = forms.CharField(widget=forms.TextInput(), required=False, max_length=50) 

89 

90 @staticmethod 

91 def set_context(item: dict, context: dict[str, Any]) -> dict[str, Any]: 

92 context["projects"] = {"title": item.get("title", "")} 

93 context["projects"]["edit_flat_url"] = context["edit_flat_url"] 

94 return context 

95 

96 

97class ProjectsForContext: 

98 def __init__( 

99 self, 

100 *, 

101 title: str, 

102 ordered_entries: list[dict], 

103 templates: ListTemplates, 

104 add_item_url: str, 

105 edit_flat_url: str, 

106 edit_flat_post_url: str, 

107 ): 

108 self.title = title 

109 self.ordered_entries = ordered_entries 

110 self.templates = templates 

111 self.add_item_url = add_item_url 

112 self.edit_flat_url = edit_flat_url 

113 self.edit_flat_post_url = edit_flat_post_url 

114 

115 

116class ProjectsPlugin(ListPlugin): 

117 name: str = "projects" 

118 verbose_name: str = "Projects" 

119 inline: ListInline 

120 templates = ListTemplates( 

121 main="django_resume/plain/projects.html", 

122 flat="django_resume/plain/projects_flat.html", 

123 flat_form="django_resume/plain/projects_flat_form.html", 

124 item="django_resume/plain/projects_item.html", 

125 item_form="django_resume/plain/projects_item_form.html", 

126 ) 

127 

128 @staticmethod 

129 def get_form_classes() -> dict[str, Type[forms.Form]]: 

130 return {"item": ProjectItemForm, "flat": ProjectFlatForm} 

131 

132 @staticmethod 

133 def items_ordered_by_position(items, reverse=False): 

134 return sorted(items, key=lambda item: item.get("position", 0), reverse=reverse) 

135 

136 def get_context( 

137 self, plugin_data, person_pk, *, context: dict[str, Any] 

138 ) -> ProjectsForContext: 

139 ordered_entries = self.items_ordered_by_position( 

140 plugin_data.get("items", []), reverse=True 

141 ) 

142 if context.get("show_edit_button", False): 

143 # if there should be edit buttons, add the edit URLs to each entry 

144 for entry in ordered_entries: 

145 entry["edit_url"] = self.inline.get_edit_item_url( 

146 person_pk, item_id=entry["id"] 

147 ) 

148 entry["delete_url"] = self.inline.get_delete_item_url( 

149 person_pk, item_id=entry["id"] 

150 ) 

151 print("edit flat post: ", self.inline.get_edit_flat_post_url(person_pk)) 

152 projects = ProjectsForContext( 

153 title=plugin_data.get("flat", {}).get("title", self.verbose_name), 

154 ordered_entries=ordered_entries, 

155 templates=self.templates, 

156 add_item_url=self.inline.get_edit_item_url(person_pk), 

157 edit_flat_url=self.inline.get_edit_flat_url(person_pk), 

158 edit_flat_post_url=self.inline.get_edit_flat_post_url(person_pk), 

159 ) 

160 return projects