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
« prev ^ index » next coverage.py v7.6.1, created at 2024-10-13 13:17 +0200
1import json
2from typing import Type, Any
4from django import forms
6from .base import ListPlugin, ListItemFormMixin, ListTemplates, ListInline
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)
26 def __init__(self, *args, **kwargs):
27 super().__init__(*args, **kwargs)
28 self.set_initial_position()
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)
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 }
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
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
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
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
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
102class TimelineFlatForm(forms.Form):
103 title = forms.CharField(
104 widget=forms.TextInput(), required=False, max_length=50, initial="Timeline"
105 )
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
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 )
126 @staticmethod
127 def get_form_classes() -> dict[str, Type[forms.Form]]:
128 return {"item": TimelineItemForm, "flat": TimelineFlatForm}
131class FreelanceTimelinePlugin(TimelineMixin, ListPlugin):
132 name = "freelance_timeline"
133 verbose_name = "Freelance Timeline"
136class EmployedTimelinePlugin(TimelineMixin, ListPlugin):
137 name = "employed_timeline"
138 verbose_name = "Employed Timeline"