Skip to content

Index

hypha.apply.funds.models

ApplicationRevision

Bases: BaseStreamForm, AccessFormData, Model

stream_file_class class-attribute instance-attribute

stream_file_class = SubmissionStreamFieldFile

storage_class class-attribute instance-attribute

storage_class = PrivateStorage

raw_data property

raw_data

question_field_ids property

question_field_ids

file_field_ids property

file_field_ids

question_text_field_ids property

question_text_field_ids

first_group_question_text_field_ids property

first_group_question_text_field_ids

raw_fields property

raw_fields

fields property

fields

named_blocks property

named_blocks

normal_blocks property

normal_blocks

group_toggle_blocks property

group_toggle_blocks

first_group_normal_text_blocks property

first_group_normal_text_blocks

submission_form_class class-attribute instance-attribute

submission_form_class = PageStreamBaseForm

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

submission class-attribute instance-attribute

submission = ForeignKey('funds.ApplicationSubmission', related_name='revisions', on_delete=CASCADE)

form_data class-attribute instance-attribute

form_data = JSONField(encoder=StreamFieldDataEncoder)

timestamp class-attribute instance-attribute

timestamp = DateTimeField(auto_now=True)

author class-attribute instance-attribute

author = ForeignKey(AUTH_USER_MODEL, on_delete=SET_NULL, null=True)

form_fields property

form_fields

Meta

ordering class-attribute instance-attribute
ordering = ['-timestamp']

stream_file classmethod

stream_file(instance, field, file)
Source code in hypha/apply/funds/models/mixins.py
@classmethod
def stream_file(cls, instance, field, file):
    if not file:
        return []
    if isinstance(file, cls.stream_file_class):
        return file
    if isinstance(file, File):
        return cls.stream_file_class(
            instance, field, file, name=file.name, storage=cls.storage_class()
        )

    if isinstance(file, PlaceholderUploadedFile):
        return cls.stream_file_class(
            instance,
            field,
            None,
            name=file.file_id,
            filename=file.name,
            storage=cls.storage_class(),
        )

    # This fixes a backwards compatibility issue with #507
    # Once every application has been re-saved it should be possible to remove it
    if "path" in file:
        file["filename"] = file["name"]
        file["name"] = file["path"]
    return cls.stream_file_class(
        instance,
        field,
        None,
        name=file["name"],
        filename=file.get("filename"),
        storage=cls.storage_class(),
    )

process_file classmethod

process_file(instance, field, file)
Source code in hypha/apply/funds/models/mixins.py
@classmethod
def process_file(cls, instance, field, file):
    if isinstance(file, list):
        return [cls.stream_file(instance, field, f) for f in file]
    else:
        return cls.stream_file(instance, field, file)

process_file_data

process_file_data(data)
Source code in hypha/apply/funds/models/mixins.py
def process_file_data(self, data):
    for field in self.form_fields:
        if isinstance(field.block, UploadableMediaBlock):
            file = self.process_file(self, field, data.get(field.id, []))
            try:
                file.save()
            except (AttributeError, FileNotFoundError):
                try:
                    for f in file:
                        f.save()
                except FileNotFoundError:
                    pass
            self.form_data[field.id] = file

extract_files

extract_files()
Source code in hypha/apply/funds/models/mixins.py
def extract_files(self):
    files = {}
    for field in self.form_fields:
        if isinstance(field.block, UploadableMediaBlock):
            files[field.id] = self.data(field.id) or []
            self.form_data.pop(field.id, None)
    return files

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

deserialised_data classmethod

deserialised_data(instance, data, form_fields)
Source code in hypha/apply/funds/models/mixins.py
@classmethod
def deserialised_data(cls, instance, data, form_fields):
    # Converts the file dicts into actual file objects
    data = 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"]]
        if isinstance(block, UploadableMediaBlock):
            field_id = field_data.get("id")
            if field_id:
                field = form_fields[i]
                file = data.get(field_id, [])
                data[field_id] = cls.process_file(instance, field, file)
    return data

get_definitive_id

get_definitive_id(id)
Source code in hypha/apply/funds/models/mixins.py
def get_definitive_id(self, id):
    if id in self.named_blocks:
        return self.named_blocks[id]
    return id

field

field(id)
Source code in hypha/apply/funds/models/mixins.py
def field(self, id):
    definitive_id = self.get_definitive_id(id)
    try:
        return self.raw_fields[definitive_id]
    except KeyError:
        raise UnusedFieldException(id) from None

data

data(id)
Source code in hypha/apply/funds/models/mixins.py
def data(self, id):
    definitive_id = self.get_definitive_id(id)
    try:
        return self.raw_data[definitive_id]
    except KeyError:
        # We have most likely progressed application forms so the data isn't in form_data
        return None

get_serialize_multi_inputs_answer

get_serialize_multi_inputs_answer(field)
Source code in hypha/apply/funds/models/mixins.py
def get_serialize_multi_inputs_answer(self, field):
    number_of_inputs = field.value.get("number_of_inputs")
    answers = [self.data(field.id + "_" + str(i)) for i in range(number_of_inputs)]
    data = ", ".join(filter(None, answers))
    return data

serialize

serialize(field_id)
Source code in hypha/apply/funds/models/mixins.py
def serialize(self, field_id):
    field = self.field(field_id)
    if isinstance(field.block, MultiInputCharFieldBlock):
        data = self.get_serialize_multi_inputs_answer(field)
    else:
        data = self.data(field_id)
    return field.render(
        context={
            "serialize": True,
            "data": data,
        }
    )

get_multi_inputs_answer

get_multi_inputs_answer(field, include_question=False)
Source code in hypha/apply/funds/models/mixins.py
def get_multi_inputs_answer(self, field, include_question=False):
    number_of_inputs = field.value.get("number_of_inputs")
    answers = [self.data(field.id + "_" + str(i)) for i in range(number_of_inputs)]

    render_data = [
        field.render(
            context={
                "data": answer,
                "include_question": include_question if i == 0 else False,
            }
        )
        for i, answer in enumerate(filter(None, answers))
    ]
    return "".join(render_data).replace("</section>", "") + "</section>"

render_answer

render_answer(field_id, include_question=False)
Source code in hypha/apply/funds/models/mixins.py
def render_answer(self, field_id, include_question=False):
    try:
        field = self.field(field_id)
    except UnusedFieldException:
        return "-"
    if isinstance(field.block, MultiInputCharFieldBlock):
        render_data = self.get_multi_inputs_answer(field, include_question)
        return render_data
    else:
        data = self.data(field_id)
    # Some migrated content have empty address.
    if not data:
        return field.render(
            context={"data": "", "include_question": include_question}
        )
    return field.render(
        context={"data": data, "include_question": include_question}
    )

render_answers

render_answers()
Source code in hypha/apply/funds/models/mixins.py
def render_answers(self):
    # Returns a list of the rendered answers
    return [
        self.render_answer(field_id, include_question=True)
        for field_id in self.normal_blocks
    ]

render_first_group_text_answers

render_first_group_text_answers()
Source code in hypha/apply/funds/models/mixins.py
def render_first_group_text_answers(self):
    return [
        self.render_answer(field_id, include_question=True)
        for field_id in self.first_group_normal_text_blocks
    ]

render_text_blocks_answers

render_text_blocks_answers()
Source code in hypha/apply/funds/models/mixins.py
def render_text_blocks_answers(self):
    # Returns a list of the rendered answers of type text
    return [
        self.render_answer(field_id, include_question=True)
        for field_id in self.question_text_field_ids
        if field_id not in self.named_blocks
    ]

output_answers

output_answers()
Source code in hypha/apply/funds/models/mixins.py
def output_answers(self):
    # Returns a safe string of the rendered answers
    return mark_safe("".join(self.render_answers()))

output_text_answers

output_text_answers()
Source code in hypha/apply/funds/models/mixins.py
def output_text_answers(self):
    return mark_safe("".join(self.render_text_blocks_answers()))

output_first_group_text_answers

output_first_group_text_answers()
Source code in hypha/apply/funds/models/mixins.py
def output_first_group_text_answers(self):
    return mark_safe("".join(self.render_first_group_text_answers()))

get_answer_from_label

get_answer_from_label(label)
Source code in hypha/apply/funds/models/mixins.py
def get_answer_from_label(self, label):
    for field_id in self.question_text_field_ids:
        if field_id not in self.named_blocks:
            question_field = self.serialize(field_id)
            if label.lower() in question_field["question"].lower():
                if isinstance(question_field["answer"], str):
                    answer = question_field["answer"]
                else:
                    answer = ",".join(question_field["answer"])
                if answer and not answer == "N":
                    return answer
    return None

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()
Source code in hypha/apply/stream_forms/models.py
def get_defined_fields(self):
    return self.form_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_compare_url_to_latest

get_compare_url_to_latest()
Source code in hypha/apply/funds/models/application_revisions.py
def get_compare_url_to_latest(self):
    return reverse(
        "funds:submissions:revisions:compare",
        kwargs={
            "submission_pk": self.submission.id,
            "to": self.submission.live_revision.id,
            "from": self.id,
        },
    )

get_absolute_url

get_absolute_url()
Source code in hypha/apply/funds/models/application_revisions.py
def get_absolute_url(self):
    # Compares against the previous revision
    previous_revision = self.submission.revisions.filter(id__lt=self.id).first()
    return reverse(
        "funds:submissions:revisions:compare",
        kwargs={
            "submission_pk": self.submission.id,
            "to": self.id,
            "from": previous_revision.id,
        },
    )

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'

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")

AssignedReviewers

Bases: Model

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

reviewer class-attribute instance-attribute

reviewer = ForeignKey(AUTH_USER_MODEL, on_delete=CASCADE, limit_choices_to=LIMIT_TO_REVIEWER_GROUPS)

type class-attribute instance-attribute

type = ForeignKey('auth.Group', on_delete=PROTECT)

submission class-attribute instance-attribute

submission = ForeignKey('funds.ApplicationSubmission', related_name='assigned', on_delete=CASCADE)

role class-attribute instance-attribute

role = ForeignKey('funds.ReviewerRole', related_name='+', on_delete=SET_NULL, null=True)

objects class-attribute instance-attribute

objects = as_manager()

Meta

unique_together class-attribute instance-attribute
unique_together = (('submission', 'role'), ('submission', 'reviewer'))

ApplicationForm

Bases: Model

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

name class-attribute instance-attribute

name = CharField(max_length=255)

form_fields class-attribute instance-attribute

form_fields = StreamField(ApplicationCustomFormFieldsBlock(), use_json_field=True)

panels class-attribute instance-attribute

panels = [FieldPanel('name'), FieldPanel('form_fields')]

Reminder

Bases: Model

REVIEW class-attribute instance-attribute

REVIEW = 'reviewers_review'

ACTIONS class-attribute instance-attribute

ACTIONS = {REVIEW: 'Remind reviewers to Review'}

EMAIL class-attribute instance-attribute

EMAIL = 'email'

MEDIUM class-attribute instance-attribute

MEDIUM = {REVIEW: EMAIL}

ACTION_MESSAGE class-attribute instance-attribute

ACTION_MESSAGE = {f'{REVIEW}-{EMAIL}': REVIEW_REMINDER}

submission class-attribute instance-attribute

submission = ForeignKey('funds.ApplicationSubmission', on_delete=CASCADE, related_name='reminders')

user class-attribute instance-attribute

user = ForeignKey(AUTH_USER_MODEL, on_delete=PROTECT)

time class-attribute instance-attribute

time = DateTimeField()

action class-attribute instance-attribute

action = CharField(choices=items(), default=REVIEW, max_length=50)

sent class-attribute instance-attribute

sent = BooleanField(default=False)

title class-attribute instance-attribute

title = CharField(max_length=60, blank=False, default='')

description class-attribute instance-attribute

description = TextField(blank=True)

is_expired property

is_expired

action_message property

action_message

action_type property

action_type

medium property

medium

Meta

ordering class-attribute instance-attribute
ordering = ['-time']

clean

clean()
Source code in hypha/apply/funds/models/reminders.py
def clean(self):
    if self.title == "":
        raise ValidationError("Title is Empty")

ReviewerRole

Bases: Model

name class-attribute instance-attribute

name = CharField(max_length=128)

icon class-attribute instance-attribute

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

order class-attribute instance-attribute

order = IntegerField(help_text=gettext_lazy('The order this role should appear in the Update Reviewers form.'), null=True, blank=True)

panels class-attribute instance-attribute

panels = [FieldPanel('name'), FieldPanel('icon'), FieldPanel('order')]

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

icon_url

icon_url(filter_spec)
Source code in hypha/apply/funds/models/reviewer_role.py
def icon_url(self, filter_spec):
    return generate_image_url(self.icon, filter_spec)

ReviewerSettings

Bases: BaseSiteSetting

SUBMISSIONS class-attribute instance-attribute

SUBMISSIONS = [('all', 'All Submissions'), ('reviewed', 'Only reviewed Submissions')]

STATES class-attribute instance-attribute

STATES = [('all', 'All States'), ('ext_state_or_higher', 'Only External review and higher'), ('ext_state_only', 'Only External review')]

OUTCOMES class-attribute instance-attribute

OUTCOMES = [('all', 'All Outcomes'), ('all_except_dismissed', 'All Outcomes Except Dismissed'), ('accepted', 'Only Accepted')]

submission class-attribute instance-attribute

submission = CharField(choices=SUBMISSIONS, default='all', max_length=10, help_text=gettext_lazy('Submissions for which reviewers should have access to'))

state class-attribute instance-attribute

state = CharField(choices=STATES, default='all', max_length=20, help_text=gettext_lazy('Submissions states for which reviewers should have access to'))

outcome class-attribute instance-attribute

outcome = CharField(choices=OUTCOMES, default='all', max_length=20, help_text=gettext_lazy('Submissions outcomes for which reviewers should have access to'))

assigned class-attribute instance-attribute

assigned = BooleanField(default=False, help_text=gettext_lazy('Submissions for which reviewer is assigned to'))

use_settings class-attribute instance-attribute

use_settings = BooleanField(default=False, help_text=gettext_lazy('Use the above configured variables to filter out submissions'))

panels class-attribute instance-attribute

panels = [FieldPanel('submission'), FieldPanel('state'), FieldPanel('outcome'), FieldPanel('assigned'), FieldPanel('use_settings')]

Meta

verbose_name class-attribute instance-attribute
verbose_name = 'Reviewer Settings'

ScreeningStatus

Bases: Model

title class-attribute instance-attribute

title = CharField(max_length=128)

yes class-attribute instance-attribute

yes = BooleanField(default=False, verbose_name=gettext_lazy('Yes/No'), help_text=gettext_lazy('Tick mark for Yes otherwise No.'))

default class-attribute instance-attribute

default = BooleanField(default=False, verbose_name=gettext_lazy('Default Yes/No'), help_text=gettext_lazy('Only one Yes and No screening decision can be set as default.'))

base_form_class class-attribute instance-attribute

base_form_class = ScreeningStatusAdminForm

Meta

verbose_name class-attribute instance-attribute
verbose_name = 'Screening Decision'
verbose_name_plural class-attribute instance-attribute
verbose_name_plural = 'screening decisions'

ApplicationSubmission

Bases: WorkflowHelpers, BaseStreamForm, AccessFormData, AbstractFormSubmission

stream_file_class class-attribute instance-attribute

stream_file_class = SubmissionStreamFieldFile

storage_class class-attribute instance-attribute

storage_class = PrivateStorage

raw_data property

raw_data

question_field_ids property

question_field_ids

file_field_ids property

file_field_ids

question_text_field_ids property

question_text_field_ids

first_group_question_text_field_ids property

first_group_question_text_field_ids

raw_fields property

raw_fields

fields property

fields

named_blocks property

named_blocks

normal_blocks property

normal_blocks

group_toggle_blocks property

group_toggle_blocks

first_group_normal_text_blocks property

first_group_normal_text_blocks

submission_form_class class-attribute instance-attribute

submission_form_class = PageStreamBaseForm

WORKFLOW_CHOICES class-attribute instance-attribute

WORKFLOW_CHOICES = {name: _qlNfor (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

form_data class-attribute instance-attribute

form_data = JSONField(encoder=StreamFieldDataEncoder)

form_fields class-attribute instance-attribute

form_fields = StreamField(ApplicationCustomFormFieldsBlock(), use_json_field=True)

summary class-attribute instance-attribute

summary = TextField(default='', null=True, blank=True)

page class-attribute instance-attribute

page = ForeignKey('wagtailcore.Page', on_delete=PROTECT)

round class-attribute instance-attribute

round = ForeignKey('wagtailcore.Page', on_delete=PROTECT, related_name='submissions', null=True)

lead class-attribute instance-attribute

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

next class-attribute instance-attribute

next = OneToOneField('self', on_delete=CASCADE, related_name='previous', null=True)

reviewers class-attribute instance-attribute

reviewers = ManyToManyField(AUTH_USER_MODEL, related_name='submissions_reviewer', blank=True, through='AssignedReviewers')

partners class-attribute instance-attribute

partners = ManyToManyField(AUTH_USER_MODEL, related_name='submissions_partner', limit_choices_to=LIMIT_TO_PARTNERS, blank=True)

meta_terms class-attribute instance-attribute

meta_terms = ManyToManyField(MetaTerm, related_name='submissions', blank=True)

flags class-attribute instance-attribute

flags = GenericRelation(Flag, content_type_field='target_content_type', object_id_field='target_object_id', related_query_name='submission')

activities class-attribute instance-attribute

activities = GenericRelation('activity.Activity', content_type_field='source_content_type', object_id_field='source_object_id', related_query_name='submission')

user class-attribute instance-attribute

user = ForeignKey(AUTH_USER_MODEL, on_delete=SET_NULL, null=True)

search_data class-attribute instance-attribute

search_data = TextField()

search_document class-attribute instance-attribute

search_document = SearchVectorField(null=True)

status class-attribute instance-attribute

status = FSMField(default=INITIAL_STATE, protected=True)

screening_statuses class-attribute instance-attribute

screening_statuses = ManyToManyField('funds.ScreeningStatus', related_name='submissions', blank=True)

submit_time class-attribute instance-attribute

submit_time = DateTimeField(verbose_name=gettext_lazy('submit time'), auto_now_add=False)

live_revision class-attribute instance-attribute

live_revision = OneToOneField('ApplicationRevision', on_delete=CASCADE, related_name='live', null=True, editable=False)

draft_revision class-attribute instance-attribute

draft_revision = OneToOneField('ApplicationRevision', on_delete=CASCADE, related_name='draft', null=True, editable=False)

drupal_id class-attribute instance-attribute

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

is_archive class-attribute instance-attribute

is_archive = BooleanField(default=False)

objects class-attribute instance-attribute

objects = as_manager()

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

is_draft property

is_draft

stage property

stage

phase property

phase

active property

active

is_determination_form_attached property

is_determination_form_attached

We use old django determination forms but now as we are moving to streamfield determination forms which can be created and attached to funds in admin.

This method checks if there are new determination forms attached to the submission or we would still use the old determination forms for backward compatibility.

has_all_reviewer_roles_assigned property

has_all_reviewer_roles_assigned

community_review property

community_review

missing_reviewers property

missing_reviewers

staff_not_reviewed property

staff_not_reviewed

reviewers_not_reviewed property

reviewers_not_reviewed

flagged_staff property

flagged_staff

ready_for_determination property

ready_for_determination

accepted_for_funding property

accepted_for_funding

in_final_stage property

in_final_stage

in_internal_review_phase property

in_internal_review_phase

in_external_review_phase property

in_external_review_phase

is_finished property

is_finished

has_default_screening_status_set property

has_default_screening_status_set

has_yes_default_screening_status_set property

has_yes_default_screening_status_set

has_no_default_screening_status_set property

has_no_default_screening_status_set

can_not_edit_default property

can_not_edit_default

joined_screening_statuses property

joined_screening_statuses

yes_screening_statuses property

yes_screening_statuses

no_screening_statuses property

no_screening_statuses

supports_default_screening property

supports_default_screening

Meta

indexes class-attribute instance-attribute
indexes = [GinIndex(fields=['search_document'])]

stream_file classmethod

stream_file(instance, field, file)
Source code in hypha/apply/funds/models/mixins.py
@classmethod
def stream_file(cls, instance, field, file):
    if not file:
        return []
    if isinstance(file, cls.stream_file_class):
        return file
    if isinstance(file, File):
        return cls.stream_file_class(
            instance, field, file, name=file.name, storage=cls.storage_class()
        )

    if isinstance(file, PlaceholderUploadedFile):
        return cls.stream_file_class(
            instance,
            field,
            None,
            name=file.file_id,
            filename=file.name,
            storage=cls.storage_class(),
        )

    # This fixes a backwards compatibility issue with #507
    # Once every application has been re-saved it should be possible to remove it
    if "path" in file:
        file["filename"] = file["name"]
        file["name"] = file["path"]
    return cls.stream_file_class(
        instance,
        field,
        None,
        name=file["name"],
        filename=file.get("filename"),
        storage=cls.storage_class(),
    )

process_file classmethod

process_file(instance, field, file)
Source code in hypha/apply/funds/models/mixins.py
@classmethod
def process_file(cls, instance, field, file):
    if isinstance(file, list):
        return [cls.stream_file(instance, field, f) for f in file]
    else:
        return cls.stream_file(instance, field, file)

process_file_data

process_file_data(data)
Source code in hypha/apply/funds/models/mixins.py
def process_file_data(self, data):
    for field in self.form_fields:
        if isinstance(field.block, UploadableMediaBlock):
            file = self.process_file(self, field, data.get(field.id, []))
            try:
                file.save()
            except (AttributeError, FileNotFoundError):
                try:
                    for f in file:
                        f.save()
                except FileNotFoundError:
                    pass
            self.form_data[field.id] = file

extract_files

extract_files()
Source code in hypha/apply/funds/models/mixins.py
def extract_files(self):
    files = {}
    for field in self.form_fields:
        if isinstance(field.block, UploadableMediaBlock):
            files[field.id] = self.data(field.id) or []
            self.form_data.pop(field.id, None)
    return files

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

deserialised_data classmethod

deserialised_data(instance, data, form_fields)
Source code in hypha/apply/funds/models/mixins.py
@classmethod
def deserialised_data(cls, instance, data, form_fields):
    # Converts the file dicts into actual file objects
    data = 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"]]
        if isinstance(block, UploadableMediaBlock):
            field_id = field_data.get("id")
            if field_id:
                field = form_fields[i]
                file = data.get(field_id, [])
                data[field_id] = cls.process_file(instance, field, file)
    return data

get_definitive_id

get_definitive_id(id)
Source code in hypha/apply/funds/models/mixins.py
def get_definitive_id(self, id):
    if id in self.named_blocks:
        return self.named_blocks[id]
    return id

field

field(id)
Source code in hypha/apply/funds/models/mixins.py
def field(self, id):
    definitive_id = self.get_definitive_id(id)
    try:
        return self.raw_fields[definitive_id]
    except KeyError:
        raise UnusedFieldException(id) from None

data

data(id)
Source code in hypha/apply/funds/models/mixins.py
def data(self, id):
    definitive_id = self.get_definitive_id(id)
    try:
        return self.raw_data[definitive_id]
    except KeyError:
        # We have most likely progressed application forms so the data isn't in form_data
        return None

get_serialize_multi_inputs_answer

get_serialize_multi_inputs_answer(field)
Source code in hypha/apply/funds/models/mixins.py
def get_serialize_multi_inputs_answer(self, field):
    number_of_inputs = field.value.get("number_of_inputs")
    answers = [self.data(field.id + "_" + str(i)) for i in range(number_of_inputs)]
    data = ", ".join(filter(None, answers))
    return data

serialize

serialize(field_id)
Source code in hypha/apply/funds/models/mixins.py
def serialize(self, field_id):
    field = self.field(field_id)
    if isinstance(field.block, MultiInputCharFieldBlock):
        data = self.get_serialize_multi_inputs_answer(field)
    else:
        data = self.data(field_id)
    return field.render(
        context={
            "serialize": True,
            "data": data,
        }
    )

get_multi_inputs_answer

get_multi_inputs_answer(field, include_question=False)
Source code in hypha/apply/funds/models/mixins.py
def get_multi_inputs_answer(self, field, include_question=False):
    number_of_inputs = field.value.get("number_of_inputs")
    answers = [self.data(field.id + "_" + str(i)) for i in range(number_of_inputs)]

    render_data = [
        field.render(
            context={
                "data": answer,
                "include_question": include_question if i == 0 else False,
            }
        )
        for i, answer in enumerate(filter(None, answers))
    ]
    return "".join(render_data).replace("</section>", "") + "</section>"

render_answer

render_answer(field_id, include_question=False)
Source code in hypha/apply/funds/models/mixins.py
def render_answer(self, field_id, include_question=False):
    try:
        field = self.field(field_id)
    except UnusedFieldException:
        return "-"
    if isinstance(field.block, MultiInputCharFieldBlock):
        render_data = self.get_multi_inputs_answer(field, include_question)
        return render_data
    else:
        data = self.data(field_id)
    # Some migrated content have empty address.
    if not data:
        return field.render(
            context={"data": "", "include_question": include_question}
        )
    return field.render(
        context={"data": data, "include_question": include_question}
    )

render_answers

render_answers()
Source code in hypha/apply/funds/models/mixins.py
def render_answers(self):
    # Returns a list of the rendered answers
    return [
        self.render_answer(field_id, include_question=True)
        for field_id in self.normal_blocks
    ]

render_first_group_text_answers

render_first_group_text_answers()
Source code in hypha/apply/funds/models/mixins.py
def render_first_group_text_answers(self):
    return [
        self.render_answer(field_id, include_question=True)
        for field_id in self.first_group_normal_text_blocks
    ]

render_text_blocks_answers

render_text_blocks_answers()
Source code in hypha/apply/funds/models/mixins.py
def render_text_blocks_answers(self):
    # Returns a list of the rendered answers of type text
    return [
        self.render_answer(field_id, include_question=True)
        for field_id in self.question_text_field_ids
        if field_id not in self.named_blocks
    ]

output_answers

output_answers()
Source code in hypha/apply/funds/models/mixins.py
def output_answers(self):
    # Returns a safe string of the rendered answers
    return mark_safe("".join(self.render_answers()))

output_text_answers

output_text_answers()
Source code in hypha/apply/funds/models/mixins.py
def output_text_answers(self):
    return mark_safe("".join(self.render_text_blocks_answers()))

output_first_group_text_answers

output_first_group_text_answers()
Source code in hypha/apply/funds/models/mixins.py
def output_first_group_text_answers(self):
    return mark_safe("".join(self.render_first_group_text_answers()))

get_answer_from_label

get_answer_from_label(label)
Source code in hypha/apply/funds/models/mixins.py
def get_answer_from_label(self, label):
    for field_id in self.question_text_field_ids:
        if field_id not in self.named_blocks:
            question_field = self.serialize(field_id)
            if label.lower() in question_field["question"].lower():
                if isinstance(question_field["answer"], str):
                    answer = question_field["answer"]
                else:
                    answer = ",".join(question_field["answer"])
                if answer and not answer == "N":
                    return answer
    return None

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()
Source code in hypha/apply/stream_forms/models.py
def get_defined_fields(self):
    return self.form_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),
    )

not_progressed

not_progressed()
Source code in hypha/apply/funds/models/submissions.py
def not_progressed(self):
    return not self.next

restart_stage

restart_stage(**kwargs)

If running form the console please include your user using the kwarg "by"

u = User.objects.get(email="my@email.com") for a in ApplicationSubmission.objects.all(): a.restart_stage(by=u) a.save()

Source code in hypha/apply/funds/models/submissions.py
@transition(
    status,
    source="*",
    target=RETURN_VALUE(INITIAL_STATE, "draft_proposal", "invited_to_proposal"),
    permission=make_permission_check({UserPermissions.ADMIN}),
)
def restart_stage(self, **kwargs):
    """
    If running form the console please include your user using the kwarg "by"

    u = User.objects.get(email="<[email protected]>")
    for a in ApplicationSubmission.objects.all():
        a.restart_stage(by=u)
        a.save()
    """
    if hasattr(self, "previous"):
        return "draft_proposal"
    elif self.next:
        return "invited_to_proposal"
    return INITIAL_STATE

ensure_user_has_account

ensure_user_has_account()
Source code in hypha/apply/funds/models/submissions.py
def ensure_user_has_account(self):
    if self.user and self.user.is_authenticated:
        self.form_data["email"] = self.user.email
        if name := self.user.get_full_name():
            self.form_data["full_name"] = name
        else:
            # user doesn't have name set, so use the one from the form
            self.user.full_name = self.form_data["full_name"]
            self.user.save()
    else:
        # Rely on the form having the following must include fields (see blocks.py)
        email = self.form_data.get("email")
        full_name = self.form_data.get("full_name")

        User = get_user_model()
        if "skip_account_creation_notification" in self.form_data:
            self.form_data.pop("skip_account_creation_notification", None)
            self.user, _ = User.objects.get_or_create(
                email=email, defaults={"full_name": full_name}
            )
        else:
            self.user, _ = User.objects.get_or_create_and_notify(
                email=email,
                site=self.page.get_site(),
                defaults={"full_name": full_name},
            )

    # Make sure the user is in the applicant group
    if not self.user.is_applicant:
        applicant_group = Group.objects.get(name=APPLICANT_GROUP_NAME)
        self.user.groups.add(applicant_group)
        self.user.save()

get_from_parent

get_from_parent(attribute)
Source code in hypha/apply/funds/models/submissions.py
def get_from_parent(self, attribute):
    try:
        return getattr(self.round.specific, attribute)
    except AttributeError:
        # We are a lab submission
        return getattr(self.page.specific, attribute)

progress_application

progress_application(**kwargs)
Source code in hypha/apply/funds/models/submissions.py
def progress_application(self, **kwargs):
    target = None
    for phase in STAGE_CHANGE_ACTIONS:
        transition = self.get_transition(phase)
        if can_proceed(transition):
            # We convert to dict as not concerned about transitions from the first phase
            # See note in workflow.py
            target = dict(PHASES)[phase].stage
    if not target:
        raise ValueError("Incorrect State for transition")

    submission_in_db = ApplicationSubmission.objects.get(id=self.id)
    prev_meta_terms = submission_in_db.meta_terms.all()

    self.id = None
    proposal_form = kwargs.get("proposal_form")
    proposal_form = int(proposal_form) if proposal_form else 0
    self.form_fields = self.get_from_parent("get_defined_fields")(
        target, form_index=proposal_form
    )

    self.live_revision = None
    self.draft_revision = None
    self.save()
    self.meta_terms.set(prev_meta_terms)

    submission_in_db.next = self
    submission_in_db.save()

new_data

new_data(data)
Source code in hypha/apply/funds/models/submissions.py
def new_data(self, data):
    self._is_draft = False
    self.form_data = data
    return self

from_draft

from_draft()
Source code in hypha/apply/funds/models/submissions.py
def from_draft(self):
    self._is_draft = True
    self.form_data = self.deserialised_data(
        self, self.draft_revision.form_data, self.form_fields
    )
    return self

create_revision

create_revision(draft=False, force=False, by=None, **kwargs)
Source code in hypha/apply/funds/models/submissions.py
def create_revision(self, draft=False, force=False, by=None, **kwargs):
    # Will return True/False if the revision was created or not
    ApplicationRevision = apps.get_model("funds", "ApplicationRevision")
    self.clean_submission()
    current_submission = ApplicationSubmission.objects.get(id=self.id)
    current_data = current_submission.form_data
    if current_data != self.form_data or force:
        if self.live_revision == self.draft_revision:
            revision = ApplicationRevision.objects.create(
                submission=self, form_data=self.form_data, author=by
            )
        else:
            revision = self.draft_revision
            revision.form_data = self.form_data
            revision.author = by
            revision.save()

        if draft:
            self.form_data = current_submission.form_data
        else:
            self.live_revision = revision
            self.search_data = " ".join(list(self.prepare_search_values()))
            self.search_document = self.prepare_search_vector()

        self.draft_revision = revision
        self.save(skip_custom=True)
        return revision
    return None

clean_submission

clean_submission()
Source code in hypha/apply/funds/models/submissions.py
def clean_submission(self):
    self.process_form_data()
    self.ensure_user_has_account()
    self.process_file_data(self.form_data)

get_assigned_meta_terms

get_assigned_meta_terms()

Returns assigned meta terms excluding the 'root' term

Source code in hypha/apply/funds/models/submissions.py
def get_assigned_meta_terms(self):
    """Returns assigned meta terms excluding the 'root' term"""
    return self.meta_terms.exclude(depth=1)

process_form_data

process_form_data()
Source code in hypha/apply/funds/models/submissions.py
def process_form_data(self):
    for field_name, field_id in self.named_blocks.items():
        response = self.form_data.pop(field_id, None)
        if response:
            self.form_data[field_name] = response

save

save(*args, update_fields=None, skip_custom=False, **kwargs)
Source code in hypha/apply/funds/models/submissions.py
def save(self, *args, update_fields=None, skip_custom=False, **kwargs):
    if update_fields is None:
        update_fields = []
    if update_fields and "form_data" not in update_fields:
        # We don't want to use this approach if the user is sending data
        return super().save(*args, update_fields=update_fields, **kwargs)
    elif skip_custom:
        return super().save(*args, **kwargs)

    if self._is_draft:
        raise ValueError("Cannot save with draft data")

    creating = not self.id

    if creating:
        self.submit_time = timezone.now()
        # We are creating the object default to first stage
        self.workflow_name = self.get_from_parent("workflow_name")
        # Copy extra relevant information to the child
        self.lead = self.get_from_parent("lead")

        # We need the submission id to correctly save the files
        files = self.extract_files()

    self.clean_submission()

    # add a denormed version of the answer for searching
    # @TODO: remove 'search_data' in favour of 'search_document' for FTS
    self.search_data = " ".join(self.prepare_search_values())
    self.search_document = self.prepare_search_vector()

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

    if creating:
        AssignedReviewers = apps.get_model("funds", "AssignedReviewers")
        ApplicationRevision = apps.get_model("funds", "ApplicationRevision")

        self.process_file_data(files)
        AssignedReviewers.objects.bulk_create_reviewers(
            list(self.get_from_parent("reviewers").all()),
            self,
        )
        first_revision = ApplicationRevision.objects.create(
            submission=self,
            form_data=self.form_data,
            author=self.user,
        )
        self.live_revision = first_revision
        self.draft_revision = first_revision
        self.save()

reviewed_by

reviewed_by(user)
Source code in hypha/apply/funds/models/submissions.py
def reviewed_by(self, user):
    return self.assigned.reviewed().filter(reviewer=user).exists()

flagged_by

flagged_by(user)
Source code in hypha/apply/funds/models/submissions.py
def flagged_by(self, user):
    return self.flags.filter(user=user, type=Flag.USER).exists()

has_permission_to_review

has_permission_to_review(user)
Source code in hypha/apply/funds/models/submissions.py
def has_permission_to_review(self, user):
    if user.is_apply_staff:
        return True

    if user in self.reviewers_not_reviewed:
        return True

    if (
        user.is_community_reviewer
        and self.user != user
        and self.community_review
        and not self.reviewed_by(user)
    ):
        return True

    return False

can_review

can_review(user)
Source code in hypha/apply/funds/models/submissions.py
def can_review(self, user):
    if self.reviewed_by(user):
        return False

    return self.has_permission_to_review(user)

can_view_draft

can_view_draft(user)
Source code in hypha/apply/funds/models/submissions.py
def can_view_draft(self, user):
    if self.user == user:
        return True

    if user.is_apply_staff and settings.SUBMISSIONS_DRAFT_ACCESS_STAFF:
        return True

    if user.is_apply_staff_admin and settings.SUBMISSIONS_DRAFT_ACCESS_STAFF_ADMIN:
        return True

    return False

get_searchable_contents

get_searchable_contents()
Source code in hypha/apply/funds/models/submissions.py
def get_searchable_contents(self):
    contents = []
    for field_id in self.question_field_ids:
        field = self.field(field_id)
        data = self.data(field_id)
        value = field.block.get_searchable_content(field.value, data)
        if value:
            if isinstance(value, list):
                contents.append(", ".join(value))
            else:
                contents.append(value)
    return contents

prepare_search_values

prepare_search_values()
Source code in hypha/apply/funds/models/submissions.py
def prepare_search_values(self):
    values = self.get_searchable_contents()

    # Add named fields into the search index
    for field in ["full_name", "email", "title"]:
        values.append(getattr(self, field))
    return values

index_components

index_components()
Source code in hypha/apply/funds/models/submissions.py
def index_components(self):
    return {
        "A": " ".join([f"id:{self.id}", self.title]),
        "C": " ".join([self.full_name, self.email]),
        "B": " ".join(self.get_searchable_contents()),
    }

prepare_search_vector

prepare_search_vector()
Source code in hypha/apply/funds/models/submissions.py
def prepare_search_vector(self):
    search_vectors = []
    for weight, text in self.index_components().items():
        search_vectors.append(SearchVector(Value(text), weight=weight))
    return reduce(operator.add, search_vectors)

get_absolute_url

get_absolute_url()
Source code in hypha/apply/funds/models/submissions.py
def get_absolute_url(self):
    return reverse("funds:submissions:detail", args=(self.id,))

get_data

get_data()
Source code in hypha/apply/funds/models/submissions.py
def get_data(self):
    # Updated for JSONField - Not used but base get_data will error
    form_data = self.form_data.copy()
    form_data.update(
        {
            "submit_time": self.submit_time,
        }
    )

    return form_data

FundType

Bases: ApplicationBase

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: _rpDfor (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

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

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

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

subpage_types class-attribute instance-attribute

subpage_types = ['funds.Round']

Meta

verbose_name class-attribute instance-attribute
verbose_name = gettext_lazy('Fund')

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)

RequestForPartners

Bases: ApplicationBase

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: _oJXfor (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

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

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

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

subpage_types class-attribute instance-attribute

subpage_types = ['funds.Round', 'funds.SealedRound']

Meta

verbose_name class-attribute instance-attribute
verbose_name = gettext_lazy('RFP')

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)

Round

Bases: RoundBase

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: _qJLfor (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

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

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)

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.

parent_page_types class-attribute instance-attribute

parent_page_types = ['funds.FundType', 'funds.RequestForPartners']

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),
        )

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,
    )

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")

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()

SealedRound

SealedRound(*args, **kwargs)

Bases: RoundBase

Source code in hypha/apply/funds/models/__init__.py
def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.sealed = True

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: _XZIfor (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

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

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

url class-attribute instance-attribute

url = property(get_url)

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.

parent_page_types class-attribute instance-attribute

parent_page_types = ['funds.RequestForPartners']

sealed instance-attribute

sealed = True

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),
        )

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,
    )

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")

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()

LabType

Bases: LabBase

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: _X4ffor (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

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

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 = []

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

verbose_name class-attribute instance-attribute
verbose_name = gettext_lazy('Lab')

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),
        )

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,
    )

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

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)