Пример #1
0
class ApplicationBase(EmailForm, WorkflowStreamForm):  # type: ignore
    is_createable = False

    # Adds validation around forms & workflows. Isn't on Workflow class due to not displaying workflow field on Round
    base_form_class = WorkflowFormAdminForm

    reviewers = ParentalManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='%(class)s_reviewers',
        limit_choices_to=LIMIT_TO_REVIEWERS,
        blank=True,
    )

    slack_channel = models.CharField(blank=True, max_length=128, help_text=_('The slack #channel for notifications.'))

    objects = PageManager.from_queryset(ApplicationBaseManager)()

    parent_page_types = ['apply_home.ApplyHomePage']

    def get_template(self, request, *args, **kwargs):
        # We want to force children to use our base template
        # template attribute is ignored by children
        return 'funds/application_base.html'

    def detail(self):
        # The location to find out more information
        return self.application_public.first()

    @cached_property
    def open_round(self):
        return RoundBase.objects.child_of(self).open().first()

    def next_deadline(self):
        try:
            return self.open_round.end_date
        except AttributeError:
            # There isn't an open round
            return None

    def serve(self, request):
        if hasattr(request, 'is_preview') or not self.open_round:
            return super().serve(request)

        # delegate to the open_round to use the latest form instances
        request.show_round = True
        return self.open_round.serve(request)

    content_panels = WorkflowStreamForm.content_panels + [
        FieldPanel('reviewers'),
        FieldPanel('slack_channel'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        EmailForm.email_tab,
        ObjectList(WorkflowStreamForm.promote_panels, heading='Promote'),
    ])
Пример #2
0
class PersonPage(FundingMixin, BasePage):
    subpage_types = []
    parent_page_types = ['PersonIndexPage']

    drupal_id = models.IntegerField(null=True, blank=True, editable=False)

    first_name = models.CharField(max_length=255, blank=True)
    last_name = models.CharField(max_length=255)
    photo = models.ForeignKey(
        'images.CustomImage',
        null=True,
        blank=True,
        related_name='+',
        on_delete=models.SET_NULL
    )
    active = models.BooleanField(default=True)
    job_title = models.CharField(max_length=255, blank=True)
    introduction = models.TextField(blank=True)
    website = models.URLField(blank=True, max_length=255)
    biography = StreamField(StoryBlock(), blank=True)
    email = models.EmailField(blank=True)

    objects = PageManager.from_queryset(PersonQuerySet)()

    search_fields = BasePage.search_fields + [
        index.SearchField('introduction'),
        index.SearchField('biography')
    ]

    content_panels = BasePage.content_panels + [
        MultiFieldPanel([
            FieldPanel('first_name'),
            FieldPanel('last_name'),
        ], heading="Name"),
        FieldPanel('active'),
        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('funds_reviewed', label='Funds Reviewed'),
    ] + FundingMixin.content_panels
Пример #3
0
class RoundBase(WorkflowStreamForm, SubmittableStreamForm):  # type: ignore
    is_creatable = False
    submission_class = ApplicationSubmission

    objects = PageManager.from_queryset(RoundBaseManager)()

    subpage_types = []  # type: ignore

    # Adds validation for making start_date required
    base_form_class = RoundBasePageAdminForm

    lead = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        limit_choices_to=LIMIT_TO_STAFF,
        related_name='%(class)s_lead',
        on_delete=models.PROTECT,
    )
    reviewers = ParentalManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='%(class)s_reviewer',
        limit_choices_to=LIMIT_TO_REVIEWERS,
        blank=True,
    )
    start_date = models.DateField(null=True, blank=True, default=date.today)
    end_date = models.DateField(
        blank=True,
        null=True,
        default=date.today,
        help_text='When no end date is provided the round will remain open indefinitely.'
    )
    sealed = models.BooleanField(default=False)

    content_panels = SubmittableStreamForm.content_panels + [
        FieldPanel('lead'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('start_date'),
                FieldPanel('end_date'),
            ]),
        ], heading="Dates"),
        FieldPanel('reviewers', widget=forms.SelectMultiple(attrs={'size': '16'})),
        ReadOnlyPanel('get_workflow_name_display', heading="Workflow", help_text="Copied from the fund."),
        # Forms comes from parental key in models/forms.py
        ReadOnlyInlinePanel('forms', help_text="Copied from the fund."),
        ReadOnlyInlinePanel('review_forms', help_text="Copied from the fund."),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(SubmittableStreamForm.promote_panels, heading='Promote'),
    ])

    def get_template(self, request, *args, **kwargs):
        # Make sure all children use the shared template
        return 'funds/round.html'

    def get_landing_page_template(self, request, *args, **kwargs):
        # Make sure all children use the shared template
        return 'funds/round_landing.html'

    @cached_property
    def fund(self):
        return self.get_parent()

    @property
    def is_sealed(self):
        return self.sealed and self.is_open

    @property
    def is_open(self):
        return self.start_date <= date.today() <= self.end_date

    def save(self, *args, **kwargs):
        is_new = not self.id
        if is_new and hasattr(self, 'parent_page'):
            parent_page = self.parent_page[self.__class__][self.title]
            self.workflow_name = parent_page.workflow_name
            self.reviewers = parent_page.reviewers.all()

        super().save(*args, **kwargs)

        if is_new and hasattr(self, 'parent_page'):
            # Would be nice to do this using model clusters as part of the __init__
            self._copy_forms('forms')
            self._copy_forms('review_forms')

    def _copy_forms(self, field):
        for form in getattr(self.get_parent().specific, field).all():
            new_form = self._meta.get_field(field).related_model
            self._copy_form(form, new_form)

    def _copy_form(self, form, new_class):
        # Create a copy of the existing form object
        new_form = form.form
        new_form.id = None
        new_form.name = '{} for {} ({})'.format(new_form.name, self.title, self.get_parent().title)
        new_form.save()
        if hasattr(form, 'stage'):
            new_class.objects.create(round=self, form=new_form, stage=form.stage)
        else:
            new_class.objects.create(round=self, form=new_form)

    def get_submit_meta_data(self, **kwargs):
        return super().get_submit_meta_data(
            page=self.get_parent(),
            round=self,
            **kwargs,
        )

    def clean(self):
        super().clean()

        conflict_query = ()

        if self.start_date and self.end_date and self.start_date > self.end_date:
            raise ValidationError({
                'end_date': 'End date must come after the start date',
            })

        if self.start_date and self.end_date:
            conflict_query = (
                Q(start_date__range=[self.start_date, self.end_date]) |
                Q(end_date__range=[self.start_date, self.end_date]) |
                Q(start_date__lte=self.start_date, end_date__gte=self.end_date)
            )
        elif self.start_date:
            conflict_query = (
                Q(start_date__lte=self.start_date, end_date__isnull=True) |
                Q(end_date__gte=self.start_date)
            )

        if not self.id and hasattr(self, 'parent_page'):
            # Check if the create hook has added the parent page, we aren't an object yet.
            # Ensures we can access related objects during the clean phase instead of save.
            base_query = RoundBase.objects.child_of(self.parent_page[self.__class__][self.title])
        else:
            # don't need parent page, we are an actual object now.
            base_query = RoundBase.objects.sibling_of(self)

        if conflict_query:
            conflicting_rounds = base_query.filter(
                conflict_query
            ).exclude(id=self.id)

            if conflicting_rounds.exists():
                error_message = mark_safe('Overlaps with the following rounds:<br> {}'.format(
                    '<br>'.join([
                        f'<a href="{admin_url(round)}">{round.title}</a>: {round.start_date} - {round.end_date}'
                        for round in conflicting_rounds]
                    )
                ))
                error = {
                    'start_date': error_message,
                }
                if self.end_date:
                    error['end_date'] = error_message

                raise ValidationError(error)

    def get_initial_data_open_call_submission(self, submission_id):
        initial_values = {}

        try:
            submission_class = self.get_submission_class()
            submission = submission_class.objects.get(id=submission_id)
            if submission.status in OPEN_CALL_PHASES and self.get_parent() == submission.page:
                title_block_id = submission.named_blocks.get('title')
                if title_block_id:
                    field_data = submission.data(title_block_id)
                    initial_values[title_block_id] = field_data + ' (please edit)'

                for field_id in submission.first_group_normal_text_blocks:
                    field_data = submission.data(field_id)
                    initial_values[field_id] = field_data

                # Select first item in the Group toggle blocks
                for toggle_block_id, toggle_field in submission.group_toggle_blocks:
                    try:
                        initial_values[toggle_block_id] = toggle_field.value['choices'][0]
                    except IndexError:
                        initial_values[toggle_block_id] = 'yes'
                    except KeyError:
                        pass

        except (submission_class.DoesNotExist, ValueError):
            pass

        return initial_values

    def get_form_parameters(self, submission_id=None):
        form_parameters = {}

        if submission_id:
            initial_values = self.get_initial_data_open_call_submission(submission_id)
            if initial_values:
                form_parameters['initial'] = initial_values

        return form_parameters

    def get_form(self, *args, **kwargs):
        draft = kwargs.pop('draft', False)
        form_class = self.get_form_class(draft)
        submission_id = kwargs.pop('submission_id', None)
        if submission_id:
            form_params = self.get_form_parameters(submission_id=submission_id)
        else:
            form_params = self.get_form_parameters()
        form_params.update(kwargs)
        return form_class(*args, **form_params)

    def serve(self, request, *args, **kwargs):
        if hasattr(request, 'is_preview') or hasattr(request, 'show_round'):
            # Overriding serve method to pass submission id to get_form method
            copy_open_submission = request.GET.get('open_call_submission')
            if request.method == 'POST':
                draft = request.POST.get('draft', False)
                form = self.get_form(request.POST, request.FILES, page=self, user=request.user, draft=draft)

                if form.is_valid():
                    form_submission = self.process_form_submission(form, draft=draft)
                    return self.render_landing_page(request, form_submission, *args, **kwargs)
            else:
                form = self.get_form(page=self, user=request.user, submission_id=copy_open_submission)

            context = self.get_context(request)
            context['form'] = form
            context['show_all_group_fields'] = True if copy_open_submission else False
            return render(
                request,
                self.get_template(request),
                context
            )

        # We hide the round as only the open round is used which is displayed through the
        # fund page
        raise Http404()
Пример #4
0
    def recent(self):
        """Find past events, most recent first.  Only includes events
        with end date in the past."""
        now = timezone.now()
        # construct a datetime based on now but with zero hour/minute/second
        today = datetime(now.year,
                         now.month,
                         now.day,
                         tzinfo=timezone.get_default_timezone())
        return self.filter(end_time__lt=today).order_by("-start_time")


# custom manager for wagtail pages, see:
# https://docs.wagtail.io/en/stable/topics/pages.html#custom-page-managers
EventManager = PageManager.from_queryset(EventQuerySet)


class EventTag(TaggedItemBase):
    """Tags for Event pages."""

    content_object = ParentalKey("events.Event",
                                 on_delete=models.CASCADE,
                                 related_name="tagged_items")


class Speaker(models.Model):
    """Relationship between Person and Event."""

    event = ParentalKey("events.Event", related_name="speakers")
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
Пример #5
0
class RoundBase(WorkflowStreamForm, SubmittableStreamForm):  # type: ignore
    is_creatable = False
    submission_class = ApplicationSubmission

    objects = PageManager.from_queryset(RoundBaseManager)()

    subpage_types = []  # type: ignore

    lead = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        limit_choices_to=LIMIT_TO_STAFF,
        related_name='%(class)s_lead',
        on_delete=models.PROTECT,
    )
    reviewers = ParentalManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='%(class)s_reviewer',
        limit_choices_to=LIMIT_TO_REVIEWERS,
        blank=True,
    )
    start_date = models.DateField(default=date.today)
    end_date = models.DateField(
        blank=True,
        null=True,
        default=date.today,
        help_text='When no end date is provided the round will remain open indefinitely.'
    )
    sealed = models.BooleanField(default=False)

    content_panels = SubmittableStreamForm.content_panels + [
        FieldPanel('lead'),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('start_date'),
                FieldPanel('end_date'),
            ]),
        ], heading="Dates"),
        FieldPanel('reviewers'),
        ReadOnlyPanel('get_workflow_name_display', heading="Workflow"),
        # Forms comes from parental key in models/forms.py
        ReadOnlyInlinePanel('forms', help_text="Copied from the fund."),
        ReadOnlyInlinePanel('review_forms', help_text="Copied from the fund."),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(SubmittableStreamForm.promote_panels, heading='Promote'),
    ])

    def get_template(self, request, *args, **kwargs):
        # Make sure all children use the shared template
        return 'funds/round.html'

    def get_landing_page_template(self, request, *args, **kwargs):
        # Make sure all children use the shared template
        return 'funds/round_landing.html'

    @cached_property
    def fund(self):
        return self.get_parent()

    @property
    def is_sealed(self):
        return self.sealed and self.is_open

    @property
    def is_open(self):
        return self.start_date <= date.today() <= self.end_date

    def save(self, *args, **kwargs):
        is_new = not self.id
        if is_new and hasattr(self, 'parent_page'):
            parent_page = self.parent_page[self.__class__][self.title]
            self.workflow_name = parent_page.workflow_name
            self.reviewers = parent_page.reviewers.all()

        super().save(*args, **kwargs)

        if is_new and hasattr(self, 'parent_page'):
            # Would be nice to do this using model clusters as part of the __init__
            self._copy_forms('forms')
            self._copy_forms('review_forms')

    def _copy_forms(self, field):
        for form in getattr(self.get_parent().specific, field).all():
            new_form = self._meta.get_field(field).related_model
            self._copy_form(form, new_form)

    def _copy_form(self, form, new_class):
        # Create a copy of the existing form object
        new_form = form.form
        new_form.id = None
        new_form.name = '{} for {} ({})'.format(new_form.name, self.title, self.get_parent().title)
        new_form.save()
        new_class.objects.create(round=self, form=new_form)

    def get_submit_meta_data(self, **kwargs):
        return super().get_submit_meta_data(
            page=self.get_parent(),
            round=self,
            **kwargs,
        )

    def clean(self):
        super().clean()

        if self.end_date and self.start_date > self.end_date:
            raise ValidationError({
                'end_date': 'End date must come after the start date',
            })

        if self.end_date:
            conflict_query = (
                Q(start_date__range=[self.start_date, self.end_date]) |
                Q(end_date__range=[self.start_date, self.end_date]) |
                Q(start_date__lte=self.start_date, end_date__gte=self.end_date)
            )
        else:
            conflict_query = (
                Q(start_date__lte=self.start_date, end_date__isnull=True) |
                Q(end_date__gte=self.start_date)
            )

        if not self.id and hasattr(self, 'parent_page'):
            # Check if the create hook has added the parent page, we aren't an object yet.
            # Ensures we can access related objects during the clean phase instead of save.
            base_query = RoundBase.objects.child_of(self.parent_page[self.__class__][self.title])
        else:
            # don't need parent page, we are an actual object now.
            base_query = RoundBase.objects.sibling_of(self)

        conflicting_rounds = base_query.filter(
            conflict_query
        ).exclude(id=self.id)

        if conflicting_rounds.exists():
            error_message = mark_safe('Overlaps with the following rounds:<br> {}'.format(
                '<br>'.join([
                    f'<a href="{admin_url(round)}">{round.title}</a>: {round.start_date} - {round.end_date}'
                    for round in conflicting_rounds]
                )
            ))
            error = {
                'start_date': error_message,
            }
            if self.end_date:
                error['end_date'] = error_message

            raise ValidationError(error)

    def serve(self, request):
        if hasattr(request, 'is_preview') or hasattr(request, 'show_round'):
            return super().serve(request)

        # We hide the round as only the open round is used which is displayed through the
        # fund page
        raise Http404()
Пример #6
0
            data = self.file.read(256)
            if not data:
                break
            hash256.update(data)
        checksum = hash256.hexdigest()

        self.file.seek(original_position)
        return os.path.join(folder_name, checksum[:3], filename)


class CustomPageQuerySet(PageQuerySet):
    def about_spam(self):
        return self.filter(title__contains='spam')


CustomManager = PageManager.from_queryset(CustomPageQuerySet)


class CustomManagerPage(Page):
    objects = CustomManager()


class MyBasePage(Page):
    """
    A base Page model, used to set site-wide defaults and overrides.
    """
    objects = CustomManager()

    class Meta:
        abstract = True
Пример #7
0
import datetime

from django.db.models import Q

from wagtail.core.query import PageQuerySet
from wagtail.core.models import PageManager


class ActiveInactivePageQuerySet(PageQuerySet):
    def active(self):
        td = datetime.datetime.today()
        return self.filter(Q(expire_at__isnull=True) | Q(expire_at__gt=td))

    def inactive(self):
        td = datetime.datetime.today()
        return self.filter(expire_at__lte=td)


ActiveInactivePageManager = PageManager.from_queryset(
    ActiveInactivePageQuerySet)
Пример #8
0
        """Include only projects with the working group flag set"""
        return self.filter(working_group=True)

    def order_by_newest_grant(self):
        """order by grant start date, most recent grants first; secondary
        sort by project title"""
        # NOTE: using annotation to get just the most recent start date
        # to avoid issues with projects appearing multiple times.
        return self.annotate(
            last_start=models.Max("grants__start_date")).order_by(
                "-last_start", "title")


# custom manager for wagtail pages, see:
# https://docs.wagtail.io/en/stable/topics/pages.html#custom-page-managers
ProjectManager = PageManager.from_queryset(ProjectQuerySet)


class ProjectTag(TaggedItemBase):
    """Tags for Project pages."""

    content_object = ParentalKey("projects.Project",
                                 on_delete=models.CASCADE,
                                 related_name="tagged_items")


class Project(BasePage, ClusterableModel):
    """Page type for a CDH sponsored project or working group."""

    short_description = models.CharField(
        max_length=255,
Пример #9
0
from v1.models import CFGOVPage
from v1.models.snippets import ReusableText


class JobListingPageQuerySet(PageQuerySet):
    def open(self):
        today = timezone.now().date()

        return self \
            .filter(live=True) \
            .filter(open_date__lte=today) \
            .filter(close_date__gte=today) \
            .order_by('close_date', 'title')


JobListingPageManager = PageManager.from_queryset(JobListingPageQuerySet)

LOCATION_HELP_TEXT = ("Select <strong>either</strong> one or more offices "
                      "<strong>or</strong> one or more regions.")


class JobListingPageForm(WagtailAdminPageForm):
    def clean(self):
        cleaned_data = super().clean()

        offices = cleaned_data.get('offices')
        has_offices = offices.exists() if offices else False

        regions = cleaned_data.get('regions')
        has_regions = regions.exists() if regions else False
Пример #10
0
class BlogPostQuerySet(PageQuerySet):
    def recent(self):
        """Order blog posts by date published."""
        # NOTE we can't use ordering on the model to do this by default, so we
        # have to make sure to call this method instead. See:
        # https://docs.wagtail.io/en/stable/topics/pages.html#page-queryset-ordering
        return self.order_by("-first_published_at")

    def featured(self):
        """return blog posts that are marked as featured"""
        return self.filter(featured=True)


# custom manager for wagtail pages, see:
# https://docs.wagtail.io/en/stable/topics/pages.html#custom-page-managers
BlogPostManager = PageManager.from_queryset(BlogPostQuerySet)


class BlogPostTag(TaggedItemBase):
    """Tags for Blog posts."""

    content_object = ParentalKey("blog.BlogPost",
                                 on_delete=models.CASCADE,
                                 related_name="tagged_items")


class BlogPost(BasePage, ClusterableModel, PagePreviewDescriptionMixin):
    """A Blog post, implemented as a Wagtail page."""

    featured_image = models.ForeignKey(
        "wagtailimages.image",
Пример #11
0
    def upcoming(self):
        return self.end_after(localdate())

    def previous(self):
        return self.start_before(localdate())

    def date_range(self, date_range):
        from_date, to_date = date_range
        return self.end_after(from_date).start_before(to_date)

    def for_group(self, group):
        return self.filter(Q(group=group) | Q(shadow_events__group=group))


EventPageManager = PageManager.from_queryset(EventPageQuerySet)


class EventPage(XrPage):
    template = "xr_events/pages/event_detail.html"
    content = StreamField(
        ContentBlock,
        blank=True,
        help_text=_("The content is only visible on the detail page."),
    )
    location = models.CharField(
        max_length=255,
        blank=True,
        help_text=_(
            "Some city or address, like you would enter in GMaps or OpenStreetMap, "
            'e.g. "Berlin", "Somestreet 84, 12345 Samplecity".'),
Пример #12
0
                                       minute=0,
                                       second=0,
                                       microsecond=0)
        current_season = Concert.calculate_season(today)
        return Concert.objects.\
            annotate(first_date=Min('concert_date__date')).\
            annotate(last_date=Max('concert_date__date')).\
            filter(
                last_date__lte=today,
                season=current_season,
                live=True,
                concert_date__date__isnull=False).distinct().\
            order_by('first_date')


ConcertManager = PageManager.from_queryset(ConcertQuerySet)


class ConcertAdminForm(WagtailAdminPageForm):
    def __init__(self,
                 data=None,
                 files=None,
                 parent_page=None,
                 *args,
                 **kwargs):
        super().__init__(data, files, *args, **kwargs)
        # Limit performer choices to those listed as performers in
        # child Performance pages
        instance = kwargs.get('instance')
        if instance.id:
            perfs = Performance.objects.live().descendant_of(self.instance)