Commit e69d6c57 authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg
Browse files

Merge branch 'fix-579' into 'master'

Add documentation to the partners app

Closes #579

See merge request !1272
parents ae42a29f 396d05d8
...@@ -6,11 +6,15 @@ from utils.translation import TranslatedModelAdmin ...@@ -6,11 +6,15 @@ from utils.translation import TranslatedModelAdmin
class PartnerImageInline(admin.StackedInline): class PartnerImageInline(admin.StackedInline):
"""Class to show partner images inline in the admin."""
model = PartnerImage model = PartnerImage
@admin.register(Partner) @admin.register(Partner)
class PartnerAdmin(admin.ModelAdmin): class PartnerAdmin(admin.ModelAdmin):
"""Class to show partners in the admin."""
prepopulated_fields = {"slug": ("name",)} prepopulated_fields = {"slug": ("name",)}
list_display = ('name', 'is_active', 'is_main_partner',) list_display = ('name', 'is_active', 'is_main_partner',)
inlines = (PartnerImageInline,) inlines = (PartnerImageInline,)
...@@ -31,12 +35,16 @@ class PartnerAdmin(admin.ModelAdmin): ...@@ -31,12 +35,16 @@ class PartnerAdmin(admin.ModelAdmin):
@admin.register(VacancyCategory) @admin.register(VacancyCategory)
class VacancyCategoryAdmin(TranslatedModelAdmin): class VacancyCategoryAdmin(TranslatedModelAdmin):
"""Class to show vacancy categories in the admin."""
prepopulated_fields = {"slug": ("name_en",)} prepopulated_fields = {"slug": ("name_en",)}
fields = ['name', 'slug'] fields = ['name', 'slug']
@admin.register(Vacancy) @admin.register(Vacancy)
class VacancyAdmin(admin.ModelAdmin): class VacancyAdmin(admin.ModelAdmin):
"""Class to show vacancies in the admin."""
list_display = ('title', 'partner', 'company_name', 'expiration_date') list_display = ('title', 'partner', 'company_name', 'expiration_date')
fieldsets = ( fieldsets = (
...@@ -60,6 +68,8 @@ class VacancyAdmin(admin.ModelAdmin): ...@@ -60,6 +68,8 @@ class VacancyAdmin(admin.ModelAdmin):
@admin.register(PartnerEvent) @admin.register(PartnerEvent)
class PartnerEventAdmin(TranslatedModelAdmin): class PartnerEventAdmin(TranslatedModelAdmin):
"""Class to show partner events in the admin."""
fields = ['partner', 'other_partner', 'title', 'description', 'location', fields = ['partner', 'other_partner', 'title', 'description', 'location',
'start', 'end', 'url', 'published'] 'start', 'end', 'url', 'published']
list_display = ('title', 'start', 'end', list_display = ('title', 'start', 'end',
......
...@@ -8,7 +8,11 @@ from partners.models import PartnerEvent, Partner ...@@ -8,7 +8,11 @@ from partners.models import PartnerEvent, Partner
class PartnerEventCalendarJSSerializer(CalenderJSSerializer): class PartnerEventCalendarJSSerializer(CalenderJSSerializer):
"""Partner event calender serializer."""
class Meta(CalenderJSSerializer.Meta): class Meta(CalenderJSSerializer.Meta):
"""Meta class for partner event calendar serializer."""
model = PartnerEvent model = PartnerEvent
def _title(self, instance): def _title(self, instance):
...@@ -17,27 +21,39 @@ class PartnerEventCalendarJSSerializer(CalenderJSSerializer): ...@@ -17,27 +21,39 @@ class PartnerEventCalendarJSSerializer(CalenderJSSerializer):
return "{} ({})".format(instance.title, instance.other_partner) return "{} ({})".format(instance.title, instance.other_partner)
def _background_color(self, instance): def _background_color(self, instance):
"""Return the color of the background."""
return 'black' return 'black'
def _text_color(self, instance): def _text_color(self, instance):
"""Return the color of the text."""
return '#E62272' return '#E62272'
def _url(self, instance): def _url(self, instance):
"""Return the url of the partner event."""
return instance.url return instance.url
def _target_blank(self, instance): def _target_blank(self, instance):
"""Return whether the anchor tag should have 'target="_blank"'."""
return True return True
class PartnerSerializer(serializers.ModelSerializer): class PartnerSerializer(serializers.ModelSerializer):
"""Partner serializer."""
class Meta: class Meta:
"""Meta class for partner serializer."""
model = Partner model = Partner
fields = ('pk', 'name', 'link', 'company_profile', 'address', fields = ('pk', 'name', 'link', 'company_profile', 'address',
'zip_code', 'city', 'logo') 'zip_code', 'city', 'logo')
class PartnerEventSerializer(serializers.ModelSerializer): class PartnerEventSerializer(serializers.ModelSerializer):
"""Partner events serializer."""
class Meta: class Meta:
"""Meta class for partner events serializer."""
model = PartnerEvent model = PartnerEvent
fields = ('pk', 'title', 'description', 'start', 'end', 'location', fields = ('pk', 'title', 'description', 'start', 'end', 'location',
'url') 'url')
...@@ -45,4 +61,5 @@ class PartnerEventSerializer(serializers.ModelSerializer): ...@@ -45,4 +61,5 @@ class PartnerEventSerializer(serializers.ModelSerializer):
description = serializers.SerializerMethodField('_description') description = serializers.SerializerMethodField('_description')
def _description(self, instance): def _description(self, instance):
"""Return description of partner event."""
return unescape(strip_tags(instance.description)) return unescape(strip_tags(instance.description))
...@@ -15,11 +15,14 @@ from utils.snippets import extract_date_range ...@@ -15,11 +15,14 @@ from utils.snippets import extract_date_range
class PartnerViewset(viewsets.ReadOnlyModelViewSet): class PartnerViewset(viewsets.ReadOnlyModelViewSet):
"""View set for partners."""
serializer_class = PartnerSerializer serializer_class = PartnerSerializer
queryset = Partner.objects.filter(is_active=True) queryset = Partner.objects.filter(is_active=True)
@action(detail=False, permission_classes=(IsAuthenticatedOrReadOnly,)) @action(detail=False, permission_classes=(IsAuthenticatedOrReadOnly,))
def calendarjs(self, request): def calendarjs(self, request):
"""Return response with serialized partner event calender data."""
start, end = extract_date_range(request) start, end = extract_date_range(request)
queryset = PartnerEvent.objects.filter( queryset = PartnerEvent.objects.filter(
...@@ -33,6 +36,8 @@ class PartnerViewset(viewsets.ReadOnlyModelViewSet): ...@@ -33,6 +36,8 @@ class PartnerViewset(viewsets.ReadOnlyModelViewSet):
class PartnerEventViewset(viewsets.ReadOnlyModelViewSet): class PartnerEventViewset(viewsets.ReadOnlyModelViewSet):
"""View set for partner events."""
queryset = PartnerEvent.objects.filter(end__gte=timezone.now(), queryset = PartnerEvent.objects.filter(end__gte=timezone.now(),
published=True) published=True)
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
......
...@@ -3,5 +3,7 @@ from django.utils.translation import gettext_lazy as _ ...@@ -3,5 +3,7 @@ from django.utils.translation import gettext_lazy as _
class PartnersConfig(AppConfig): class PartnersConfig(AppConfig):
"""Appconfig for partners app."""
name = 'partners' name = 'partners'
verbose_name = _('Partners') verbose_name = _('Partners')
...@@ -8,6 +8,7 @@ from partners.models import Vacancy ...@@ -8,6 +8,7 @@ from partners.models import Vacancy
def send_vacancy_expiration_notifications(dry_run=False): def send_vacancy_expiration_notifications(dry_run=False):
"""Send a notification about expiring vacancies."""
# Select vacencies that expire in roughly a month, wherefor # Select vacencies that expire in roughly a month, wherefor
# a mail hasn't been sent yet to Mr/Mrs Extern # a mail hasn't been sent yet to Mr/Mrs Extern
expired_vacancies = Vacancy.objects.filter( expired_vacancies = Vacancy.objects.filter(
......
...@@ -4,8 +4,10 @@ from partners import emails ...@@ -4,8 +4,10 @@ from partners import emails
class Command(BaseCommand): class Command(BaseCommand):
"""Command class for sendvacancyexpirationnotification command."""
def add_arguments(self, parser): def add_arguments(self, parser):
"""Add --dry-run argument to command."""
parser.add_argument( parser.add_argument(
'--dry-run', '--dry-run',
action='store_true', action='store_true',
...@@ -15,5 +17,6 @@ class Command(BaseCommand): ...@@ -15,5 +17,6 @@ class Command(BaseCommand):
) )
def handle(self, *args, **options): def handle(self, *args, **options):
"""Call the function to handle the sending of emails."""
emails.send_vacancy_expiration_notifications( emails.send_vacancy_expiration_notifications(
bool(options['dry-run'])) bool(options['dry-run']))
...@@ -9,6 +9,8 @@ from utils.translation import ModelTranslateMeta, MultilingualField ...@@ -9,6 +9,8 @@ from utils.translation import ModelTranslateMeta, MultilingualField
class Partner(models.Model): class Partner(models.Model):
"""Model describing partner."""
is_active = models.BooleanField(default=False) is_active = models.BooleanField(default=False)
is_main_partner = models.BooleanField(default=False) is_main_partner = models.BooleanField(default=False)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
...@@ -42,12 +44,19 @@ class Partner(models.Model): ...@@ -42,12 +44,19 @@ class Partner(models.Model):
city = models.CharField(max_length=100) city = models.CharField(max_length=100)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""Save a partner and set main partner."""
if self.is_main_partner: if self.is_main_partner:
self._reset_main_partner() self._reset_main_partner()
super(Partner, self).save(*args, **kwargs) super(Partner, self).save(*args, **kwargs)
def _reset_main_partner(self): def _reset_main_partner(self):
"""
Reset the main partner status.
If this partner is not main partner,
remove the main partner status from the main partner.
"""
try: try:
current_main_partner = Partner.objects.get(is_main_partner=True) current_main_partner = Partner.objects.get(is_main_partner=True)
if self != current_main_partner: if self != current_main_partner:
...@@ -57,16 +66,22 @@ class Partner(models.Model): ...@@ -57,16 +66,22 @@ class Partner(models.Model):
pass pass
def __str__(self): def __str__(self):
"""Return the name of the partner."""
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
"""Return the url of the partner page."""
return reverse('partners:partner', args=(self.slug,)) return reverse('partners:partner', args=(self.slug,))
class Meta: class Meta:
"""Meta class for partner model."""
ordering = ('name',) ordering = ('name',)
class PartnerImage(models.Model): class PartnerImage(models.Model):
"""Model to save partner image."""
partner = models.ForeignKey( partner = models.ForeignKey(
Partner, Partner,
on_delete=models.CASCADE, on_delete=models.CASCADE,
...@@ -75,21 +90,29 @@ class PartnerImage(models.Model): ...@@ -75,21 +90,29 @@ class PartnerImage(models.Model):
image = models.ImageField(upload_to='public/partners/images/') image = models.ImageField(upload_to='public/partners/images/')
def __str__(self): def __str__(self):
"""Return string representation of partner name."""
return ugettext('image of {}').format(self.partner.name) return ugettext('image of {}').format(self.partner.name)
class VacancyCategory(models.Model, metaclass=ModelTranslateMeta): class VacancyCategory(models.Model, metaclass=ModelTranslateMeta):
"""Model describing vacancy categories."""
name = MultilingualField(models.CharField, max_length=30) name = MultilingualField(models.CharField, max_length=30)
slug = models.SlugField() slug = models.SlugField()
def __str__(self): def __str__(self):
"""Return the category name."""
return self.name return self.name
class Meta: class Meta:
"""Meta class for vacancy category model."""
verbose_name_plural = _('Vacancy Categories') verbose_name_plural = _('Vacancy Categories')
class Vacancy(models.Model): class Vacancy(models.Model):
"""Model describing vacancies."""
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
description = HTMLField() description = HTMLField()
link = models.CharField( link = models.CharField(
...@@ -122,28 +145,30 @@ class Vacancy(models.Model): ...@@ -122,28 +145,30 @@ class Vacancy(models.Model):
remarks = HTMLField(blank=True, help_text=_('not shown on the page')) remarks = HTMLField(blank=True, help_text=_('not shown on the page'))
def get_company_name(self): def get_company_name(self):
"""Return company or partner name."""
if self.partner: if self.partner:
return self.partner.name return self.partner.name
return self.company_name return self.company_name
def get_company_logo(self): def get_company_logo(self):
"""Return company or partner logo."""
if self.partner: if self.partner:
return self.partner.logo return self.partner.logo
return self.company_logo return self.company_logo
def __str__(self): def __str__(self):
"""Return vacancy partner or company and title."""
return '{} — {}'.format(self.get_company_name(), self.title) return '{} — {}'.format(self.get_company_name(), self.title)
class Meta:
verbose_name_plural = _('Vacancies')
def get_absolute_url(self): def get_absolute_url(self):
"""Return partner or vacancy url."""
url = reverse('partners:vacancies') url = reverse('partners:vacancies')
if self.partner: if self.partner:
url = reverse('partners:partner', args=(self.partner.slug,)) url = reverse('partners:partner', args=(self.partner.slug,))
return '{}#vacancy-{}'.format(url, self.pk) return '{}#vacancy-{}'.format(url, self.pk)
def clean(self): def clean(self):
"""Validate the vacancy."""
super().clean() super().clean()
errors = {} errors = {}
...@@ -172,8 +197,15 @@ class Vacancy(models.Model): ...@@ -172,8 +197,15 @@ class Vacancy(models.Model):
if errors: if errors:
raise ValidationError(errors) raise ValidationError(errors)
class Meta:
"""Meta class for vacancy model."""
verbose_name_plural = _('Vacancies')
class PartnerEvent(models.Model, metaclass=ModelTranslateMeta): class PartnerEvent(models.Model, metaclass=ModelTranslateMeta):
"""Model describing partner event."""
partner = models.ForeignKey( partner = models.ForeignKey(
Partner, Partner,
on_delete=models.CASCADE, on_delete=models.CASCADE,
...@@ -210,6 +242,7 @@ class PartnerEvent(models.Model, metaclass=ModelTranslateMeta): ...@@ -210,6 +242,7 @@ class PartnerEvent(models.Model, metaclass=ModelTranslateMeta):
published = models.BooleanField(_("published"), default=False) published = models.BooleanField(_("published"), default=False)
def clean(self): def clean(self):
"""Validate the partner event."""
super().clean() super().clean()
errors = {} errors = {}
if ((not self.partner and not self.other_partner) or if ((not self.partner and not self.other_partner) or
...@@ -224,4 +257,5 @@ class PartnerEvent(models.Model, metaclass=ModelTranslateMeta): ...@@ -224,4 +257,5 @@ class PartnerEvent(models.Model, metaclass=ModelTranslateMeta):
raise ValidationError(errors) raise ValidationError(errors)
def __str__(self): def __str__(self):
"""Return the event title."""
return self.title return self.title
...@@ -5,29 +5,40 @@ from . import models ...@@ -5,29 +5,40 @@ from . import models
class StaticViewSitemap(sitemaps.Sitemap): class StaticViewSitemap(sitemaps.Sitemap):
"""Sitemap generator for static partner views."""
changefreq = 'daily' changefreq = 'daily'
def items(self): def items(self):
"""Return static partner view names."""
return ['partners:index', 'partners:vacancies'] return ['partners:index', 'partners:vacancies']
def location(self, item): def location(self, item):
"""Return view url."""
return reverse(item) return reverse(item)
class PartnerSitemap(sitemaps.Sitemap): class PartnerSitemap(sitemaps.Sitemap):
"""Sitemap generator for partners."""
def items(self): def items(self):
"""Return all active partners."""
return models.Partner.objects.filter(is_active=True) return models.Partner.objects.filter(is_active=True)
def location(self, item): def location(self, item):
"""Return the partner url."""
return item.get_absolute_url() return item.get_absolute_url()
class VacancySitemap(sitemaps.Sitemap): class VacancySitemap(sitemaps.Sitemap):
"""Sitemap generator for vacancies."""
def items(self): def items(self):
"""Return all vacancies."""
return models.Vacancy.objects.all() return models.Vacancy.objects.all()
def location(self, item): def location(self, item):
"""Return the vacancy url."""
return item.get_absolute_url() return item.get_absolute_url()
......
...@@ -10,6 +10,7 @@ register = template.Library() ...@@ -10,6 +10,7 @@ register = template.Library()
@register.inclusion_tag('partners/banners.html', takes_context=True) @register.inclusion_tag('partners/banners.html', takes_context=True)
def render_partner_banners(context): def render_partner_banners(context):
"""Render the partner banner."""
request = context['request'] request = context['request']
all_partners = Partner.objects.filter(is_active=True).order_by('id') all_partners = Partner.objects.filter(is_active=True).order_by('id')
ids = [partner.id for partner in all_partners] ids = [partner.id for partner in all_partners]
......
...@@ -11,6 +11,7 @@ register = template.Library() ...@@ -11,6 +11,7 @@ register = template.Library()
@register.inclusion_tag('includes/grid_item.html') @register.inclusion_tag('includes/grid_item.html')
def partner_card(partner): def partner_card(partner):
"""Return grid item showing partner."""
image_url = '' image_url = ''
if partner.logo: if partner.logo:
image_url = get_thumbnail_url(partner.logo, image_url = get_thumbnail_url(partner.logo,
...@@ -30,6 +31,7 @@ def partner_card(partner): ...@@ -30,6 +31,7 @@ def partner_card(partner):
@register.inclusion_tag('includes/grid_item.html') @register.inclusion_tag('includes/grid_item.html')
def partner_image_card(image): def partner_image_card(image):
"""Return grid item showing partner image."""
class_name = 'partner-image-card' class_name = 'partner-image-card'
image_url = get_thumbnail_url(image, settings.THUMBNAIL_SIZES['medium']) image_url = get_thumbnail_url(image, settings.THUMBNAIL_SIZES['medium'])
...@@ -45,6 +47,7 @@ def partner_image_card(image): ...@@ -45,6 +47,7 @@ def partner_image_card(image):
@register.inclusion_tag('partners/vacancy_card.html') @register.inclusion_tag('partners/vacancy_card.html')
def vacancy_card(vacancy): def vacancy_card(vacancy):
"""Return grid item showing vacancy."""
image_url = None image_url = None
if vacancy.get_company_logo(): if vacancy.get_company_logo():
image_url = get_thumbnail_url(vacancy.get_company_logo(), image_url = get_thumbnail_url(vacancy.get_company_logo(),
......
...@@ -7,6 +7,7 @@ from partners.models import Partner, Vacancy, VacancyCategory ...@@ -7,6 +7,7 @@ from partners.models import Partner, Vacancy, VacancyCategory
def index(request): def index(request):
"""View to show overview page of partners."""
partners = Partner.objects.filter(is_active=True, is_main_partner=False) partners = Partner.objects.filter(is_active=True, is_main_partner=False)
try: try:
main_partner = Partner.objects.get( main_partner = Partner.objects.get(
...@@ -24,6 +25,7 @@ def index(request): ...@@ -24,6 +25,7 @@ def index(request):
def partner(request, slug): def partner(request, slug):
"""View to show partner page."""
partner = get_object_or_404(Partner, slug=slug) partner = get_object_or_404(Partner, slug=slug)
context = { context = {
'partner': partner, 'partner': partner,
...@@ -33,6 +35,7 @@ def partner(request, slug): ...@@ -33,6 +35,7 @@ def partner(request, slug):