Skip to content

Commit

Permalink
Merge pull request #2966 from HyphaApp/enhancement/gh-2958-round-paf
Browse files Browse the repository at this point in the history
Make project approval form similar like ApplicationForms or Reviews Forms
  • Loading branch information
frjo authored Sep 12, 2022
2 parents 0acb382 + af5ad67 commit 453bd8d
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 3.2.15 on 2022-09-07 12:35

from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields


class Migration(migrations.Migration):

dependencies = [
('application_projects', '0055_alter_project_status_add_pafreviewersrole'),
('funds', '0101_auto_20220722_0844'),
]

operations = [
migrations.RemoveField(
model_name='applicationbase',
name='approval_form',
),
migrations.RemoveField(
model_name='labbase',
name='approval_form',
),
migrations.CreateModel(
name='LabBaseProjectApprovalForm',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
('form', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='application_projects.projectapprovalform')),
('lab', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='approval_forms', to='funds.labbase')),
],
options={
'ordering': ['sort_order'],
'abstract': False,
},
),
migrations.CreateModel(
name='ApplicationBaseProjectApprovalForm',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
('application', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='approval_forms', to='funds.applicationbase')),
('form', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='application_projects.projectapprovalform')),
],
options={
'ordering': ['sort_order'],
'abstract': False,
},
),
]
18 changes: 0 additions & 18 deletions hypha/apply/funds/models/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,6 @@ class ApplicationBase(EmailForm, WorkflowStreamForm): # type: ignore
blank=True,
)

approval_form = models.ForeignKey(
'application_projects.ProjectApprovalForm',
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='funds',
)

guide_link = models.URLField(blank=True, max_length=255, help_text=_('Link to the apply guide.'))

slack_channel = models.CharField(blank=True, max_length=128, help_text=_('The slack #channel for notifications. If left empty, notifications will go to the default channel.'))
Expand Down Expand Up @@ -117,7 +109,6 @@ def serve(self, request):
return self.open_round.serve(request)

content_panels = WorkflowStreamForm.content_panels + [
FieldPanel('approval_form'),
FieldPanel('reviewers', widget=forms.SelectMultiple(attrs={'size': '16'})),
FieldPanel('guide_link'),
FieldPanel('slack_channel'),
Expand Down Expand Up @@ -423,14 +414,6 @@ class LabBase(EmailForm, WorkflowStreamForm, SubmittableStreamForm): # type: ig
blank=True,
)

approval_form = models.ForeignKey(
'application_projects.ProjectApprovalForm',
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='labs',
)

guide_link = models.URLField(blank=True, max_length=255, help_text=_('Link to the apply guide.'))

slack_channel = models.CharField(blank=True, max_length=128, help_text=_('The slack #channel for notifications.'))
Expand All @@ -439,7 +422,6 @@ class LabBase(EmailForm, WorkflowStreamForm, SubmittableStreamForm): # type: ig
subpage_types = [] # type: ignore

content_panels = WorkflowStreamForm.content_panels + [
FieldPanel('approval_form'),
FieldPanel('lead'),
FieldPanel('reviewers', widget=forms.SelectMultiple(attrs={'size': '16'})),
FieldPanel('guide_link'),
Expand Down
37 changes: 37 additions & 0 deletions hypha/apply/funds/models/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,40 @@ class RoundBaseDeterminationForm(AbstractRelatedDeterminationForm):

class LabBaseDeterminationForm(AbstractRelatedDeterminationForm):
lab = ParentalKey('LabBase', related_name='determination_forms')


class AbstractRelatedProjectApprovalForm(Orderable):
class Meta(Orderable.Meta):
abstract = True

form = models.ForeignKey('application_projects.ProjectApprovalForm', on_delete=models.PROTECT)

@property
def fields(self):
return self.form.form_fields

def __eq__(self, other):
try:
if self.fields == other.fields and self.sort_order == other.sort_order:
# If the objects are saved to db. pk should also be compared
if hasattr(other, 'pk') and hasattr(self, 'pk'):
return self.pk == other.pk
return True
return False
except AttributeError:
return False

def __hash__(self):
fields = [field.id for field in self.fields]
return hash((tuple(fields), self.sort_order, self.pk))

def __str__(self):
return self.form.name


class ApplicationBaseProjectApprovalForm(AbstractRelatedProjectApprovalForm):
application = ParentalKey('ApplicationBase', related_name='approval_forms')


class LabBaseProjectApprovalForm(AbstractRelatedProjectApprovalForm):
lab = ParentalKey('LabBase', related_name='approval_forms')
3 changes: 2 additions & 1 deletion hypha/apply/funds/models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ def render_landing_page(self, request, form_submission=None, *args, **kwargs):
label=_('External Review Forms'), max_num=1,
help_text='Add a form to be used by external reviewers.'
),
InlinePanel('determination_forms', label=_('Determination Forms'))
InlinePanel('determination_forms', label=_('Determination Forms')),
InlinePanel('approval_forms', label=_('Project Approval Form'), max_num=1)
]


Expand Down
1 change: 0 additions & 1 deletion hypha/apply/funds/tests/factories/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ class Params:

# Will need to update how the stages are identified as Fund Page changes
workflow_name = factory.LazyAttribute(lambda o: workflow_for_stages(o.workflow_stages))
approval_form = factory.SubFactory('hypha.apply.projects.tests.factories.ProjectApprovalFormFactory')

@factory.post_generation
def forms(self, create, extracted, **kwargs):
Expand Down
38 changes: 27 additions & 11 deletions hypha/apply/funds/tests/test_admin_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from hypha.apply.determinations.tests.factories import DeterminationFormFactory
from hypha.apply.funds.models import FundType
from hypha.apply.projects.tests.factories import ProjectApprovalFormFactory
from hypha.apply.review.tests.factories import ReviewFormFactory

from .factories import ApplicationFormFactory, FundTypeFactory, workflow_for_stages
Expand Down Expand Up @@ -38,23 +39,24 @@ def formset_base(field, total, delete, factory, same=False, form_stage_info=None
return base_data


def form_data(num_appl_forms=0, num_review_forms=0, num_determination_forms=0, num_external_review_forms=0, delete=0, stages=1, same_forms=False, form_stage_info=[1]):
def form_data(num_appl_forms=0, num_review_forms=0, num_determination_forms=0, num_external_review_forms=0, num_project_approval_form=0, delete=0, stages=1, same_forms=False, form_stage_info=[1]):
form_data = formset_base(
'forms', num_appl_forms, delete, same=same_forms, factory=ApplicationFormFactory,
form_stage_info=form_stage_info)
review_form_data = formset_base('review_forms', num_review_forms, False, same=same_forms, factory=ReviewFormFactory)
external_review_form_data = formset_base('external_review_forms', num_external_review_forms, True, same=same_forms, factory=ReviewFormFactory)
determination_form_data = formset_base('determination_forms', num_determination_forms, False, same=same_forms, factory=DeterminationFormFactory)
project_approval_form_data = formset_base('approval_forms', num_project_approval_form, False, same=same_forms, factory=ProjectApprovalFormFactory)

form_data.update(review_form_data)
form_data.update(external_review_form_data)
form_data.update(determination_form_data)
form_data.update(project_approval_form_data)

fund_data = factory.build(dict, FACTORY_CLASS=FundTypeFactory)
fund_data['workflow_name'] = workflow_for_stages(stages)

form_data.update(fund_data)
form_data.update(approval_form='')
return form_data


Expand All @@ -70,15 +72,15 @@ def test_doesnt_validates_with_no_form(self):
self.assertTrue(form.errors['__all__'])

def test_validates_with_one_form_one_stage(self):
form = self.submit_data(form_data(1, 1, 1))
form = self.submit_data(form_data(1, 1, 1, 0, 1))
self.assertTrue(form.is_valid(), form.errors.as_text())

def test_validates_with_one_form_one_stage_with_deleted(self):
form = self.submit_data(form_data(1, 1, 1, delete=1, form_stage_info=[2, 1]))
form = self.submit_data(form_data(1, 1, 1, 0, 1, delete=1, form_stage_info=[2, 1]))
self.assertTrue(form.is_valid(), form.errors.as_text())

def test_doesnt_validates_with_two_forms_one_stage(self):
form = self.submit_data(form_data(2, 2, 2, form_stage_info=[1, 2]))
form = self.submit_data(form_data(2, 2, 2, 0, 1, form_stage_info=[1, 2]))
self.assertFalse(form.is_valid())
self.assertTrue(form.errors['__all__'])
formset_errors = form.formsets['forms'].errors
Expand All @@ -88,26 +90,40 @@ def test_doesnt_validates_with_two_forms_one_stage(self):
self.assertTrue(formset_errors[1]['form'])

def test_can_save_two_forms(self):
form = self.submit_data(form_data(2, 2, 2, stages=2, form_stage_info=[1, 2]))
form = self.submit_data(form_data(2, 2, 2, 0, 1, stages=2, form_stage_info=[1, 2]))
self.assertTrue(form.is_valid())

def test_can_save_multiple_forms_stage_two(self):
form = self.submit_data(form_data(3, 2, 2, stages=2, form_stage_info=[1, 2, 2]))
form = self.submit_data(form_data(3, 2, 2, 0, 1, stages=2, form_stage_info=[1, 2, 2]))
self.assertTrue(form.is_valid())

def test_doesnt_validates_with_two_first_stage_forms_in_two_stage(self):
form = self.submit_data(form_data(2, 2, 2, stages=2, form_stage_info=[1, 1]))
form = self.submit_data(form_data(2, 2, 2, 0, 1, stages=2, form_stage_info=[1, 1]))
self.assertFalse(form.is_valid())
self.assertTrue(form.errors['__all__'])

def test_validate_external_review_form(self):
form = self.submit_data(form_data(1, 1, 1, num_external_review_forms=1, stages=1))
form = self.submit_data(form_data(1, 1, 1, num_external_review_forms=1, num_project_approval_form=1, stages=1))
self.assertTrue(form.is_valid(), form.errors.as_text())

def test_validates_without_external_review_form(self):
form = self.submit_data(form_data(1, 1, 1, num_external_review_forms=0, stages=1))
form = self.submit_data(form_data(1, 1, 1, num_external_review_forms=0, num_project_approval_form=1, stages=1))
self.assertTrue(form.is_valid(), form.errors.as_text())

def test_doesnt_validates_with_multiple_external_review_form(self):
form = self.submit_data(form_data(1, 1, 1, num_external_review_forms=2, stages=1))
form = self.submit_data(form_data(1, 1, 1, num_external_review_forms=2, num_project_approval_form=1, stages=1))
self.assertFalse(form.is_valid(), form.errors.as_text())

def test_validate_project_approval_form(self):
form = self.submit_data(form_data(1, 1, 1, 0, num_project_approval_form=1, stages=1))
self.assertTrue(form.is_valid(), form.errors.as_text())

def test_does_validates_without_project_approval_form(self):
form = self.submit_data(form_data(1, 1, 1, 0, num_project_approval_form=0, stages=1))
self.assertTrue(form.is_valid(), form.errors.as_text())

def test_doesnt_validates_with_multiple_project_approval_form(self):
form = self.submit_data(form_data(1, 1, 1, 0, num_project_approval_form=2, stages=1))
self.assertFalse(form.is_valid(), form.errors.as_text())
form = self.submit_data(form_data(1, 1, 1, 0, num_project_approval_form=2, stages=2))
self.assertFalse(form.is_valid(), form.errors.as_text())
3 changes: 2 additions & 1 deletion hypha/apply/funds/tests/test_admin_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def setUpTestData(cls):
cls.user = SuperUserFactory()
cls.home = ApplyHomePageFactory()

def create_page(self, appl_forms=1, review_forms=1, determination_forms=1, external_review_form=0, stages=1, same_forms=False, form_stage_info=[1]):
def create_page(self, appl_forms=1, review_forms=1, determination_forms=1, external_review_form=0, project_approval_form=1, stages=1, same_forms=False, form_stage_info=[1]):
self.client.force_login(self.user)
url = reverse('wagtailadmin_pages:add', args=('funds', 'fundtype', self.home.id))

Expand All @@ -44,6 +44,7 @@ def create_page(self, appl_forms=1, review_forms=1, determination_forms=1, exter
review_forms,
determination_forms,
external_review_form,
project_approval_form,
same_forms=same_forms,
stages=stages,
form_stage_info=form_stage_info,
Expand Down
14 changes: 6 additions & 8 deletions hypha/apply/projects/views/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,10 +716,8 @@ def dispatch(self, request, *args, **kwargs):

@cached_property
def approval_form(self):
if self.object.get_defined_fields():
approval_form = self.object
else:
approval_form = self.object.submission.page.specific.approval_form
# fetching from the fund directly instead of going through round
approval_form = self.object.submission.page.specific.approval_forms.first() # picking up the first one

return approval_form

Expand All @@ -731,16 +729,16 @@ def get_context_data(self, **kwargs):
)

def get_defined_fields(self):
approval_form = self.object.submission.get_from_parent('approval_form')
approval_form = self.approval_form
if approval_form:
return approval_form.form_fields
return approval_form.form.form_fields
return self.object.get_defined_fields()

def get_form_kwargs(self):
kwargs = super().get_form_kwargs()

if self.approval_form:
fields = self.approval_form.get_form_fields()
fields = self.approval_form.form.get_form_fields()
else:
fields = {}

Expand All @@ -750,7 +748,7 @@ def get_form_kwargs(self):

def form_valid(self, form):
try:
form_fields = self.approval_form.form_fields
form_fields = self.approval_form.form.form_fields
except AttributeError:
form_fields = []

Expand Down

0 comments on commit 453bd8d

Please sign in to comment.