Coverage for src/django_resume/plugins/timelines.py: 88%

73 statements  

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

1import json 

2from typing import Type, Any 

3 

4from django import forms 

5 

6from .base import ListPlugin, ListItemFormMixin, ListTemplates, ListInline 

7 

8 

9class TimelineItemForm(ListItemFormMixin, forms.Form): 

10 role = forms.CharField(widget=forms.TextInput()) 

11 company_url = forms.URLField( 

12 widget=forms.URLInput(), required=False, assume_scheme="https" 

13 ) 

14 company_name = forms.CharField(widget=forms.TextInput(), max_length=50) 

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

16 start = forms.CharField(widget=forms.TextInput(), required=False) 

17 end = forms.CharField(widget=forms.TextInput(), required=False) 

18 initial_badges = [ 

19 "Some Badge", 

20 ] 

21 badges = forms.JSONField( 

22 widget=forms.TextInput(), required=False, initial=initial_badges 

23 ) 

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

25 

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

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

28 self.set_initial_position() 

29 

30 def badges_as_json(self): 

31 """ 

32 Return the initial badges which should already be a normal list of strings 

33 or the initial_badged list for the first render of the form encoded as json. 

34 """ 

35 existing_badges = self.initial.get("badges") 

36 if existing_badges is not None: 36 ↛ 38line 36 didn't jump to line 38 because the condition on line 36 was always true

37 return json.dumps(existing_badges) 

38 return json.dumps(self.initial_badges) 

39 

40 @staticmethod 

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

42 """Just some default values.""" 

43 return { 

44 "company_name": "company_name", 

45 "company_url": "https://example.com", 

46 "role": "role", 

47 "start": "start", 

48 "end": "end", 

49 "description": "description", 

50 "badges": TimelineItemForm.initial_badges, 

51 } 

52 

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

54 context["entry"] = { 

55 "id": item["id"], 

56 "company_url": item["company_url"], 

57 "company_name": item["company_name"], 

58 "role": item["role"], 

59 "start": item["start"], 

60 "end": item["end"], 

61 "description": item["description"], 

62 "badges": item["badges"], 

63 "edit_url": context["edit_url"], 

64 "delete_url": context["delete_url"], 

65 } 

66 return context 

67 

68 @staticmethod 

69 def get_max_position(items): 

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

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

72 return max(positions) if positions else -1 

73 

74 def set_initial_position(self): 

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

76 if "position" not in self.initial: 

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

78 

79 def clean_title(self): 

80 title = self.cleaned_data["title"] 

81 if title == "Senor Developer": 

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

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

84 return title 

85 

86 def clean_position(self): 

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

88 if position < 0: 88 ↛ 89line 88 didn't jump to line 89 because the condition on line 88 was never true

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

90 for item in self.existing_items: 

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

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

93 continue 

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

95 max_position = self.get_max_position(self.existing_items) 

96 raise forms.ValidationError( 

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

98 ) 

99 return position 

100 

101 

102class TimelineFlatForm(forms.Form): 

103 title = forms.CharField( 

104 widget=forms.TextInput(), required=False, max_length=50, initial="Timeline" 

105 ) 

106 

107 @staticmethod 

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

109 context["timeline"] = {"title": item.get("title", "")} 

110 context["timeline"]["edit_flat_url"] = context["edit_flat_url"] 

111 return context 

112 

113 

114class TimelineMixin: 

115 name: str 

116 verbose_name: str 

117 inline: ListInline 

118 templates = ListTemplates( 

119 main="django_resume/timelines/plain/content.html", 

120 flat="django_resume/timelines/plain/flat.html", 

121 flat_form="django_resume/timelines/plain/flat_form.html", 

122 item="django_resume/timelines/plain/item.html", 

123 item_form="django_resume/timelines/plain/item_form.html", 

124 ) 

125 

126 @staticmethod 

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

128 return {"item": TimelineItemForm, "flat": TimelineFlatForm} 

129 

130 

131class FreelanceTimelinePlugin(TimelineMixin, ListPlugin): 

132 name = "freelance_timeline" 

133 verbose_name = "Freelance Timeline" 

134 

135 

136class EmployedTimelinePlugin(TimelineMixin, ListPlugin): 

137 name = "employed_timeline" 

138 verbose_name = "Employed Timeline"