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: _3eAfor (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.'))

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('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: _bW0for (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)

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

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_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: _7GGfor (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.'))

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')]

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'