Skip to content

Applications

hypha.apply.funds.models.applications

ApplicationBaseManager

Bases: PageQuerySet

order_by_end_date

order_by_end_date()
Source code in hypha/apply/funds/models/applications.py
def order_by_end_date(self):
    # OutRef path__startswith with find all descendants of the parent
    # We only have children, so no issues at this time
    rounds = RoundBase.objects.open().filter(path__startswith=OuterRef("path"))
    qs = (
        self.public()
        .live()
        .annotate(end_date=Subquery(rounds.values("end_date")[:1]))
    )
    return qs.order_by("end_date")

ApplicationBase

Bases: EmailForm, WorkflowStreamForm

submission_form_class class-attribute instance-attribute

submission_form_class = PageStreamBaseForm

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

WORKFLOW_CHOICES class-attribute instance-attribute

WORKFLOW_CHOICES = {name: _4eNfor (name, workflow) in items()}

workflow_name class-attribute instance-attribute

workflow_name = CharField(choices=items(), max_length=100, default='single', verbose_name=gettext_lazy('Workflow'))

workflow property

workflow

confirmation_text_extra class-attribute instance-attribute

confirmation_text_extra = TextField(blank=True, help_text=gettext_lazy('Additional text for the application confirmation message.'))

email_confirmation_panels class-attribute instance-attribute

email_confirmation_panels = [MultiFieldPanel([FieldRowPanel([FieldPanel('from_address', classname='col6'), FieldPanel('to_address', classname='col6')]), FieldPanel('subject'), FieldPanel('confirmation_text_extra')], heading=gettext_lazy('Confirmation email'))]

email_tab class-attribute instance-attribute

email_tab = ObjectList(email_confirmation_panels, heading=gettext_lazy('Confirmation email'))

is_createable class-attribute instance-attribute

is_createable = False

base_form_class class-attribute instance-attribute

base_form_class = WorkflowFormAdminForm

reviewers class-attribute instance-attribute

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

image class-attribute instance-attribute

image = ForeignKey('images.CustomImage', null=True, blank=True, on_delete=SET_NULL, related_name='+')

description class-attribute instance-attribute

description = TextField(null=True, blank=True)

weight class-attribute instance-attribute

weight = PositiveIntegerField(default=1, blank=True, validators=[MinValueValidator(1), MaxValueValidator(100)])
guide_link = URLField(blank=True, max_length=255, help_text=gettext_lazy('Link to the apply guide.'))

slack_channel class-attribute instance-attribute

slack_channel = CharField(blank=True, max_length=128, help_text=gettext_lazy('The slack #channel for notifications. If left empty, notifications will go to the default channel.'))

activity_digest_recipient_emails class-attribute instance-attribute

activity_digest_recipient_emails = ArrayField(EmailField(default=''), blank=True, null=True, help_text=gettext_lazy('Comma separated list of emails where a summary of all the activities related to this fund will be sent.'))

list_on_front_page class-attribute instance-attribute

list_on_front_page = BooleanField(default=True, help_text=gettext_lazy('Should the fund be listed on the front page.'))

show_deadline class-attribute instance-attribute

show_deadline = BooleanField(default=True, help_text=gettext_lazy('Should the deadline date be visible for users.'))

objects class-attribute instance-attribute

objects = from_queryset(ApplicationBaseManager)()

parent_page_types class-attribute instance-attribute

parent_page_types = ['apply_home.ApplyHomePage']

content_panels class-attribute instance-attribute

content_panels = content_panels + [FieldPanel('reviewers', widget=CheckboxSelectMultiple), FieldPanel('guide_link'), FieldPanel('description'), FieldPanel('image'), FieldPanel('weight'), FieldPanel('slack_channel'), FieldPanel('activity_digest_recipient_emails'), FieldPanel('list_on_front_page'), FieldPanel('show_deadline')]

edit_handler class-attribute instance-attribute

edit_handler = TabbedInterface([ObjectList(content_panels, heading=gettext_lazy('Content')), email_tab, ObjectList(promote_panels, heading=gettext_lazy('Promote'))])

Meta

abstract class-attribute instance-attribute
abstract = True

from_db classmethod

from_db(db, field_names, values)
Source code in hypha/apply/stream_forms/models.py
@classmethod
def from_db(cls, db, field_names, values):
    instance = super().from_db(db, field_names, values)
    if "form_data" in field_names:
        instance.form_data = cls.deserialize_form_data(
            instance, instance.form_data, instance.form_fields
        )
    return instance

deserialize_form_data classmethod

deserialize_form_data(instance, form_data, form_fields)
Source code in hypha/apply/stream_forms/models.py
@classmethod
def deserialize_form_data(cls, instance, form_data, form_fields):
    data = form_data.copy()
    # PERFORMANCE NOTE:
    # Do not attempt to iterate over form_fields - that will fully instantiate the form_fields
    # including any sub queries that they do
    for _i, field_data in enumerate(form_fields.raw_data):
        block = form_fields.stream_block.child_blocks[field_data["type"]]
        field_id = field_data.get("id")
        try:
            value = data[field_id]
        except KeyError:
            pass
        else:
            data[field_id] = block.decode(value)
    return data

get_defined_fields

get_defined_fields(stage=None, form_index=0)
Source code in hypha/apply/funds/models/utils.py
def get_defined_fields(self, stage=None, form_index=0):
    if not stage:
        stage_num = 1
    else:
        stage_num = self.workflow.stages.index(stage) + 1
    return self.forms.filter(stage=stage_num)[form_index].fields

get_form_fields

get_form_fields(draft=False, form_data=None, user=None)
Source code in hypha/apply/stream_forms/models.py
def get_form_fields(self, draft=False, form_data=None, user=None):
    if form_data is None:
        form_data = {}

    form_fields = OrderedDict()
    field_blocks = self.get_defined_fields()
    group_counter = 1
    is_in_group = False

    # If true option 1 is selected
    grouped_fields_visible = False
    for struct_child in field_blocks:
        block = struct_child.block
        struct_value = struct_child.value
        if isinstance(block, FormFieldBlock):
            field_from_block = block.get_field(struct_value)
            disabled_help_text = _(
                "You are logged in so this information is fetched from your user account."
            )
            if isinstance(block, FullNameBlock) and user and user.is_authenticated:
                if user.full_name:
                    field_from_block.disabled = True
                    field_from_block.initial = user.full_name
                    field_from_block.help_text = disabled_help_text
                else:
                    field_from_block.help_text = _(
                        "You are logged in but your user account does not have a "
                        "full name. We'll update your user account with the name you provide here."
                    )
            if isinstance(block, EmailBlock) and user and user.is_authenticated:
                field_from_block.disabled = True
                field_from_block.initial = user.email
                field_from_block.help_text = disabled_help_text
            if draft and not issubclass(
                block.__class__, ApplicationMustIncludeFieldBlock
            ):
                field_from_block.required = False
            field_from_block.help_link = struct_value.get("help_link")
            field_from_block.group_number = group_counter if is_in_group else 1
            if isinstance(block, GroupToggleBlock) and not is_in_group:
                field_from_block.group_number = 1
                field_from_block.grouper_for = group_counter + 1
                group_counter += 1
                is_in_group = True
                grouped_fields_visible = (
                    form_data.get(struct_child.id) == field_from_block.choices[0][0]
                )
            if isinstance(block, TextFieldBlock):
                field_from_block.word_limit = struct_value.get("word_limit")
            if isinstance(block, MultiInputCharFieldBlock):
                number_of_inputs = struct_value.get("number_of_inputs")
                for index in range(number_of_inputs):
                    form_fields[struct_child.id + "_" + str(index)] = (
                        field_from_block
                    )
                    field_from_block.multi_input_id = struct_child.id
                    field_from_block.add_button_text = struct_value.get(
                        "add_button_text"
                    )
                    if (
                        index == number_of_inputs - 1
                    ):  # Add button after last input field
                        field_from_block.multi_input_add_button = True
                        # Index for field until which fields will be visible to applicant.
                        # Initially only the first field with id UUID_0 will be visible.
                        field_from_block.visibility_index = 0
                        field_from_block.max_index = index
                    if index != 0:
                        field_from_block.multi_input_field = True
                        field_from_block.required = False
                        field_from_block.initial = None
                    field_from_block = copy.copy(field_from_block)
            else:
                if is_in_group and not isinstance(block, GroupToggleBlock):
                    field_from_block.required_when_visible = (
                        field_from_block.required
                    )
                    field_from_block.required = (
                        field_from_block.required & grouped_fields_visible
                    )
                    field_from_block.visible = grouped_fields_visible
                form_fields[struct_child.id] = field_from_block
        elif isinstance(block, GroupToggleEndBlock):
            # Group toggle end block is used only to group fields and not used in actual form.
            # Todo: Use streamblock to create nested form field blocks, a more elegant method to group form fields.
            is_in_group = False
        else:
            field_wrapper = BlockFieldWrapper(struct_child)
            field_wrapper.group_number = group_counter if is_in_group else 1
            form_fields[struct_child.id] = field_wrapper

    return form_fields

get_form_class

get_form_class(draft=False, form_data=None, user=None)
Source code in hypha/apply/stream_forms/models.py
def get_form_class(self, draft=False, form_data=None, user=None):
    return type(
        "WagtailStreamForm",
        (self.submission_form_class,),
        self.get_form_fields(draft, form_data, user),
    )

render_landing_page

render_landing_page(request, form_submission=None, *args, **kwargs)
Source code in hypha/apply/funds/models/utils.py
def render_landing_page(self, request, form_submission=None, *args, **kwargs):
    # We only reach this page after creation of a new submission
    # Hook in to notify about new applications
    if form_submission.status == DRAFT_STATE:
        messenger(
            MESSAGES.DRAFT_SUBMISSION,
            request=request,
            user=form_submission.user,
            source=form_submission,
        )
    else:
        messenger(
            MESSAGES.NEW_SUBMISSION,
            request=request,
            user=form_submission.user,
            source=form_submission,
        )

    return redirect("apply:submissions:success", pk=form_submission.id)

send_mail

send_mail(submission)
Source code in hypha/apply/funds/models/utils.py
def send_mail(self, submission):
    # Make sure we don't send emails to users here. Messaging handles that
    pass

get_template

get_template(request, *args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
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"

open_round

open_round()
Source code in hypha/apply/funds/models/applications.py
@cached_property
def open_round(self):
    return RoundBase.objects.child_of(self).open().first()

next_deadline

next_deadline()
Source code in hypha/apply/funds/models/applications.py
def next_deadline(self):
    try:
        return self.open_round.end_date
    except AttributeError:
        # There isn't an open round
        return None

serve

serve(request)
Source code in hypha/apply/funds/models/applications.py
def serve(self, request):
    # Manually do what the login_required decorator does so that we can check settings
    if not request.user.is_authenticated and settings.FORCE_LOGIN_FOR_APPLICATION:
        return redirect(
            "%s?next=%s" % (settings.WAGTAIL_FRONTEND_LOGIN_URL, request.path)
        )

    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)

RoundBaseManager

Bases: PageQuerySet

open

open()
Source code in hypha/apply/funds/models/applications.py
def open(self):
    rounds = self.live().public().specific()
    rounds = rounds.filter(
        Q(start_date__lte=date.today())
        & Q(Q(end_date__isnull=True) | Q(end_date__gte=date.today()))
    )
    return rounds

closed

closed()
Source code in hypha/apply/funds/models/applications.py
def closed(self):
    rounds = self.live().public().specific()
    rounds = rounds.filter(end_date__lt=date.today())
    return rounds

RoundBase

Bases: WorkflowStreamForm, SubmittableStreamForm

submission_form_class class-attribute instance-attribute

submission_form_class = PageStreamBaseForm

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

WORKFLOW_CHOICES class-attribute instance-attribute

WORKFLOW_CHOICES = {name: _9QPfor (name, workflow) in items()}

workflow_name class-attribute instance-attribute

workflow_name = CharField(choices=items(), max_length=100, default='single', verbose_name=gettext_lazy('Workflow'))

workflow property

workflow

is_creatable class-attribute instance-attribute

is_creatable = False

submission_class class-attribute instance-attribute

submission_class = ApplicationSubmission

objects class-attribute instance-attribute

objects = from_queryset(RoundBaseManager)()

subpage_types class-attribute instance-attribute

subpage_types = []

base_form_class class-attribute instance-attribute

base_form_class = RoundBasePageAdminForm

lead class-attribute instance-attribute

lead = ForeignKey(AUTH_USER_MODEL, limit_choices_to=LIMIT_TO_STAFF, related_name='%(class)s_lead', on_delete=PROTECT)

reviewers class-attribute instance-attribute

reviewers = ParentalManyToManyField(AUTH_USER_MODEL, related_name='%(class)s_reviewer', limit_choices_to=LIMIT_TO_REVIEWERS, blank=True)

start_date class-attribute instance-attribute

start_date = DateField(null=True, blank=True, default=today)

end_date class-attribute instance-attribute

end_date = DateField(blank=True, null=True, default=today, help_text=gettext_lazy('When no end date is provided the round will remain open indefinitely.'))

sealed class-attribute instance-attribute

sealed = BooleanField(default=False)

url class-attribute instance-attribute

url = property(get_url)

content_panels class-attribute instance-attribute

content_panels = content_panels + [FieldPanel('lead'), MultiFieldPanel([FieldRowPanel([FieldPanel('start_date'), FieldPanel('end_date')])], heading=gettext_lazy('Dates')), FieldPanel('reviewers', widget=CheckboxSelectMultiple), ReadOnlyPanel('get_workflow_name_display', heading=gettext_lazy('Workflow'), help_text=gettext_lazy('Copied from the fund.')), ReadOnlyInlinePanel('forms', panels=[ReadOnlyPanel('name')], heading=gettext_lazy('Application forms'), help_text=gettext_lazy('Copied from the fund.')), ReadOnlyInlinePanel('review_forms', panels=[ReadOnlyPanel('name')], heading=gettext_lazy('Internal Review Form'), help_text=gettext_lazy('Copied from the fund.')), ReadOnlyInlinePanel('external_review_forms', panels=[ReadOnlyPanel('name')], help_text=gettext_lazy('Copied from the fund.'), heading=gettext_lazy('External Review Form')), ReadOnlyInlinePanel('determination_forms', panels=[ReadOnlyPanel('name')], help_text=gettext_lazy('Copied from the fund.'), heading=gettext_lazy('Determination Form'))]

edit_handler class-attribute instance-attribute

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

is_sealed property

is_sealed

is_open property

is_open

Checks if the application is open based on the current date.

The application is considered open if the current date is between the start and end dates (inclusive). If the end date is not set, the application is considered open if the current date is on or after the start date.

Returns:

  • bool ( bool ) –

    True if the application is open, False otherwise.

Meta

abstract class-attribute instance-attribute
abstract = True

from_db classmethod

from_db(db, field_names, values)
Source code in hypha/apply/stream_forms/models.py
@classmethod
def from_db(cls, db, field_names, values):
    instance = super().from_db(db, field_names, values)
    if "form_data" in field_names:
        instance.form_data = cls.deserialize_form_data(
            instance, instance.form_data, instance.form_fields
        )
    return instance

deserialize_form_data classmethod

deserialize_form_data(instance, form_data, form_fields)
Source code in hypha/apply/stream_forms/models.py
@classmethod
def deserialize_form_data(cls, instance, form_data, form_fields):
    data = form_data.copy()
    # PERFORMANCE NOTE:
    # Do not attempt to iterate over form_fields - that will fully instantiate the form_fields
    # including any sub queries that they do
    for _i, field_data in enumerate(form_fields.raw_data):
        block = form_fields.stream_block.child_blocks[field_data["type"]]
        field_id = field_data.get("id")
        try:
            value = data[field_id]
        except KeyError:
            pass
        else:
            data[field_id] = block.decode(value)
    return data

get_defined_fields

get_defined_fields(stage=None, form_index=0)
Source code in hypha/apply/funds/models/utils.py
def get_defined_fields(self, stage=None, form_index=0):
    if not stage:
        stage_num = 1
    else:
        stage_num = self.workflow.stages.index(stage) + 1
    return self.forms.filter(stage=stage_num)[form_index].fields

get_form_fields

get_form_fields(draft=False, form_data=None, user=None)
Source code in hypha/apply/stream_forms/models.py
def get_form_fields(self, draft=False, form_data=None, user=None):
    if form_data is None:
        form_data = {}

    form_fields = OrderedDict()
    field_blocks = self.get_defined_fields()
    group_counter = 1
    is_in_group = False

    # If true option 1 is selected
    grouped_fields_visible = False
    for struct_child in field_blocks:
        block = struct_child.block
        struct_value = struct_child.value
        if isinstance(block, FormFieldBlock):
            field_from_block = block.get_field(struct_value)
            disabled_help_text = _(
                "You are logged in so this information is fetched from your user account."
            )
            if isinstance(block, FullNameBlock) and user and user.is_authenticated:
                if user.full_name:
                    field_from_block.disabled = True
                    field_from_block.initial = user.full_name
                    field_from_block.help_text = disabled_help_text
                else:
                    field_from_block.help_text = _(
                        "You are logged in but your user account does not have a "
                        "full name. We'll update your user account with the name you provide here."
                    )
            if isinstance(block, EmailBlock) and user and user.is_authenticated:
                field_from_block.disabled = True
                field_from_block.initial = user.email
                field_from_block.help_text = disabled_help_text
            if draft and not issubclass(
                block.__class__, ApplicationMustIncludeFieldBlock
            ):
                field_from_block.required = False
            field_from_block.help_link = struct_value.get("help_link")
            field_from_block.group_number = group_counter if is_in_group else 1
            if isinstance(block, GroupToggleBlock) and not is_in_group:
                field_from_block.group_number = 1
                field_from_block.grouper_for = group_counter + 1
                group_counter += 1
                is_in_group = True
                grouped_fields_visible = (
                    form_data.get(struct_child.id) == field_from_block.choices[0][0]
                )
            if isinstance(block, TextFieldBlock):
                field_from_block.word_limit = struct_value.get("word_limit")
            if isinstance(block, MultiInputCharFieldBlock):
                number_of_inputs = struct_value.get("number_of_inputs")
                for index in range(number_of_inputs):
                    form_fields[struct_child.id + "_" + str(index)] = (
                        field_from_block
                    )
                    field_from_block.multi_input_id = struct_child.id
                    field_from_block.add_button_text = struct_value.get(
                        "add_button_text"
                    )
                    if (
                        index == number_of_inputs - 1
                    ):  # Add button after last input field
                        field_from_block.multi_input_add_button = True
                        # Index for field until which fields will be visible to applicant.
                        # Initially only the first field with id UUID_0 will be visible.
                        field_from_block.visibility_index = 0
                        field_from_block.max_index = index
                    if index != 0:
                        field_from_block.multi_input_field = True
                        field_from_block.required = False
                        field_from_block.initial = None
                    field_from_block = copy.copy(field_from_block)
            else:
                if is_in_group and not isinstance(block, GroupToggleBlock):
                    field_from_block.required_when_visible = (
                        field_from_block.required
                    )
                    field_from_block.required = (
                        field_from_block.required & grouped_fields_visible
                    )
                    field_from_block.visible = grouped_fields_visible
                form_fields[struct_child.id] = field_from_block
        elif isinstance(block, GroupToggleEndBlock):
            # Group toggle end block is used only to group fields and not used in actual form.
            # Todo: Use streamblock to create nested form field blocks, a more elegant method to group form fields.
            is_in_group = False
        else:
            field_wrapper = BlockFieldWrapper(struct_child)
            field_wrapper.group_number = group_counter if is_in_group else 1
            form_fields[struct_child.id] = field_wrapper

    return form_fields

get_form_class

get_form_class(draft=False, form_data=None, user=None)
Source code in hypha/apply/stream_forms/models.py
def get_form_class(self, draft=False, form_data=None, user=None):
    return type(
        "WagtailStreamForm",
        (self.submission_form_class,),
        self.get_form_fields(draft, form_data, user),
    )

get_submission_class

get_submission_class()
Source code in hypha/apply/funds/models/utils.py
def get_submission_class(self):
    return self.submission_class

process_form_submission

process_form_submission(form, draft=False)
Source code in hypha/apply/funds/models/utils.py
def process_form_submission(self, form, draft=False):
    if not form.user.is_authenticated:
        form.user = None
    if draft:
        return self.get_submission_class().objects.create(
            form_data=form.cleaned_data,
            form_fields=self.get_defined_fields(),
            **self.get_submit_meta_data(user=form.user),
            status=DRAFT_STATE,
        )
    else:
        return self.get_submission_class().objects.create(
            form_data=form.cleaned_data,
            form_fields=self.get_defined_fields(),
            **self.get_submit_meta_data(user=form.user),
        )

render_landing_page

render_landing_page(request, form_submission=None, *args, **kwargs)
Source code in hypha/apply/funds/models/utils.py
def render_landing_page(self, request, form_submission=None, *args, **kwargs):
    # We only reach this page after creation of a new submission
    # Hook in to notify about new applications
    if form_submission.status == DRAFT_STATE:
        messenger(
            MESSAGES.DRAFT_SUBMISSION,
            request=request,
            user=form_submission.user,
            source=form_submission,
        )
    else:
        messenger(
            MESSAGES.NEW_SUBMISSION,
            request=request,
            user=form_submission.user,
            source=form_submission,
        )

    return redirect("apply:submissions:success", pk=form_submission.id)

get_url

get_url(request=None)

Generates the live url, primarily used in the wagtail admin for the "view live" button.

Returns:

  • str ( Optional[str] ) –

    The live url of the page, or None if the page is not live.

Source code in hypha/apply/funds/models/applications.py
def get_url(self, request: Optional[WSGIRequest] = None) -> Optional[str]:
    """Generates the live url, primarily used in the wagtail admin for the "view live" button.

    Returns:
        str: The live url of the page, or None if the page is not live.
    """
    if self.is_open:
        return self.fund.url
    return None

get_template

get_template(request, *args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
def get_template(self, request, *args, **kwargs):
    # Make sure all children use the shared template
    return "funds/round.html"

get_landing_page_template

get_landing_page_template(request, *args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
def get_landing_page_template(self, request, *args, **kwargs):
    # Make sure all children use the shared template
    return "funds/round_landing.html"

fund

fund()
Source code in hypha/apply/funds/models/applications.py
@cached_property
def fund(self):
    return self.get_parent()

save

save(*args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
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")
        self._copy_forms("external_review_forms")
        self._copy_forms("determination_forms")

get_submit_meta_data

get_submit_meta_data(**kwargs)
Source code in hypha/apply/funds/models/applications.py
def get_submit_meta_data(self, **kwargs):
    return super().get_submit_meta_data(
        page=self.get_parent(),
        round=self,
        **kwargs,
    )

clean

clean()
Source code in hypha/apply/funds/models/applications.py
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)

get_initial_data_open_call_submission

get_initial_data_open_call_submission(submission_id)
Source code in hypha/apply/funds/models/applications.py
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

get_form_parameters

get_form_parameters(submission_id=None)
Source code in hypha/apply/funds/models/applications.py
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

get_form

get_form(*args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
def get_form(self, *args, **kwargs):
    draft = kwargs.pop("draft", False)
    user = kwargs.get("user")
    try:
        form_class = self.get_form_class(draft, args[0], user=user)
    except IndexError:
        form_class = self.get_form_class(draft, user=user)
    submission_id = kwargs.pop("submission_id", None)
    form_params = self.get_form_parameters(submission_id=submission_id)
    form_params.update(kwargs)
    return form_class(*args, **form_params)

serve

serve(request, *args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
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":
            preview = "preview" in request.POST
            draft = request.POST.get("draft", preview)
            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)
                # Required for django-file-form: delete temporary files for the new files
                # that are uploaded.
                form.delete_temporary_files()

                # If a preview is specified in form submission, render the applicant's answers rather than the landing page.
                # At the moment ALL previews are drafted first and then shown
                if preview and draft:
                    context = self.get_context(request)
                    context["object"] = form_submission
                    context["form"] = form
                    return render(
                        request, "funds/application_preview.html", context
                    )

                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
        # Check if a preview is required before submitting the application
        context["require_preview"] = settings.SUBMISSION_PREVIEW_REQUIRED
        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()

LabBase

Bases: EmailForm, WorkflowStreamForm, SubmittableStreamForm

submission_form_class class-attribute instance-attribute

submission_form_class = PageStreamBaseForm

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

WORKFLOW_CHOICES class-attribute instance-attribute

WORKFLOW_CHOICES = {name: _8W6for (name, workflow) in items()}

workflow_name class-attribute instance-attribute

workflow_name = CharField(choices=items(), max_length=100, default='single', verbose_name=gettext_lazy('Workflow'))

workflow property

workflow

confirmation_text_extra class-attribute instance-attribute

confirmation_text_extra = TextField(blank=True, help_text=gettext_lazy('Additional text for the application confirmation message.'))

email_confirmation_panels class-attribute instance-attribute

email_confirmation_panels = [MultiFieldPanel([FieldRowPanel([FieldPanel('from_address', classname='col6'), FieldPanel('to_address', classname='col6')]), FieldPanel('subject'), FieldPanel('confirmation_text_extra')], heading=gettext_lazy('Confirmation email'))]

email_tab class-attribute instance-attribute

email_tab = ObjectList(email_confirmation_panels, heading=gettext_lazy('Confirmation email'))

is_createable class-attribute instance-attribute

is_createable = False

submission_class class-attribute instance-attribute

submission_class = ApplicationSubmission

base_form_class class-attribute instance-attribute

base_form_class = WorkflowFormAdminForm

lead class-attribute instance-attribute

lead = ForeignKey(AUTH_USER_MODEL, limit_choices_to=LIMIT_TO_STAFF, related_name='lab_lead', on_delete=PROTECT)

reviewers class-attribute instance-attribute

reviewers = ParentalManyToManyField(AUTH_USER_MODEL, related_name='labs_reviewer', limit_choices_to=LIMIT_TO_REVIEWERS, blank=True)

image class-attribute instance-attribute

image = ForeignKey('images.CustomImage', null=True, blank=True, on_delete=SET_NULL, related_name='+')

description class-attribute instance-attribute

description = TextField(null=True, blank=True)

weight class-attribute instance-attribute

weight = PositiveIntegerField(default=1, blank=True, validators=[MinValueValidator(1), MaxValueValidator(100)])
guide_link = URLField(blank=True, max_length=255, help_text=gettext_lazy('Link to the apply guide.'))

slack_channel class-attribute instance-attribute

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

activity_digest_recipient_emails class-attribute instance-attribute

activity_digest_recipient_emails = ArrayField(EmailField(default=''), blank=True, null=True, help_text=gettext_lazy('Comma separated list of emails where a summary of all the activities related to this lab will be sent.'))

list_on_front_page class-attribute instance-attribute

list_on_front_page = BooleanField(default=True, help_text=gettext_lazy('Should the lab be listed on the front page.'))

parent_page_types class-attribute instance-attribute

parent_page_types = ['apply_home.ApplyHomePage']

subpage_types class-attribute instance-attribute

subpage_types = []

content_panels class-attribute instance-attribute

content_panels = content_panels + [FieldPanel('lead'), FieldPanel('reviewers', widget=CheckboxSelectMultiple), FieldPanel('guide_link'), FieldPanel('description'), FieldPanel('image'), FieldPanel('weight'), FieldPanel('slack_channel'), FieldPanel('activity_digest_recipient_emails'), FieldPanel('list_on_front_page')]

edit_handler class-attribute instance-attribute

edit_handler = TabbedInterface([ObjectList(content_panels, heading=gettext_lazy('Content')), email_tab, ObjectList(promote_panels, heading=gettext_lazy('Promote'))])

Meta

abstract class-attribute instance-attribute
abstract = True

from_db classmethod

from_db(db, field_names, values)
Source code in hypha/apply/stream_forms/models.py
@classmethod
def from_db(cls, db, field_names, values):
    instance = super().from_db(db, field_names, values)
    if "form_data" in field_names:
        instance.form_data = cls.deserialize_form_data(
            instance, instance.form_data, instance.form_fields
        )
    return instance

deserialize_form_data classmethod

deserialize_form_data(instance, form_data, form_fields)
Source code in hypha/apply/stream_forms/models.py
@classmethod
def deserialize_form_data(cls, instance, form_data, form_fields):
    data = form_data.copy()
    # PERFORMANCE NOTE:
    # Do not attempt to iterate over form_fields - that will fully instantiate the form_fields
    # including any sub queries that they do
    for _i, field_data in enumerate(form_fields.raw_data):
        block = form_fields.stream_block.child_blocks[field_data["type"]]
        field_id = field_data.get("id")
        try:
            value = data[field_id]
        except KeyError:
            pass
        else:
            data[field_id] = block.decode(value)
    return data

get_defined_fields

get_defined_fields(stage=None, form_index=0)
Source code in hypha/apply/funds/models/utils.py
def get_defined_fields(self, stage=None, form_index=0):
    if not stage:
        stage_num = 1
    else:
        stage_num = self.workflow.stages.index(stage) + 1
    return self.forms.filter(stage=stage_num)[form_index].fields

get_form_fields

get_form_fields(draft=False, form_data=None, user=None)
Source code in hypha/apply/stream_forms/models.py
def get_form_fields(self, draft=False, form_data=None, user=None):
    if form_data is None:
        form_data = {}

    form_fields = OrderedDict()
    field_blocks = self.get_defined_fields()
    group_counter = 1
    is_in_group = False

    # If true option 1 is selected
    grouped_fields_visible = False
    for struct_child in field_blocks:
        block = struct_child.block
        struct_value = struct_child.value
        if isinstance(block, FormFieldBlock):
            field_from_block = block.get_field(struct_value)
            disabled_help_text = _(
                "You are logged in so this information is fetched from your user account."
            )
            if isinstance(block, FullNameBlock) and user and user.is_authenticated:
                if user.full_name:
                    field_from_block.disabled = True
                    field_from_block.initial = user.full_name
                    field_from_block.help_text = disabled_help_text
                else:
                    field_from_block.help_text = _(
                        "You are logged in but your user account does not have a "
                        "full name. We'll update your user account with the name you provide here."
                    )
            if isinstance(block, EmailBlock) and user and user.is_authenticated:
                field_from_block.disabled = True
                field_from_block.initial = user.email
                field_from_block.help_text = disabled_help_text
            if draft and not issubclass(
                block.__class__, ApplicationMustIncludeFieldBlock
            ):
                field_from_block.required = False
            field_from_block.help_link = struct_value.get("help_link")
            field_from_block.group_number = group_counter if is_in_group else 1
            if isinstance(block, GroupToggleBlock) and not is_in_group:
                field_from_block.group_number = 1
                field_from_block.grouper_for = group_counter + 1
                group_counter += 1
                is_in_group = True
                grouped_fields_visible = (
                    form_data.get(struct_child.id) == field_from_block.choices[0][0]
                )
            if isinstance(block, TextFieldBlock):
                field_from_block.word_limit = struct_value.get("word_limit")
            if isinstance(block, MultiInputCharFieldBlock):
                number_of_inputs = struct_value.get("number_of_inputs")
                for index in range(number_of_inputs):
                    form_fields[struct_child.id + "_" + str(index)] = (
                        field_from_block
                    )
                    field_from_block.multi_input_id = struct_child.id
                    field_from_block.add_button_text = struct_value.get(
                        "add_button_text"
                    )
                    if (
                        index == number_of_inputs - 1
                    ):  # Add button after last input field
                        field_from_block.multi_input_add_button = True
                        # Index for field until which fields will be visible to applicant.
                        # Initially only the first field with id UUID_0 will be visible.
                        field_from_block.visibility_index = 0
                        field_from_block.max_index = index
                    if index != 0:
                        field_from_block.multi_input_field = True
                        field_from_block.required = False
                        field_from_block.initial = None
                    field_from_block = copy.copy(field_from_block)
            else:
                if is_in_group and not isinstance(block, GroupToggleBlock):
                    field_from_block.required_when_visible = (
                        field_from_block.required
                    )
                    field_from_block.required = (
                        field_from_block.required & grouped_fields_visible
                    )
                    field_from_block.visible = grouped_fields_visible
                form_fields[struct_child.id] = field_from_block
        elif isinstance(block, GroupToggleEndBlock):
            # Group toggle end block is used only to group fields and not used in actual form.
            # Todo: Use streamblock to create nested form field blocks, a more elegant method to group form fields.
            is_in_group = False
        else:
            field_wrapper = BlockFieldWrapper(struct_child)
            field_wrapper.group_number = group_counter if is_in_group else 1
            form_fields[struct_child.id] = field_wrapper

    return form_fields

get_form_class

get_form_class(draft=False, form_data=None, user=None)
Source code in hypha/apply/stream_forms/models.py
def get_form_class(self, draft=False, form_data=None, user=None):
    return type(
        "WagtailStreamForm",
        (self.submission_form_class,),
        self.get_form_fields(draft, form_data, user),
    )

get_submission_class

get_submission_class()
Source code in hypha/apply/funds/models/utils.py
def get_submission_class(self):
    return self.submission_class

process_form_submission

process_form_submission(form, draft=False)
Source code in hypha/apply/funds/models/utils.py
def process_form_submission(self, form, draft=False):
    if not form.user.is_authenticated:
        form.user = None
    if draft:
        return self.get_submission_class().objects.create(
            form_data=form.cleaned_data,
            form_fields=self.get_defined_fields(),
            **self.get_submit_meta_data(user=form.user),
            status=DRAFT_STATE,
        )
    else:
        return self.get_submission_class().objects.create(
            form_data=form.cleaned_data,
            form_fields=self.get_defined_fields(),
            **self.get_submit_meta_data(user=form.user),
        )

render_landing_page

render_landing_page(request, form_submission=None, *args, **kwargs)
Source code in hypha/apply/funds/models/utils.py
def render_landing_page(self, request, form_submission=None, *args, **kwargs):
    # We only reach this page after creation of a new submission
    # Hook in to notify about new applications
    if form_submission.status == DRAFT_STATE:
        messenger(
            MESSAGES.DRAFT_SUBMISSION,
            request=request,
            user=form_submission.user,
            source=form_submission,
        )
    else:
        messenger(
            MESSAGES.NEW_SUBMISSION,
            request=request,
            user=form_submission.user,
            source=form_submission,
        )

    return redirect("apply:submissions:success", pk=form_submission.id)

send_mail

send_mail(submission)
Source code in hypha/apply/funds/models/utils.py
def send_mail(self, submission):
    # Make sure we don't send emails to users here. Messaging handles that
    pass

get_submit_meta_data

get_submit_meta_data(**kwargs)
Source code in hypha/apply/funds/models/applications.py
def get_submit_meta_data(self, **kwargs):
    return super().get_submit_meta_data(
        page=self,
        round=None,
        **kwargs,
    )

open_round

open_round()
Source code in hypha/apply/funds/models/applications.py
def open_round(self):
    return self.live

get_form

get_form(*args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
def get_form(self, *args, **kwargs):
    draft = kwargs.pop("draft", False)
    user = kwargs.get("user")
    form_class = self.get_form_class(draft=draft, user=user)
    form_params = self.get_form_parameters()
    form_params.update(kwargs)

    return form_class(*args, **form_params)

serve

serve(request, *args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
def serve(self, request, *args, **kwargs):
    # Manually do what the login_required decorator does so that we can check settings
    if not request.user.is_authenticated and settings.FORCE_LOGIN_FOR_APPLICATION:
        return redirect(
            "%s?next=%s" % (settings.WAGTAIL_FRONTEND_LOGIN_URL, request.path)
        )

    if request.method == "POST":
        preview = "preview" in request.POST
        draft = request.POST.get("draft", preview)
        form = self.get_form(
            request.POST, request.FILES, page=self, user=request.user, draft=draft
        )
        if form.is_valid():
            form_submission = SubmittableStreamForm.process_form_submission(
                self, form, draft=draft
            )

            # If a preview is specified in form submission, render the applicant's answers rather than the landing page.
            # At the moment ALL previews are drafted first and then shown
            if preview and draft:
                context = self.get_context(request)
                context["object"] = form_submission
                context["form"] = form
                return render(request, "funds/application_preview.html", context)

            return self.render_landing_page(
                request, form_submission, *args, **kwargs
            )
    else:
        form = self.get_form(page=self, user=request.user)

    context = self.get_context(request)
    context["form"] = form
    # Check if a preview is required before submitting the application
    context["require_preview"] = settings.SUBMISSION_PREVIEW_REQUIRED
    return TemplateResponse(request, self.get_template(request), context)

RoundsAndLabsQueryset

Bases: PageQuerySet

new

new()
Source code in hypha/apply/funds/models/applications.py
def new(self):
    return self.filter(start_date__gt=date.today())

open

open()
Source code in hypha/apply/funds/models/applications.py
def open(self):
    return self.filter(
        Q(end_date__gte=date.today(), start_date__lte=date.today())
        | Q(end_date__isnull=True)
    )

closed

closed()
Source code in hypha/apply/funds/models/applications.py
def closed(self):
    return self.filter(end_date__lt=date.today())

by_lead

by_lead(user)
Source code in hypha/apply/funds/models/applications.py
def by_lead(self, user):
    return self.filter(lead_pk=user.pk)

RoundsAndLabsProgressQueryset

Bases: RoundsAndLabsQueryset

new

new()
Source code in hypha/apply/funds/models/applications.py
def new(self):
    return self.filter(start_date__gt=date.today())

open

open()
Source code in hypha/apply/funds/models/applications.py
def open(self):
    return self.filter(
        Q(end_date__gte=date.today(), start_date__lte=date.today())
        | Q(end_date__isnull=True)
    )

closed

closed()
Source code in hypha/apply/funds/models/applications.py
def closed(self):
    return self.filter(end_date__lt=date.today())

by_lead

by_lead(user)
Source code in hypha/apply/funds/models/applications.py
def by_lead(self, user):
    return self.filter(lead_pk=user.pk)

active

active()
Source code in hypha/apply/funds/models/applications.py
def active(self):
    return self.filter(progress__lt=100)

inactive

inactive()
Source code in hypha/apply/funds/models/applications.py
def inactive(self):
    return self.filter(progress=100)

RoundsAndLabsManager

Bases: PageManager

get_queryset

get_queryset(base_queryset=RoundsAndLabsQueryset)
Source code in hypha/apply/funds/models/applications.py
def get_queryset(self, base_queryset=RoundsAndLabsQueryset):
    funds = ApplicationBase.objects.filter(path=OuterRef("parent_path"))

    return (
        base_queryset(self.model, using=self._db)
        .type(SubmittableStreamForm)
        .annotate(
            lead=Coalesce(
                F("roundbase__lead__full_name"),
                F("labbase__lead__full_name"),
            ),
            start_date=F("roundbase__start_date"),
            end_date=F("roundbase__end_date"),
            parent_path=Left(
                F("path"),
                Length("path") - ApplicationBase.steplen,
                output_field=CharField(),
            ),
            fund=Subquery(funds.values("title")[:1]),
            lead_pk=Coalesce(
                F("roundbase__lead__pk"),
                F("labbase__lead__pk"),
            ),
        )
    )

with_progress

with_progress()
Source code in hypha/apply/funds/models/applications.py
def with_progress(self):
    submissions = ApplicationSubmission.objects.filter(
        Q(round=OuterRef("pk")) | Q(page=OuterRef("pk"))
    ).current()
    closed_submissions = submissions.inactive()

    return (
        self.get_queryset(RoundsAndLabsProgressQueryset)
        .annotate(
            total_submissions=Coalesce(
                Subquery(
                    submissions.exclude_draft()
                    .values("round")
                    .annotate(count=Count("pk"))
                    .values("count"),
                    output_field=IntegerField(),
                ),
                0,
            ),
            closed_submissions=Coalesce(
                Subquery(
                    closed_submissions.exclude_draft()
                    .values("round")
                    .annotate(count=Count("pk"))
                    .values("count"),
                    output_field=IntegerField(),
                ),
                0,
            ),
        )
        .annotate(
            progress=Case(
                When(total_submissions=0, then=None),
                default=(F("closed_submissions") * 100) / F("total_submissions"),
                output_fields=FloatField(),
            )
        )
    )

open

open()
Source code in hypha/apply/funds/models/applications.py
def open(self):
    return self.get_queryset().open()

closed

closed()
Source code in hypha/apply/funds/models/applications.py
def closed(self):
    return self.get_queryset().closed()

new

new()
Source code in hypha/apply/funds/models/applications.py
def new(self):
    return self.get_queryset().new()

by_lead

by_lead(user)
Source code in hypha/apply/funds/models/applications.py
def by_lead(self, user):
    return self.get_queryset().by_lead(user)

RoundsAndLabs

Bases: Page

This behaves as a useful way to get all the rounds and labs that are defined in the project regardless of how they are implemented (lab/round/sealed_round)

objects class-attribute instance-attribute

Meta

proxy class-attribute instance-attribute
proxy = True

save

save(*args, **kwargs)
Source code in hypha/apply/funds/models/applications.py
def save(self, *args, **kwargs):
    raise NotImplementedError("Do not save through this model")

ApplicationSettings

Bases: BaseSiteSetting

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

extra_text_round class-attribute instance-attribute

extra_text_round = RichTextField(blank=True)

extra_text_lab class-attribute instance-attribute

extra_text_lab = RichTextField(blank=True)

panels class-attribute instance-attribute

panels = [MultiFieldPanel([FieldPanel('extra_text_round'), FieldPanel('extra_text_lab')], 'extra text on application landing page')]

Meta

verbose_name class-attribute instance-attribute
verbose_name = 'application settings'