def test_multi_choice_field_process_form_data_price_change_deselected(multi_choice_field): # reg linked to old version, a currently selected item had its price changed and another changed # item was deselected. # field data should be upgraded to a new version containing both the new items and the old-priced one multi_choice_field.versioned_data = _update_data(multi_choice_field.versioned_data, {_id(2): {'is_billable': True, 'price': 100}, _id(3): {'price': 500}}) old_version = multi_choice_field.current_data old_data = RegistrationData(field_data=old_version, data={_id(2): 1, _id(3): 1}) assert old_data.price == 600 multi_choice_field.versioned_data = _update_data(multi_choice_field.versioned_data, {_id(2): {'price': 10}, _id(3): {'price': 50}}) form_data = {_id(3): 1} rv = multi_choice_field.field_impl.process_form_data(None, form_data, old_data) assert rv['field_data'] not in {multi_choice_field.current_data, old_version} assert rv['data'] == form_data new_data = RegistrationData(**rv) assert new_data.price == 500 old_choices = old_version.versioned_data['choices'] new_choices = multi_choice_field.versioned_data['choices'] combined = [old_choices[-1]] + new_choices[:-1] _assert_same_choices(rv['field_data'].versioned_data['choices'], combined) # now we re-check the previously deselected option and should get the NEW price form_data = {_id(2): 1, _id(3): 1} rv = multi_choice_field.field_impl.process_form_data(None, form_data, new_data) assert RegistrationData(**rv).price == 510
def create_registration(regform, data, invitation=None, management=False, notify_user=True): registration = Registration(registration_form=regform, user=get_user_by_email(data['email']), base_price=regform.base_price, currency=regform.currency) for form_item in regform.active_fields: if form_item.parent.is_manager_only: with db.session.no_autoflush: value = form_item.field_impl.default_value else: value = data.get(form_item.html_field_name) with db.session.no_autoflush: data_entry = RegistrationData() registration.data.append(data_entry) for attr, value in form_item.field_impl.process_form_data(registration, value).iteritems(): setattr(data_entry, attr, value) if form_item.type == RegistrationFormItemType.field_pd and form_item.personal_data_type.column: setattr(registration, form_item.personal_data_type.column, value) if invitation is None: # Associate invitation based on email in case the user did not use the link with db.session.no_autoflush: invitation = (RegistrationInvitation .find(email=data['email'], registration_id=None) .with_parent(regform) .first()) if invitation: invitation.state = InvitationState.accepted invitation.registration = registration registration.sync_state(_skip_moderation=management) db.session.flush() notify_registration_creation(registration, notify_user) logger.info('New registration %s by %s', registration, session.user) return registration
def _create_accompanying_persons_field(max_persons, persons_count_against_limit, registration=None, data=None, num_persons=0): section = RegistrationFormSection(registration_form=dummy_regform, title='dummy_section', is_manager_only=False) db.session.add(section) db.session.flush() field = RegistrationFormField(input_type='accompanying_persons', title='Field', parent=section, registration_form=dummy_regform) field.field_impl.form_item.data = { 'max_persons': max_persons, 'persons_count_against_limit': persons_count_against_limit, } field.versioned_data = field.field_impl.form_item.data if registration: registration.data.append( RegistrationData( field_data=field.current_data, data=(data if data is not None else _create_accompanying_persons(num_persons)))) db.session.flush() return field
def _clone_registrations(self, old_form, new_form, field_data_map): registration_attrs = get_simple_column_attrs(Registration) - { 'uuid', 'ticket_uuid' } for old_registration in old_form.registrations: if old_registration.is_deleted: continue new_registration = Registration(user=old_registration.user, registration_form=new_form, **{ attr: getattr( old_registration, attr) for attr in registration_attrs }) reg_data_attrs = get_simple_column_attrs(RegistrationData) - { 'storage_file_id', 'storage_backend', 'size' } for old_registration_data in old_registration.data: new_registration_data = RegistrationData( registration=new_registration, **{ attr: getattr(old_registration_data, attr) for attr in reg_data_attrs }) new_registration_data.field_data = field_data_map[ old_registration_data.field_data] if old_registration_data.storage_file_id is not None: with old_registration_data.open() as fd: new_registration_data.save(fd) db.session.flush() signals.event.registration_state_updated.send(new_registration, previous_state=None)
def _migrate_participant_data(self, old_part, registration): for pd_type, field in self.pd_field_map.iteritems(): if pd_type.column: friendly_value = value = getattr(registration, pd_type.column) elif pd_type == PersonalDataType.title: try: value = {self.title_map[old_part._title]: 1} except KeyError: value = None friendly_value = convert_to_unicode(old_part._title) elif pd_type == PersonalDataType.position: continue else: value = convert_to_unicode( getattr(old_part, PARTICIPANT_ATTR_MAP[pd_type])) if pd_type == PersonalDataType.phone and value: value = normalize_phone_number(value) friendly_value = value if value: field.is_enabled = True if not self.importer.quiet: self.importer.print_info( cformat('%{yellow!}{}%{reset} %{cyan!}{}%{reset}').format( pd_type.name, friendly_value)) registration.data.append( RegistrationData(field_data=field.current_data, data=value))
def modify_registration(registration, data, management=False, notify_user=True): old_price = registration.price personal_data_changes = {} regform = registration.registration_form data_by_field = registration.data_by_field if management or not registration.user: registration.user = get_user_by_email(data['email']) billable_items_locked = not management and registration.is_paid for form_item in regform.active_fields: field_impl = form_item.field_impl if management or not form_item.parent.is_manager_only: value = data.get(form_item.html_field_name) elif form_item.id not in data_by_field: # set default value for manager-only field if it didn't have one before value = field_impl.default_value else: # manager-only field that has data which should be preserved continue if form_item.id not in data_by_field: data_by_field[form_item.id] = RegistrationData( registration=registration, field_data=form_item.current_data) attrs = field_impl.process_form_data( registration, value, data_by_field[form_item.id], billable_items_locked=billable_items_locked) for key, val in attrs.iteritems(): setattr(data_by_field[form_item.id], key, val) if form_item.type == RegistrationFormItemType.field_pd and form_item.personal_data_type.column: key = form_item.personal_data_type.column if getattr(registration, key) != value: personal_data_changes[key] = value setattr(registration, key, value) registration.sync_state() db.session.flush() # sanity check if billable_items_locked and old_price != registration.price: raise Exception( "There was an error while modifying your registration (price mismatch: %s / %s)", old_price, registration.price) if personal_data_changes: signals.event.registration_personal_data_modified.send( registration, change=personal_data_changes) signals.event.registration_updated.send(registration, management=management) notify_registration_modification(registration, notify_user) logger.info('Registration %s modified by %s', registration, session.user) registration.log( EventLogRealm.management if management else EventLogRealm.participants, EventLogKind.change, 'Registration', 'Registration modified: {}'.format(registration.full_name), session.user, data={'Email': registration.email})
def create_registration(regform, data, invitation=None, management=False, notify_user=True, skip_moderation=None): user = session.user if session else None registration = Registration(registration_form=regform, user=get_user_by_email(data['email']), base_price=regform.base_price, currency=regform.currency) if skip_moderation is None: skip_moderation = management for form_item in regform.active_fields: if form_item.parent.is_manager_only: value = form_item.field_impl.default_value else: value = data.get(form_item.html_field_name) data_entry = RegistrationData() registration.data.append(data_entry) for attr, value in form_item.field_impl.process_form_data(registration, value).items(): setattr(data_entry, attr, value) if form_item.type == RegistrationFormItemType.field_pd and form_item.personal_data_type.column: setattr(registration, form_item.personal_data_type.column, value) if invitation is None: # Associate invitation based on email in case the user did not use the link invitation = (RegistrationInvitation.query .filter_by(email=data['email'], registration_id=None) .with_parent(regform) .first()) if invitation: invitation.state = InvitationState.accepted invitation.registration = registration registration.sync_state(_skip_moderation=skip_moderation) db.session.flush() signals.event.registration_created.send(registration, management=management, data=data) notify_registration_creation(registration, notify_user) logger.info('New registration %s by %s', registration, user) registration.log(EventLogRealm.management if management else EventLogRealm.participants, EventLogKind.positive, 'Registration', f'New registration: {registration.full_name}', user, data={'Email': registration.email}) return registration
def test_multi_choice_field_process_form_data_item_removed(multi_choice_field): # reg linked to old version, one selected item removed from there # in this case everything should remain linked to the old version old_version = multi_choice_field.current_data old_data = RegistrationData(field_data=old_version, data={_id(1): 1, _id(2): 1}) multi_choice_field.versioned_data = _update_data(multi_choice_field.versioned_data, {_id(1): None}) form_data = {_id(1): 1, _id(2): 1} assert multi_choice_field.field_impl.process_form_data(None, form_data, old_data) == {}
def test_multi_choice_field_process_form_data_old_version(multi_choice_field): # reg linked to old version, price changed in new version, nothing changed # in this case everything should remain linked to the old version old_version = multi_choice_field.current_data old_data = RegistrationData(field_data=old_version, data={_id(2): 1, _id(3): 1}) multi_choice_field.versioned_data = _update_data(multi_choice_field.versioned_data, {_id(3): {'price': 1000}}) form_data = {_id(2): 1, _id(3): 1} assert multi_choice_field.field_impl.process_form_data(None, form_data, old_data) == {}
def test_multi_choice_field_process_form_data_item_removed_deselected(multi_choice_field): # reg linked to old version, one selected item removed from there and deselected # since all other items are available in the latest version we upgrade to it old_version = multi_choice_field.current_data old_data = RegistrationData(field_data=old_version, data={_id(1): 1, _id(2): 1}) multi_choice_field.versioned_data = _update_data(multi_choice_field.versioned_data, {_id(1): None}) form_data = {_id(2): 1} rv = multi_choice_field.field_impl.process_form_data(None, form_data, old_data) assert rv['field_data'] == multi_choice_field.current_data assert rv['data'] == form_data
def test_multi_choice_field_process_form_data_only_new(multi_choice_field): # reg linked to old version, but all old items deselected # field data should be upgraded to the current version old_version = multi_choice_field.current_data old_data = RegistrationData(field_data=old_version, data={_id(1): 1, _id(2): 1}) multi_choice_field.versioned_data = _update_data(multi_choice_field.versioned_data, {_id(1): None, _id(4): {}}) form_data = {_id(4): 1} rv = multi_choice_field.field_impl.process_form_data(None, form_data, old_data) assert rv['field_data'] == multi_choice_field.current_data assert rv['data'] == form_data
def test_multi_choice_field_process_form_data_current_version( multi_choice_field): # nothing changed old_data = RegistrationData(field_data=multi_choice_field.current_data, data={ _id(1): 1, _id(2): 1 }) form_data = {_id(1): 1, _id(2): 1} assert multi_choice_field.field_impl.process_form_data( None, form_data, old_data) == {}
def test_multi_choice_field_process_form_data_mixed(multi_choice_field): # reg linked to old version, a currently selected item was deleted and a new item is selected # field data should be upgraded to a new version containing both the new items and the deleted one old_version = multi_choice_field.current_data old_data = RegistrationData(field_data=old_version, data={_id(1): 1}) multi_choice_field.versioned_data = _update_data(multi_choice_field.versioned_data, {_id(1): None, _id(4): {}}) form_data = {_id(1): 1, _id(4): 1} rv = multi_choice_field.field_impl.process_form_data(None, form_data, old_data) assert rv['field_data'] not in {multi_choice_field.current_data, old_version} assert rv['data'] == form_data combined = multi_choice_field.versioned_data['choices'] + [old_version.versioned_data['choices'][0]] _assert_same_choices(rv['field_data'].versioned_data['choices'], combined)
def _migrate_participant_status(self, old_part, registration): if not self.status_used: return if old_part._status not in {'added', 'pending'}: status_info = self.status_map[old_part._status] data = {status_info['uuid']: 1} caption = status_info['caption'] else: data = None caption = '' if not self.quiet and data: self.print_info('%[red]STATUS%[reset] %[cyan]{}'.format(caption)) registration.data.append( RegistrationData(field_data=self.status_field.current_data, data=data))
def test_multi_choice_field_process_form_data_mixed_price_change(multi_choice_field): # reg linked to old version, a currently selected item had its price changed and a new item is selected # field data should be upgraded to a new version containing both the new items and the old-priced one old_version = multi_choice_field.current_data old_data = RegistrationData(field_data=old_version, data={_id(3): 1}) multi_choice_field.versioned_data = _update_data(multi_choice_field.versioned_data, {_id(3): {'price': 1000}, _id(4): {}}) form_data = {_id(3): 1, _id(4): 1} rv = multi_choice_field.field_impl.process_form_data(None, form_data, old_data) assert rv['field_data'] not in {multi_choice_field.current_data, old_version} assert rv['data'] == form_data old_choices = old_version.versioned_data['choices'] new_choices = multi_choice_field.versioned_data['choices'] combined = new_choices[:2] + [old_choices[2]] + [new_choices[-1]] _assert_same_choices(rv['field_data'].versioned_data['choices'], combined)
def modify_registration(registration, data, management=False, notify_user=True): old_price = registration.price with db.session.no_autoflush: regform = registration.registration_form data_by_field = registration.data_by_field if management or not registration.user: registration.user = get_user_by_email(data['email']) billable_items_locked = not management and registration.is_paid for form_item in regform.active_fields: field_impl = form_item.field_impl if management or not form_item.parent.is_manager_only: value = data.get(form_item.html_field_name) elif form_item.id not in data_by_field: # set default value for manager-only field if it didn't have one before value = field_impl.default_value else: # manager-only field that has data which should be preserved continue if form_item.id not in data_by_field: data_by_field[form_item.id] = RegistrationData( registration=registration, field_data=form_item.current_data) attrs = field_impl.process_form_data( registration, value, data_by_field[form_item.id], billable_items_locked=billable_items_locked) for key, val in attrs.iteritems(): setattr(data_by_field[form_item.id], key, val) if form_item.type == RegistrationFormItemType.field_pd and form_item.personal_data_type.column: setattr(registration, form_item.personal_data_type.column, value) registration.sync_state() db.session.flush() # sanity check if billable_items_locked and old_price != registration.price: raise Exception( "There was an error while modifying your registration (price mismatch: %s / %s)", old_price, registration.price) notify_registration_modification(registration, notify_user) logger.info('Registration %s modified by %s', registration, session.user)
def modify_registration(registration, data, management=False, notify_user=True): old_data = snapshot_registration_data(registration) old_price = registration.price personal_data_changes = {} regform = registration.registration_form data_by_field = registration.data_by_field if 'email' in data and (management or not registration.user): registration.user = get_user_by_email(data['email']) billable_items_locked = not management and registration.is_paid for form_item in regform.active_fields: if form_item.is_purged: continue field_impl = form_item.field_impl has_data = form_item.html_field_name in data can_modify = management or not form_item.parent.is_manager_only if has_data and can_modify: value = data.get(form_item.html_field_name) elif not has_data and form_item.id not in data_by_field: # set default value for a field if it didn't have one before (including manager-only fields) value = field_impl.default_value else: # keep current value continue if form_item.id not in data_by_field: data_by_field[form_item.id] = RegistrationData( registration=registration, field_data=form_item.current_data) attrs = field_impl.process_form_data( registration, value, data_by_field[form_item.id], billable_items_locked=billable_items_locked) for key, val in attrs.items(): setattr(data_by_field[form_item.id], key, val) if form_item.type == RegistrationFormItemType.field_pd and form_item.personal_data_type.column: key = form_item.personal_data_type.column if getattr(registration, key) != value: personal_data_changes[key] = value setattr(registration, key, value) if not management and regform.needs_publish_consent: registration.consent_to_publish = data.get( 'consent_to_publish', RegistrationVisibility.nobody) registration.sync_state() db.session.flush() # sanity check if billable_items_locked and old_price != registration.price: raise Exception( 'There was an error while modifying your registration (price mismatch: %s / %s)', old_price, registration.price) if personal_data_changes: signals.event.registration_personal_data_modified.send( registration, change=personal_data_changes) signals.event.registration_updated.send(registration, management=management, data=data) new_data = snapshot_registration_data(registration) diff = diff_registration_data(old_data, new_data) notify_registration_modification(registration, notify_user, diff=diff, old_price=old_price) logger.info('Registration %s modified by %s', registration, session.user) registration.log( EventLogRealm.management if management else EventLogRealm.participants, LogKind.change, 'Registration', f'Registration modified: {registration.full_name}', session.user, data={'Email': registration.email})