from django.utils.text import mark_safe from django.core.files import File from opentech.apply.stream_forms.blocks import FormFieldBlock from opentech.apply.utils.blocks import MustIncludeFieldBlock from opentech.apply.stream_forms.blocks import UploadableMediaBlock from opentech.apply.stream_forms.files import StreamFieldFile __all__ = ['AccessFormData'] class AccessFormData: """Mixin for interacting with form data from streamfields requires: - form_data > jsonfield containing the submitted data - form_fields > streamfield containing the original form fields """ @property def raw_data(self): # Returns the data mapped by field id instead of the data stored using the must include # values data = self.form_data.copy() for field_name, field_id in self.must_include.items(): if field_id not in data: response = data[field_name] data[field_id] = response return data @classmethod def stream_file(cls, file): if isinstance(file, StreamFieldFile): return file if isinstance(file, File): return StreamFieldFile(file.file, name=file.name) return StreamFieldFile(None, name=file['name'], filename=file.get('filename')) @classmethod def process_file(cls, file): try: return cls.stream_file(file) except TypeError: return [cls.stream_file(f) for f in file] @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.form_data, instance.form_fields) return instance @classmethod def deserialised_data(cls, data, form_fields): # Converts the file dicts into actual file objects data = data.copy() for field in form_fields: if isinstance(field.block, UploadableMediaBlock): file = data.get(field.id, []) data[field.id] = cls.process_file(file) return data def get_definitive_id(self, id): if id in self.must_include: return self.must_include[id] return id def field(self, id): definitive_id = self.get_definitive_id(id) return self.raw_fields[definitive_id] def data(self, id): definitive_id = self.get_definitive_id(id) try: return self.raw_data[definitive_id] except KeyError as e: # We have most likely progressed application forms so the data isnt in form_data return None @property def question_field_ids(self): for field_id, field in self.fields.items(): if isinstance(field.block, FormFieldBlock): yield field_id @property def raw_fields(self): # Field ids to field class mapping - similar to raw_data return { field.id: field for field in self.form_fields } @property def fields(self): # ALl fields on the application fields = self.raw_fields.copy() for field_name, field_id in self.must_include.items(): response = fields.pop(field_id) fields[field_name] = response return fields @property def must_include(self): return { field.block.name: field.id for field in self.form_fields if isinstance(field.block, MustIncludeFieldBlock) } def render_answer(self, field_id, include_question=False): field = self.field(field_id) data = self.data(field_id) return field.render(context={'data': data, 'include_question': include_question}) 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.question_field_ids if field_id not in self.must_include ] def output_answers(self): # Returns a safe string of the rendered answers return mark_safe(''.join(self.render_answers()))