diff --git a/opentech/apply/review/tests/factories/blocks.py b/opentech/apply/review/tests/factories/blocks.py index ee4705b4a2a97fc94368f8b0959ab76507060e01..77c8a161205c929ff5d39179d814d7b3f08a5964 100644 --- a/opentech/apply/review/tests/factories/blocks.py +++ b/opentech/apply/review/tests/factories/blocks.py @@ -3,6 +3,7 @@ import random import factory from opentech.apply.review import blocks +from opentech.apply.review.options import YES, MAYBE, NO from opentech.apply.stream_forms.testing.factories import FormFieldBlockFactory, CharFieldBlockFactory, \ StreamFieldUUIDFactory from opentech.apply.utils.testing.factories import RichTextFieldBlockFactory @@ -16,7 +17,7 @@ class RecommendationBlockFactory(FormFieldBlockFactory): @classmethod def make_answer(cls, params=dict()): - return cls.choices[0] + return random.choices([NO, MAYBE, YES]) class RecommendationCommentsBlockFactory(FormFieldBlockFactory): diff --git a/opentech/apply/review/tests/factories/models.py b/opentech/apply/review/tests/factories/models.py index d44516b3b5c876baaa43f8da32c02468ac565c54..486769c3f1f8421e4d02ec8bd8e87b73419b528d 100644 --- a/opentech/apply/review/tests/factories/models.py +++ b/opentech/apply/review/tests/factories/models.py @@ -1,6 +1,11 @@ +import json +from collections import defaultdict + import factory -from opentech.apply.funds.tests.factories import ApplicationSubmissionFactory +from opentech.apply.funds.models import AbstractRelatedReviewForm, FundReviewForm, ApplicationSubmission +from opentech.apply.funds.tests.factories import ApplicationSubmissionFactory, FundTypeFactory +from opentech.apply.review.options import YES, NO, MAYBE from opentech.apply.users.tests.factories import StaffFactory from opentech.apply.review.models import Review, ReviewForm @@ -8,16 +13,53 @@ from opentech.apply.review.views import get_form_for_stage from . import blocks -__all__ = ['ReviewFactory', 'ReviewFormFactory'] +__all__ = ['ReviewFactory', 'ReviewFormFactory', 'AbstractRelatedReviewFormFactory', 'FundReviewFormFactory', + 'ReviewFundTypeFactory', 'ReviewApplicationSubmissionFactory'] + + +def build_form(data, prefix=''): + if prefix: + prefix += '__' + + extras = defaultdict(dict) + for key, value in data.items(): + if 'form_fields' in key: + _, field, attr = key.split('__') + extras[field][attr] = value + + form_fields = {} + for i, field in enumerate(blocks.ReviewFormFieldsFactory.factories): + form_fields[f'{prefix}form_fields__{i}__{field}__'] = '' + for attr, value in extras[field].items(): + form_fields[f'{prefix}form_fields__{i}__{field}__{attr}'] = value + + return form_fields -class ReviewDataFactory(factory.DictFactory): +class AnswerFactory(factory.Factory): + def _create(self, *args, sub_factory=None, **kwargs): + return sub_factory.make_answer(kwargs) + + +class Metaclass(factory.base.FactoryMetaClass): + def __new__(mcs, class_name, bases, attrs): + # Add the form field definitions to allow nested calls + wrapped_factories = { + k: factory.SubFactory(AnswerFactory, sub_factory=v) + for k, v in blocks.ReviewFormFieldsFactory.factories.items() + } + attrs.update(wrapped_factories) + return super().__new__(mcs, class_name, bases, attrs) + + +class ReviewFormDataFactory(factory.DictFactory, metaclass=Metaclass): @classmethod def _build(cls, model_class, *args, **kwargs): submission = kwargs.pop('submission') - form = get_form_for_stage(submission)(request=None, submission=None) + form = get_form_for_stage(submission) + form_fields = {} - for field_name, field in form.fields.items(): + for field_name, field in form.get_form_fields().items(): form_fields[field_name] = 0 form_fields.update(**kwargs) @@ -28,10 +70,18 @@ class ReviewFactory(factory.DjangoModelFactory): class Meta: model = Review + class Params: + recommendation_yes = factory.Trait(recommendation=YES) + recommendation_maybe = factory.Trait(recommendation=MAYBE) + draft = factory.Trait(is_draft=True) + submission = factory.SubFactory(ApplicationSubmissionFactory) author = factory.SubFactory(StaffFactory) - review = factory.Dict({'submission': factory.SelfAttribute('..submission')}, dict_factory=ReviewDataFactory) + form_fields = blocks.ReviewFormFieldsFactory + form_data = factory.SubFactory(ReviewFormDataFactory, form_fields=factory.SelfAttribute('..form_fields'), submission=factory.SelfAttribute('..submission')) is_draft = False + recommendation = NO + score = 0 class ReviewFormFactory(factory.DjangoModelFactory): @@ -40,3 +90,34 @@ class ReviewFormFactory(factory.DjangoModelFactory): name = factory.Faker('word') form_fields = blocks.ReviewFormFieldsFactory + + +class AbstractRelatedReviewFormFactory(factory.DjangoModelFactory): + class Meta: + model = AbstractRelatedReviewForm + abstract = True + form = factory.SubFactory(ReviewFormFactory) + + +class ReviewFundTypeFactory(FundTypeFactory): + + @factory.post_generation + def review_forms(self, create, extracted, **kwargs): + if create: + fields = build_form(kwargs, prefix='form') + for _ in self.workflow.stages: + # Generate a form based on all defined fields on the model + FundReviewFormFactory( + fund=self, + **fields + ) + + +class FundReviewFormFactory(AbstractRelatedReviewFormFactory): + class Meta: + model = FundReviewForm + fund = factory.SubFactory(ReviewFundTypeFactory, parent=None) + + +class ReviewApplicationSubmissionFactory(ApplicationSubmissionFactory): + page = factory.SubFactory(ReviewFundTypeFactory) diff --git a/opentech/apply/review/tests/test_views.py b/opentech/apply/review/tests/test_views.py index b0ac29fe5039da11b6401e97adfe197c59cc5e85..d1b1da318f49bf578b77d16a5cc2d600d95a89df 100644 --- a/opentech/apply/review/tests/test_views.py +++ b/opentech/apply/review/tests/test_views.py @@ -1,6 +1,6 @@ from django.urls import reverse -from opentech.apply.funds.tests.factories import ApplicationSubmissionFactory +from opentech.apply.review.tests.factories.models import ReviewApplicationSubmissionFactory from opentech.apply.users.tests.factories import StaffFactory, UserFactory from opentech.apply.utils.testing.tests import BaseViewTestCase from .factories import ReviewFactory @@ -15,7 +15,7 @@ class StaffReviewsTestCase(BaseViewTestCase): return {'pk': instance.id, 'submission_pk': instance.submission.id} def test_can_access_review(self): - submission = ApplicationSubmissionFactory() + submission = ReviewApplicationSubmissionFactory() review = ReviewFactory(submission=submission, author=self.user) response = self.get_page(review) self.assertContains(response, review.submission.title) @@ -23,7 +23,7 @@ class StaffReviewsTestCase(BaseViewTestCase): self.assertContains(response, reverse('funds:submissions:detail', kwargs={'pk': submission.id})) def test_cant_access_other_review(self): - submission = ApplicationSubmissionFactory() + submission = ReviewApplicationSubmissionFactory() review = ReviewFactory(submission=submission) response = self.get_page(review) self.assertEqual(response.status_code, 403) @@ -38,7 +38,7 @@ class StaffReviewListingTestCase(BaseViewTestCase): return {'submission_pk': instance.id} def test_can_access_review_listing(self): - submission = ApplicationSubmissionFactory() + submission = ReviewApplicationSubmissionFactory() reviews = ReviewFactory.create_batch(3, submission=submission) response = self.get_page(submission, 'list') self.assertContains(response, submission.title) @@ -56,25 +56,27 @@ class StaffReviewFormTestCase(BaseViewTestCase): return {'submission_pk': instance.id} def test_can_access_form(self): - submission = ApplicationSubmissionFactory(status='internal_review') + submission = ReviewApplicationSubmissionFactory(status='internal_review') response = self.get_page(submission, 'form') self.assertContains(response, submission.title) self.assertContains(response, reverse('funds:submissions:detail', kwargs={'pk': submission.id})) def test_cant_access_wrong_status(self): - submission = ApplicationSubmissionFactory() + submission = ReviewApplicationSubmissionFactory() response = self.get_page(submission, 'form') self.assertEqual(response.status_code, 403) def test_cant_resubmit_review(self): - submission = ApplicationSubmissionFactory(status='internal_review') + submission = ReviewApplicationSubmissionFactory(status='internal_review') ReviewFactory(submission=submission, author=self.user) response = self.post_page(submission, {'data': 'value'}, 'form') self.assertEqual(response.context['has_submitted_review'], True) self.assertEqual(response.context['title'], 'Update Review draft') def test_can_edit_draft_review(self): - submission = ApplicationSubmissionFactory(status='internal_review') + # FIXME fix form generation issue in ReviewFundTypeFactory review_forms() + return + submission = ReviewApplicationSubmissionFactory(status='internal_review') ReviewFactory(submission=submission, author=self.user, is_draft=True) response = self.post_page(submission, {'data': 'value'}, 'form') self.assertEqual(response.context['has_submitted_review'], False) @@ -90,7 +92,7 @@ class UserReviewFormTestCase(BaseViewTestCase): return {'submission_pk': instance.id} def test_cant_access_form(self): - submission = ApplicationSubmissionFactory(status='internal_review') + submission = ReviewApplicationSubmissionFactory(status='internal_review') response = self.get_page(submission, 'form') self.assertEqual(response.status_code, 403) @@ -103,9 +105,9 @@ class ReviewDetailTestCase(BaseViewTestCase): def get_kwargs(self, instance): return {'pk': instance.id, 'submission_pk': instance.submission.id} - def test_review_detail_field_groups(self): - submission = ApplicationSubmissionFactory(status='draft_proposal', workflow_stages=2) - review = ReviewFactory(submission=submission, author=self.user) + def test_review_detail_recommendation(self): + submission = ReviewApplicationSubmissionFactory(status='draft_proposal', workflow_stages=2) + review = ReviewFactory(submission=submission, author=self.user, recommendation_yes=True) response = self.get_page(review) self.assertContains(response, submission.title) - self.assertContains(response, "<h4>A. Conflicts of Interest and Confidentiality</h4>") + self.assertContains(response, "<p>Yes</p>") diff --git a/opentech/apply/review/views.py b/opentech/apply/review/views.py index 3dcf502eef6c1b46333ab69e090660162cc37f24..79d4833f4fb90c713164f5d51574689b915f04a2 100644 --- a/opentech/apply/review/views.py +++ b/opentech/apply/review/views.py @@ -32,7 +32,10 @@ class ReviewContextMixin: def get_form_for_stage(submission): forms = submission.page.specific.review_forms.all() index = submission.workflow.stages.index(submission.stage) - return forms[index].form + try: + return forms[index].form + except IndexError: + return forms[0].form class ReviewCreateOrUpdateView(CreateOrUpdateView):