Skip to content

Models

hypha.apply.determinations.models

DeterminationQuerySet

Bases: QuerySet

staff

staff()
Source code in hypha/apply/determinations/models.py
def staff(self):
    # Designed to be used with a queryset related to submissions
    return self.all().order_by("-updated_at")

active

active()
Source code in hypha/apply/determinations/models.py
def active(self):
    # Designed to be used with a queryset related to submissions
    return self.get(is_draft=True)

submitted

submitted()
Source code in hypha/apply/determinations/models.py
def submitted(self):
    return self.filter(is_draft=False).order_by("-updated_at")

final

final()
Source code in hypha/apply/determinations/models.py
def final(self):
    return self.submitted().filter(outcome__in=[ACCEPTED, REJECTED])

DeterminationFormFieldsMixin

Bases: Model

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

form_fields class-attribute instance-attribute

form_fields = StreamField(DeterminationCustomFormFieldsBlock(), default=[], use_json_field=True)

determination_field property

determination_field

message_field property

message_field

send_notice_field property

send_notice_field

Meta

abstract class-attribute instance-attribute
abstract = True

DeterminationForm

Bases: DeterminationFormFieldsMixin, Model

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

form_fields class-attribute instance-attribute

form_fields = StreamField(DeterminationCustomFormFieldsBlock(), default=[], use_json_field=True)

determination_field property

determination_field

message_field property

message_field

send_notice_field property

send_notice_field

name class-attribute instance-attribute

name = CharField(max_length=255)

panels class-attribute instance-attribute

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

Meta

abstract class-attribute instance-attribute
abstract = True

Determination

Bases: DeterminationFormFieldsMixin, 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

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

form_fields class-attribute instance-attribute

form_fields = StreamField(DeterminationCustomFormFieldsBlock(), default=[], use_json_field=True)

determination_field property

determination_field

message_field property

message_field

send_notice_field property

send_notice_field

submission class-attribute instance-attribute

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

author class-attribute instance-attribute

author = ForeignKey(AUTH_USER_MODEL, on_delete=PROTECT)

outcome class-attribute instance-attribute

outcome = IntegerField(verbose_name=gettext_lazy('Determination'), choices=DETERMINATION_CHOICES, default=1)

message class-attribute instance-attribute

message = TextField(verbose_name=gettext_lazy('Determination message'), blank=True)

data class-attribute instance-attribute

data = JSONField(blank=True, null=True)

form_data class-attribute instance-attribute

form_data = JSONField(default=dict, encoder=DjangoJSONEncoder)

is_draft class-attribute instance-attribute

is_draft = BooleanField(default=False, verbose_name=gettext_lazy('Draft'))

created_at class-attribute instance-attribute

created_at = DateTimeField(verbose_name=gettext_lazy('Creation time'), auto_now_add=True)

updated_at class-attribute instance-attribute

updated_at = DateTimeField(verbose_name=gettext_lazy('Update time'), auto_now=True)

send_notice class-attribute instance-attribute

send_notice = BooleanField(default=True, verbose_name=gettext_lazy('Send message to applicant'))

drupal_id class-attribute instance-attribute

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

objects class-attribute instance-attribute

objects = as_manager()

stripped_message property

stripped_message

clean_outcome property

clean_outcome

submitted property

submitted

use_new_determination_form property

use_new_determination_form

Checks if a submission has the new streamfield determination form attached to it and along with that it also verify that if self.data is None.

self.data would be set as None for the determination which are created using streamfield determination forms.

But old lab forms can be edited to add new determination forms so we need to use old determination forms for already submitted determination.

detailed_data property

detailed_data

Meta

abstract class-attribute instance-attribute
abstract = True

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/funds/models/mixins.py
@classmethod
def from_db(cls, db, field_names, values):
    instance = super().from_db(db, field_names, values)
    if "form_data" in field_names:
        # When the form_data is loaded from the DB deserialise it
        instance.form_data = cls.deserialised_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

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

get_absolute_url

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

get_detailed_response

get_detailed_response()
Source code in hypha/apply/determinations/models.py
def get_detailed_response(self):
    data = {}
    group = 0
    data.setdefault(group, {"title": None, "questions": []})
    for field in self.form_fields:
        if issubclass(
            field.block.__class__, DeterminationMustIncludeFieldBlock
        ) or isinstance(field.block, SendNoticeBlock):
            continue
        try:
            value = self.form_data[field.id]
        except KeyError:
            group = group + 1
            data.setdefault(group, {"title": field.value.source, "questions": []})
        else:
            data[group]["questions"].append((field.value.get("field_label"), value))
    return data

DeterminationMessageSettings

Bases: BaseSiteSetting

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

request_accepted class-attribute instance-attribute

request_accepted = RichTextField('Approved', blank=True)

request_rejected class-attribute instance-attribute

request_rejected = RichTextField('Dismissed', blank=True)

request_more_info class-attribute instance-attribute

request_more_info = RichTextField('Needs more info', blank=True)

concept_accepted class-attribute instance-attribute

concept_accepted = RichTextField('Approved', blank=True)

concept_rejected class-attribute instance-attribute

concept_rejected = RichTextField('Dismissed', blank=True)

concept_more_info class-attribute instance-attribute

concept_more_info = RichTextField('Needs more info', blank=True)

proposal_accepted class-attribute instance-attribute

proposal_accepted = RichTextField('Approved', blank=True)

proposal_rejected class-attribute instance-attribute

proposal_rejected = RichTextField('Dismissed', blank=True)

proposal_more_info class-attribute instance-attribute

proposal_more_info = RichTextField('Needs more info', blank=True)

request_tab_panels class-attribute instance-attribute

request_tab_panels = [FieldPanel('request_accepted'), FieldPanel('request_rejected'), FieldPanel('request_more_info')]

concept_tab_panels class-attribute instance-attribute

concept_tab_panels = [FieldPanel('concept_accepted'), FieldPanel('concept_rejected'), FieldPanel('concept_more_info')]

proposal_tab_panels class-attribute instance-attribute

proposal_tab_panels = [FieldPanel('proposal_accepted'), FieldPanel('proposal_rejected'), FieldPanel('proposal_more_info')]

edit_handler class-attribute instance-attribute

edit_handler = TabbedInterface([ObjectList(request_tab_panels, heading=gettext_lazy('Request')), ObjectList(concept_tab_panels, heading=gettext_lazy('Concept note')), ObjectList(proposal_tab_panels, heading=gettext_lazy('Proposal'))])

Meta

verbose_name class-attribute instance-attribute
verbose_name = 'determination messages'

get_for_stage

get_for_stage(stage_name)
Source code in hypha/apply/determinations/models.py
def get_for_stage(self, stage_name):
    message_templates = {}
    if stage_name in [Request.name, Concept.name, Proposal.name]:
        prefix = f"{stage_name.lower()}_"
    else:
        # Use Request's message templates for remaining workflows
        prefix = "request_"

    for field in self._meta.get_fields():
        if prefix in field.name:
            key = field.name.replace(prefix, "")
            message_templates[key] = getattr(self, field.name)

    return message_templates

DeterminationFormSettings

Bases: BaseSiteSetting

concept_principles_label class-attribute instance-attribute

concept_principles_label = CharField('label', default='Goals and principles', max_length=255)

concept_principles_help_text class-attribute instance-attribute

concept_principles_help_text = TextField('help text', blank=True)

concept_technical_label class-attribute instance-attribute

concept_technical_label = CharField('label', default='Technical merit', max_length=255)

concept_technical_help_text class-attribute instance-attribute

concept_technical_help_text = TextField('help text', blank=True)

concept_sustainable_label class-attribute instance-attribute

concept_sustainable_label = CharField('label', default='Reasonable, realistic and sustainable', max_length=255)

concept_sustainable_help_text class-attribute instance-attribute

concept_sustainable_help_text = TextField('help text', blank=True)

proposal_liked_label class-attribute instance-attribute

proposal_liked_label = CharField('label', default='Positive aspects', max_length=255)

proposal_liked_help_text class-attribute instance-attribute

proposal_liked_help_text = TextField('help text', blank=True)

proposal_concerns_label class-attribute instance-attribute

proposal_concerns_label = CharField('label', default='Concerns', max_length=255)

proposal_concerns_help_text class-attribute instance-attribute

proposal_concerns_help_text = TextField('help text', blank=True)

proposal_red_flags_label class-attribute instance-attribute

proposal_red_flags_label = CharField('label', default='Items that must be addressed', max_length=255)

proposal_red_flags_help_text class-attribute instance-attribute

proposal_red_flags_help_text = TextField('help text', blank=True)

proposal_overview_label class-attribute instance-attribute

proposal_overview_label = CharField('label', default='Project overview questions and comments', max_length=255)

proposal_overview_help_text class-attribute instance-attribute

proposal_overview_help_text = TextField('help text', blank=True)

proposal_objectives_label class-attribute instance-attribute

proposal_objectives_label = CharField('label', default='Objectives questions and comments', max_length=255)

proposal_objectives_help_text class-attribute instance-attribute

proposal_objectives_help_text = TextField('help text', blank=True)

proposal_strategy_label class-attribute instance-attribute

proposal_strategy_label = CharField('label', default='Methods and strategy questions and comments', max_length=255)

proposal_strategy_help_text class-attribute instance-attribute

proposal_strategy_help_text = TextField('help text', blank=True)

proposal_technical_label class-attribute instance-attribute

proposal_technical_label = CharField('label', default='Technical feasibility questions and comments', max_length=255)

proposal_technical_help_text class-attribute instance-attribute

proposal_technical_help_text = TextField('help text', blank=True)

proposal_alternative_label class-attribute instance-attribute

proposal_alternative_label = CharField('label', default='Alternative analysis - "red teaming" questions and comments', max_length=255)

proposal_alternative_help_text class-attribute instance-attribute

proposal_alternative_help_text = TextField('help text', blank=True)

proposal_usability_label class-attribute instance-attribute

proposal_usability_label = CharField('label', default='Usability questions and comments', max_length=255)

proposal_usability_help_text class-attribute instance-attribute

proposal_usability_help_text = TextField('help text', blank=True)

proposal_sustainability_label class-attribute instance-attribute

proposal_sustainability_label = CharField('label', default='Sustainability questions and comments', max_length=255)

proposal_sustainability_help_text class-attribute instance-attribute

proposal_sustainability_help_text = TextField('help text', blank=True)

proposal_collaboration_label class-attribute instance-attribute

proposal_collaboration_label = CharField('label', default='Collaboration questions and comments', max_length=255)

proposal_collaboration_help_text class-attribute instance-attribute

proposal_collaboration_help_text = TextField('help text', blank=True)

proposal_realism_label class-attribute instance-attribute

proposal_realism_label = CharField('label', default='Cost realism questions and comments', max_length=255)

proposal_realism_help_text class-attribute instance-attribute

proposal_realism_help_text = TextField('help text', blank=True)

proposal_qualifications_label class-attribute instance-attribute

proposal_qualifications_label = CharField('label', default='Qualifications questions and comments', max_length=255)

proposal_qualifications_help_text class-attribute instance-attribute

proposal_qualifications_help_text = TextField('help text', blank=True)

proposal_evaluation_label class-attribute instance-attribute

proposal_evaluation_label = CharField('label', default='Evaluation questions and comments', max_length=255)

proposal_evaluation_help_text class-attribute instance-attribute

proposal_evaluation_help_text = TextField('help text', blank=True)

concept_help_text_tab_panels class-attribute instance-attribute

concept_help_text_tab_panels = [MultiFieldPanel([FieldPanel('concept_principles_label'), FieldPanel('concept_principles_help_text')], 'concept principles'), MultiFieldPanel([FieldPanel('concept_technical_label'), FieldPanel('concept_technical_help_text')], 'concept technical'), MultiFieldPanel([FieldPanel('concept_sustainable_label'), FieldPanel('concept_sustainable_help_text')], 'concept sustainable')]

proposal_help_text_tab_panels class-attribute instance-attribute

proposal_help_text_tab_panels = [MultiFieldPanel([FieldPanel('proposal_liked_label'), FieldPanel('proposal_liked_help_text')], 'proposal liked'), MultiFieldPanel([FieldPanel('proposal_concerns_label'), FieldPanel('proposal_concerns_help_text')], 'proposal concerns'), MultiFieldPanel([FieldPanel('proposal_red_flags_label'), FieldPanel('proposal_red_flags_help_text')], 'proposal red flags'), MultiFieldPanel([FieldPanel('proposal_overview_label'), FieldPanel('proposal_overview_help_text')], 'proposal overview'), MultiFieldPanel([FieldPanel('proposal_objectives_label'), FieldPanel('proposal_objectives_help_text')], 'proposal objectives'), MultiFieldPanel([FieldPanel('proposal_strategy_label'), FieldPanel('proposal_strategy_help_text')], 'proposal strategy'), MultiFieldPanel([FieldPanel('proposal_technical_label'), FieldPanel('proposal_technical_help_text')], 'proposal technical'), MultiFieldPanel([FieldPanel('proposal_alternative_label'), FieldPanel('proposal_alternative_help_text')], 'proposal alternative'), MultiFieldPanel([FieldPanel('proposal_usability_label'), FieldPanel('proposal_usability_help_text')], 'proposal usability'), MultiFieldPanel([FieldPanel('proposal_sustainability_label'), FieldPanel('proposal_sustainability_help_text')], 'proposal sustainability'), MultiFieldPanel([FieldPanel('proposal_collaboration_label'), FieldPanel('proposal_collaboration_help_text')], 'proposal collaboration'), MultiFieldPanel([FieldPanel('proposal_realism_label'), FieldPanel('proposal_realism_help_text')], 'proposal realism'), MultiFieldPanel([FieldPanel('proposal_qualifications_label'), FieldPanel('proposal_qualifications_help_text')], 'proposal qualifications'), MultiFieldPanel([FieldPanel('proposal_evaluation_label'), FieldPanel('proposal_evaluation_help_text')], 'proposal evaluation')]

edit_handler class-attribute instance-attribute

edit_handler = TabbedInterface([ObjectList(concept_help_text_tab_panels, heading=gettext_lazy('Concept form')), ObjectList(proposal_help_text_tab_panels, heading=gettext_lazy('Proposal form'))])

Meta

verbose_name class-attribute instance-attribute
verbose_name = 'determination form settings'