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

1 import json

2 from typing import Type, Any

3

4 from django import forms

5

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

7

8

9 class 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

102 class 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

114 class TimelineMixin:

115 name: str

116 verbose_name: str

117 inline: ListInline

118 templates = ListTemplates(

119 main="django_resume/plain/timeline.html",

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

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

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

123 item_form="django_resume/plain/timeline_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

131 class FreelanceTimelinePlugin(TimelineMixin, ListPlugin):

132 name = "freelance_timeline"

133 verbose_name = "Freelance Timeline"

134

135

136 class EmployedTimelinePlugin(TimelineMixin, ListPlugin):

137 name = "employed_timeline"

138 verbose_name = "Employed Timeline"