from django.db import models from django.core.exceptions import ValidationError from django.utils.functional import cached_property from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.conf import settings from modelcluster.fields import ParentalKey from wagtail.wagtailcore.models import Orderable from wagtail.wagtailcore.fields import StreamField from wagtail.wagtailadmin.edit_handlers import ( FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel, PageChooserPanel, StreamFieldPanel ) from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from opentech.public.utils.blocks import StoryBlock from opentech.public.utils.models import BasePage class SocialMediaProfile(models.Model): person_page = ParentalKey( 'PersonPage', related_name='social_media_profile' ) site_titles = ( ('twitter', "Twitter"), ('linkedin', "LinkedIn"), ) site_urls = ( ('twitter', 'https://twitter.com/'), ('linkedin', 'https://www.linkedin.com/in/') ) service = models.CharField( max_length=200, choices=site_titles ) username = models.CharField(max_length=255) @property def profile_url(self): return dict(self.site_urls)[self.service] + self.username def clean(self): if self.service == 'twitter' and self.username.startswith('@'): self.username = self.username[1:] class PersonType(models.Model): title = models.CharField(max_length=255) def __str__(self): return self.title class PersonPagePersonType(models.Model): page = ParentalKey('PersonPage', related_name='person_types') person_type = models.ForeignKey( 'PersonType', related_name='+' ) panels = [ FieldPanel('person_type') ] def __str__(self): return self.person_type.title class Funding(Orderable): page = ParentalKey('PersonPage', related_name='funding') value = models.PositiveIntegerField() year = models.PositiveIntegerField() duration = models.PositiveIntegerField(help_text='In months') source = models.ForeignKey( 'wagtailcore.Page', blank=True, null=True, on_delete=models.SET_NULL ) panels = [ FieldRowPanel([ FieldPanel('year'), FieldPanel('value'), FieldPanel('duration'), ]), # This is stubbed as we need to be able to select from multiple PageChooserPanel('source'), ] class PersonContactInfomation(Orderable): methods = ( ('irc', 'IRC'), ('im_jabber_xmpp', 'IM/Jabber/XMPP'), ('phone', 'Phone'), ('pgp', 'PGP fingerprint'), ('otr', 'OTR fingerprint'), ) page = ParentalKey('PersonPage', related_name='contact_details') contact_method = models.CharField(max_length=255, choices=methods, blank=True) other_method = models.CharField(max_length=255, blank=True, verbose_name='Other') contact_detail = models.CharField(max_length=255) panels = [ FieldRowPanel([ FieldPanel('contact_method'), FieldPanel('other_method'), ]), FieldPanel('contact_detail'), ] @property def method_display(self): return self.other_method or self.get_contact_method_display() def clean(self): if not (self.contact_method or self.other_method): raise ValidationError({ 'contact_method': 'Please select or type at least one contact method.', 'other_method': '', }) if self.contact_method and self.other_method: raise ValidationError({ 'contact_method': 'Please only select or type one contact method.', 'other_method': '', }) class PersonPage(BasePage): subpage_types = [] parent_page_types = ['PersonIndexPage'] first_name = models.CharField(max_length=255) last_name = models.CharField(max_length=255) photo = models.ForeignKey( 'images.CustomImage', null=True, blank=True, related_name='+', on_delete=models.SET_NULL ) job_title = models.CharField(max_length=255) introduction = models.TextField(blank=True) website = models.URLField(blank=True, max_length=255) biography = StreamField(StoryBlock(), blank=True) email = models.EmailField(blank=True) content_panels = BasePage.content_panels + [ MultiFieldPanel([ FieldPanel('first_name'), FieldPanel('last_name'), ], heading="Name"), ImageChooserPanel('photo'), FieldPanel('job_title'), InlinePanel('social_media_profile', label='Social accounts'), FieldPanel('website'), MultiFieldPanel([ FieldPanel('email'), InlinePanel('contact_details', label='Other Contact Methods'), ], heading='Contact information'), InlinePanel('person_types', label='Person types'), FieldPanel('introduction'), StreamFieldPanel('biography'), InlinePanel('funding', label='Funding'), ] class PersonIndexPage(BasePage): subpage_types = ['PersonPage'] parent_page_types = ['home.HomePage'] @cached_property def people(self): return self.get_children().specific().live().public() def get_context(self, request, *args, **kwargs): page_number = request.GET.get('page') paginator = Paginator(self.people, settings.DEFAULT_PER_PAGE) try: people = paginator.page(page_number) except PageNotAnInteger: people = paginator.page(1) except EmptyPage: people = paginator.page(paginator.num_pages) context = super().get_context(request, *args, **kwargs) context.update(people=people) return context