From 58887516d4e1dc3fb36fb45ca05cef4734ba8b5d Mon Sep 17 00:00:00 2001
From: Todd Dembrey <todd.dembrey@torchbox.com>
Date: Mon, 22 Jan 2018 22:21:21 +0000
Subject: [PATCH] Add much custom code to validate that the required blocks
 appear

---
 opentech/apply/funds/blocks.py | 60 ++++++++++++++++++++++++++++++++--
 1 file changed, 57 insertions(+), 3 deletions(-)

diff --git a/opentech/apply/funds/blocks.py b/opentech/apply/funds/blocks.py
index 7750bb028..cbe885a8c 100644
--- a/opentech/apply/funds/blocks.py
+++ b/opentech/apply/funds/blocks.py
@@ -1,3 +1,7 @@
+from collections import Counter
+
+from django.core.exceptions import ValidationError
+from django.forms.utils import ErrorList
 from django.utils.translation import ugettext_lazy as _
 from django.utils.text import mark_safe
 
@@ -14,6 +18,51 @@ class CustomFormFieldsBlock(FormFieldsBlock):
         child_blocks = [(block.name, block(group=_('Required'))) for block in MustIncludeFieldBlock.__subclasses__()]
         super().__init__(child_blocks, *args, **kwargs)
 
+    def clean(self, value):
+        try:
+            value = super().clean(value)
+        except ValidationError as e:
+            error_dict = e.params
+        else:
+            error_dict = dict()
+
+        required_block_names = [block.name  for block in MustIncludeFieldBlock.__subclasses__()]
+
+        block_types = [block.block_type for block in value]
+        missing = set(required_block_names) - set(block_types)
+
+        counted_types = Counter(block_types)
+        duplicates = [
+            name for name, count in counted_types.items()
+            if name in required_block_names and count > 1
+        ]
+
+        all_errors = list()
+        if missing:
+            all_errors.append(
+                'You are missing the following required fields: {}'.format(', '.join(missing).title())
+            )
+
+        if duplicates:
+            all_errors.append(
+                'You have duplicates of the following required fields: {}'.format(', '.join(duplicates).title())
+            )
+            for name in duplicates:
+                for i, block_name in enumerate(block_types):
+                    if block_name == name:
+                        try:
+                            error_dict[i].data[0].params['info'] = ErrorList(['Duplicate'])
+                        except KeyError:
+                            error_dict[i] = ErrorList(
+                                [ValidationError('Error', params={'info': ErrorList(['Duplicate'])})]
+                            )
+
+        if all_errors:
+            error_dict['__all__'] = all_errors
+            raise ValidationError('Error', params=error_dict)
+
+        return value
+
 
 class MustIncludeStatic(StaticBlock):
     def __init__(self, *args, description='', **kwargs):
@@ -24,15 +73,20 @@ class MustIncludeStatic(StaticBlock):
         admin_text = 'Much be included in the form once.'
 
     def render_form(self, *args, **kwargs):
+        errors = kwargs.pop('errors')
+        if errors:
+            error_message= '<div class="error"><input readonly placeholder="{}"></div>'.format(errors[0])
+        else:
+            error_message = ''
         form = super().render_form(*args, **kwargs)
-        form = '<br>'.join([self.description, form])
+        form = '<br>'.join([self.description, form]) + error_message
         return mark_safe(form)
 
 
 class MustIncludeFieldBlock(FormFieldBlock):
     def __init__(self, *args, **kwargs):
-        info_name = f'{self.name}_field'
-        child_blocks = [(info_name, MustIncludeStatic(description=self.description))]
+        info_name = f'{self.name.title()} Field'
+        child_blocks = [('info', MustIncludeStatic(label=info_name, description=self.description))]
         super().__init__(child_blocks, *args, **kwargs)
 
 
-- 
GitLab