def create_personal_data_fields(regform): """Creates the special section/fields for personal data.""" section = next((s for s in regform.sections if s.type == RegistrationFormItemType.section_pd), None) if section is None: section = RegistrationFormPersonalDataSection( registration_form=regform, title='Personal Data') missing = set(PersonalDataType) else: existing = { x.personal_data_type for x in section.children if x.type == RegistrationFormItemType.field_pd } missing = set(PersonalDataType) - existing for pd_type, data in PersonalDataType.FIELD_DATA: if pd_type not in missing: continue field = RegistrationFormPersonalDataField( registration_form=regform, personal_data_type=pd_type, is_required=pd_type.is_required) if not data.get('is_enabled', True): field.position = data['position'] for key, value in data.iteritems(): setattr(field, key, value) field.data, versioned_data = field.field_impl.process_field_data( data.pop('data', {})) field.current_data = RegistrationFormFieldData( versioned_data=versioned_data) section.children.append(field)
def _clone_form_items(self, old_form, new_form, clone_all_revisions): old_sections = RegistrationFormSection.find( RegistrationFormSection.registration_form_id == old_form.id) items_attrs = get_simple_column_attrs(RegistrationFormSection) for old_section in old_sections: new_section = RegistrationFormSection( **{attr: getattr(old_section, attr) for attr in items_attrs}) for old_item in old_section.children: new_item = RegistrationFormItem( parent=new_section, registration_form=new_form, **{attr: getattr(old_item, attr) for attr in items_attrs}) if new_item.is_field: if clone_all_revisions: self._clone_all_field_versions(old_item, new_item) else: field_data = RegistrationFormFieldData( field=new_item, versioned_data=old_item.versioned_data) new_item.current_data = field_data self._field_data_map[ old_item.current_data] = field_data new_section.children.append(new_item) new_form.form_items.append(new_section) db.session.flush()
def _clone_all_field_versions(self, old_item, new_item): for old_version in old_item.data_versions: new_version = RegistrationFormFieldData(versioned_data=old_version.versioned_data, field=new_item) if old_version.id == old_item.current_data_id: new_item.current_data = new_version new_item.data_versions.append(new_version) self._field_data_map[old_version] = new_version
def process_form_data(self, registration, value, old_data=None, billable_items_locked=False, new_data_version=None): # always store no-option as empty dict if value is None: value = {} return_value = {} has_old_data = old_data is not None and old_data.data is not None if has_old_data: # in case nothing changed we can skip all checks if old_data.data == value: return {} selected_choice_hashes = {c['id']: _hashable_choice(c) for c in old_data.field_data.versioned_data['choices'] if c['id'] in value} selected_choice_hashes.update({c['id']: _hashable_choice(c) for c in self.form_item.versioned_data['choices'] if c['id'] in value and c['id'] not in selected_choice_hashes}) selected_choice_hashes = set(selected_choice_hashes.values()) existing_version_hashes = {c['id']: _hashable_choice(c) for c in old_data.field_data.versioned_data['choices']} latest_version_hashes = {c['id']: _hashable_choice(c) for c in self.form_item.versioned_data['choices']} deselected_ids = old_data.data.keys() - value.keys() modified_deselected = any(latest_version_hashes.get(id_) != existing_version_hashes.get(id_) for id_ in deselected_ids) if selected_choice_hashes <= set(latest_version_hashes.values()): # all choices available in the latest version - upgrade to that version return_value['field_data'] = self.form_item.current_data elif not modified_deselected and selected_choice_hashes <= set(existing_version_hashes.values()): # all choices available in the previously selected version - stay with it return_value['field_data'] = old_data.field_data else: # create a new version containing selected choices from the previously # selected version and everything else from the latest version new_choices = [] used_ids = set() for choice in old_data.field_data.versioned_data['choices']: # copy all old choices that are currently selected if choice['id'] in value: used_ids.add(choice['id']) new_choices.append(choice) for choice in self.form_item.versioned_data['choices']: # copy all new choices unless we already got them from the old version if choice['id'] not in used_ids: used_ids.add(choice['id']) new_choices.append(choice) new_choices_hash = {_hashable_choice(x) for x in new_choices} for data_version in self.form_item.data_versions: if {_hashable_choice(x) for x in data_version.versioned_data['choices']} == new_choices_hash: break else: data_version = RegistrationFormFieldData(field=self.form_item, versioned_data={'choices': new_choices}) return_value['field_data'] = data_version new_choices = return_value['field_data'].versioned_data['choices'] if not billable_items_locked: processed_data = super().process_form_data(registration, value, old_data, False, return_value.get('field_data')) return {key: return_value.get(key, value) for key, value in processed_data.items()} # XXX: This code still relies on the client sending data for the disabled fields. # This is pretty ugly but especially in case of non-billable extra slots it makes # sense to keep it like this. If someone tampers with the list of billable fields # we detect it any reject the change to the field's data anyway. if has_old_data: old_choices_mapping = {x['id']: x for x in old_data.field_data.versioned_data['choices']} new_choices_mapping = {x['id']: x for x in new_choices} old_billable = {uuid: num for uuid, num in old_data.data.items() if old_choices_mapping[uuid]['is_billable'] and old_choices_mapping[uuid]['price']} new_billable = {uuid: num for uuid, num in value.items() if new_choices_mapping[uuid]['is_billable'] and new_choices_mapping[uuid]['price']} if has_old_data and old_billable != new_billable: # preserve existing data return return_value else: # nothing price-related changed # TODO: check item prices (in case there's a change between old/new version) # for now we simply ignore field changes in this case (since the old/new price # check in the base method will fail) processed_data = super().process_form_data(registration, value, old_data, True, return_value.get('field_data')) return {key: return_value.get(key, value) for key, value in processed_data.items()}