Coverage for website/partners/models.py: 44.86%
145 statements
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1from django.core.exceptions import ValidationError
2from django.core.files.storage import storages
3from django.core.validators import RegexValidator, URLValidator
4from django.db import models
5from django.urls import reverse
6from django.utils.translation import gettext
7from django.utils.translation import gettext_lazy as _
9from thumbnails.fields import ImageField
10from tinymce.models import HTMLField
12from utils import countries
13from utils.media.services import get_upload_to_function
16class Partner(models.Model):
17 """Model describing partner."""
19 is_active = models.BooleanField(default=False)
20 is_main_partner = models.BooleanField(default=False)
21 is_local_partner = models.BooleanField(default=False)
22 name = models.CharField(max_length=255)
23 slug = models.SlugField(unique=True)
24 link = models.CharField(max_length=255, blank=True, validators=[URLValidator()])
25 company_profile = HTMLField(blank=True)
27 logo = ImageField(
28 upload_to=get_upload_to_function("partners/logos/"),
29 resize_source_to="source_png",
30 storage=storages["public"],
31 )
33 alternate_logo = ImageField(
34 upload_to=get_upload_to_function("partners/logos/"),
35 resize_source_to="source_png",
36 storage=storages["public"],
37 blank=True,
38 null=True,
39 help_text=_(
40 "If set, this logo will be shown on the frontpage banner. Please use files with proper transparency."
41 ),
42 )
44 site_header = ImageField(
45 upload_to=get_upload_to_function("partners/headers/"),
46 resize_source_to="source_png",
47 storage=storages["public"],
48 null=True,
49 blank=True,
50 )
52 address = models.CharField(
53 max_length=100,
54 validators=[
55 RegexValidator(
56 regex=r"^([^/@:;%_]+) \d+([^/@:;%_]*)",
57 message="Enter a valid address",
58 )
59 ],
60 )
62 address2 = models.CharField(
63 max_length=100,
64 verbose_name=_("Second address line"),
65 blank=True,
66 null=True,
67 )
69 zip_code = models.CharField(
70 max_length=12,
71 )
73 city = models.CharField(max_length=100)
75 country = models.CharField(
76 max_length=2, choices=countries.EUROPE, verbose_name=_("Country")
77 )
79 def __init__(self, *args, **kwargs):
80 super().__init__(*args, **kwargs)
81 self._orig_logo = self.logo.name if self.logo else None
82 self._orig_site_header = self.site_header.name if self.site_header else None
84 def delete(self, using=None, keep_parents=False):
85 if self.logo.name:
86 self.logo.delete()
87 if self.site_header.name:
88 self.site_header.delete()
89 return super().delete(using, keep_parents)
91 def save(self, **kwargs):
92 """Save a partner and set main/local partners."""
93 if self.is_main_partner:
94 self.is_local_partner = False
95 self._reset_main_partner()
97 super().save(**kwargs)
99 if self._orig_logo and self._orig_logo != self.logo.name:
100 self.logo.storage.delete(self._orig_logo)
101 self._orig_logo = None
102 if self._orig_site_header and self._orig_site_header != self.site_header.name:
103 self.site_header.storage.delete(self._orig_site_header)
104 self._orig_site_header = None
106 def _reset_main_partner(self):
107 """Reset the main partner status.
109 If this partner is not main partner,
110 remove the main partner status from the main partner.
111 """
112 try:
113 current_main_partner = Partner.objects.get(is_main_partner=True)
114 if self != current_main_partner:
115 current_main_partner.is_main_partner = False
116 current_main_partner.save()
117 except Partner.DoesNotExist:
118 pass
120 def __str__(self):
121 """Return the name of the partner."""
122 return str(self.name)
124 def get_absolute_url(self):
125 """Return the url of the partner page."""
126 return reverse("partners:partner", args=(self.slug,))
128 class Meta:
129 """Meta class for partner model."""
131 ordering = ("name",)
134class PartnerImage(models.Model):
135 """Model to save partner image."""
137 partner = models.ForeignKey(
138 Partner, on_delete=models.CASCADE, related_name="images"
139 )
140 image = ImageField(
141 upload_to="partners/images/",
142 resize_source_to="source_png",
143 storage=storages["public"],
144 )
146 def __init__(self, *args, **kwargs):
147 super().__init__(*args, **kwargs)
148 self._orig_image = self.image.name if self.image else None
150 def delete(self, using=None, keep_parents=False):
151 if self.image.name:
152 self.image.delete()
153 return super().delete(using, keep_parents)
155 def save(self, **kwargs):
156 super().save(**kwargs)
158 if self._orig_image and self._orig_image != self.image.name:
159 self.image.storage.delete(self._orig_image)
160 self._orig_image = None
162 def __str__(self):
163 """Return string representation of partner name."""
164 return gettext("image of {}").format(self.partner.name)
167class VacancyCategory(models.Model):
168 """Model describing vacancy categories."""
170 name = models.CharField(max_length=30)
171 slug = models.SlugField(unique=True)
173 def __str__(self):
174 """Return the category name."""
175 return str(self.name)
177 class Meta:
178 """Meta class for vacancy category model."""
180 verbose_name_plural = _("Vacancy Categories")
183class Vacancy(models.Model):
184 """Model describing vacancies."""
186 title = models.CharField(_("title"), max_length=255)
187 description = HTMLField(_("description"))
188 link = models.CharField(
189 _("link"), max_length=255, blank=True, validators=[URLValidator()]
190 )
191 location = models.CharField(_("location"), max_length=255, null=True, blank=True)
192 keywords = models.TextField(
193 _("keywords"),
194 default="",
195 help_text="Comma separated list of keywords, for example: "
196 "Django,Python,Concrexit",
197 blank=True,
198 )
200 partner = models.ForeignKey(
201 Partner,
202 verbose_name=_("partner"),
203 on_delete=models.PROTECT,
204 null=True,
205 blank=True,
206 help_text=_(
207 "When you use a partner, the company name and logo "
208 "below will not be used."
209 ),
210 )
212 company_name = models.CharField(_("company name"), max_length=255, blank=True)
213 company_logo = ImageField(
214 _("company logo"),
215 upload_to="partners/vacancy-logos/",
216 resize_source_to="source_png",
217 storage=storages["public"],
218 null=True,
219 blank=True,
220 )
222 categories = models.ManyToManyField(VacancyCategory, blank=True)
224 def get_company_name(self):
225 """Return company or partner name."""
226 if self.partner: 226 ↛ 227line 226 didn't jump to line 227 because the condition on line 226 was never true
227 return self.partner.name
228 return self.company_name
230 def get_company_logo(self):
231 """Return company or partner logo."""
232 if self.partner:
233 return self.partner.logo
234 return self.company_logo
236 def __str__(self):
237 """Return vacancy partner or company and title."""
238 return f"{self.get_company_name()} — {self.title}"
240 def get_absolute_url(self):
241 """Return partner or vacancy url."""
242 url = reverse("partners:vacancies")
243 if self.partner and self.partner.is_active:
244 url = reverse("partners:partner", args=(self.partner.slug,))
245 return f"{url}#vacancy-{self.pk}"
247 def __init__(self, *args, **kwargs):
248 super().__init__(*args, **kwargs)
249 self._orig_logo = self.company_logo.name if self.company_logo else None
251 def delete(self, using=None, keep_parents=False):
252 if self.company_logo.name:
253 self.company_logo.delete()
254 return super().delete(using, keep_parents)
256 def save(self, **kwargs):
257 super().save(**kwargs)
259 if self._orig_logo and self._orig_logo != self.company_logo.name:
260 self.company_logo.storage.delete(self._orig_logo)
261 self._orig_logo = None
263 def clean(self):
264 """Validate the vacancy."""
265 super().clean()
266 errors = {}
268 msg = _("If no partner is used then both a company name and logo are required.")
269 if not self.partner and self.company_name and not self.company_logo:
270 errors.update({"company_logo": msg})
271 if not self.partner and not self.company_name and self.company_logo:
272 errors.update({"company_name": msg})
274 msg = _("Either select a partner or provide a company name and logo.")
275 if self.partner and (self.company_name or self.company_logo):
276 errors.update({"partner": msg})
277 if self.company_name:
278 errors.update({"company_name": msg})
279 if self.company_logo:
280 errors.update({"company_logo": msg})
281 if not self.partner and not self.company_name and not self.company_logo:
282 errors.update(
283 {
284 "partner": msg,
285 "company_name": msg,
286 "company_logo": msg,
287 }
288 )
290 if errors:
291 raise ValidationError(errors)
293 class Meta:
294 """Meta class for vacancy model."""
296 ordering = ["-pk"]
297 verbose_name_plural = _("Vacancies")