def test_schema_has_private(dbsession): """ It should be able to determine if a schema has private attributes """ from datetime import date from occams import models schema = models.Schema(name='Foo', title=u'Foo', publish_date=date(2014, 3, 31), attributes={ 'not_private': models.Attribute(name='not_private', title=u'', type='string', is_private=False, order=0) }) dbsession.add(schema) dbsession.flush() assert not schema.has_private schema.attributes['is_private'] = models.Attribute(name='is_private', title=u'', type='string', is_private=True, order=1) assert schema.has_private
def test_attribute_unique_case_insensitive(dbsession): """ It should enforce case-insensitive attributes """ from datetime import date import sqlalchemy.exc from occams import models schema = models.Schema(name='Foo', title=u'Foo', publish_date=date(2014, 3, 31)) schema.attributes['MyAttr'] = models.Attribute(name=u'MyAttr', title=u'My Attribute', type=u'string', order=0) dbsession.add(schema) dbsession.flush() schema.attributes['myattr'] = models.Attribute(name=u'myattr', title=u'My Attribute 2', type=u'string', order=1) with pytest.raises(sqlalchemy.exc.IntegrityError): dbsession.flush()
def test_entity_force_date(dbsession): """ It should maintain a date object for date types. (Sometimes applications will blindly assign datetimes...) """ from datetime import date, datetime from occams import models schema = models.Schema(name=u'Foo', title=u'', publish_date=date(2000, 1, 1)) s1 = models.Attribute(schema=schema, name='s1', title=u'Section 1', type='section', order=0) entity = models.Entity(schema=schema) # Do simple values simpleName = 'choicesimple' schema.attributes[simpleName] = models.Attribute(schema=schema, parent_attribute=s1, title=u'', type='date', is_required=False, order=1) now = datetime.now() today = now.date() entity[simpleName] = now dbsession.flush() assert isinstance(entity[simpleName], date) assert today == entity[simpleName]
def check_report_column_type(dbsession, ds_type, sa_type): """ It should normalize datastore types to SQL types """ from datetime import date from occams import models, reporting schema = models.Schema(name=u'A', title=u'A', publish_date=date.today(), attributes={ 's1': models.Attribute(name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type=ds_type, order=1) }) }) dbsession.add(schema) dbsession.flush() report = reporting.build_report(dbsession, u'A') column_type = dbsession.query(report.c.a).column_descriptions[0]['type'] assert isinstance(column_type, sa_type), \ '%s did not covert to %s, got %s' \ % (ds_type, str(sa_type), column_type)
def test_datadict_multiple_choice(dbsession): """ It should retain answer choices in the columns dictionary """ from copy import deepcopy from datetime import date, timedelta from six import iterkeys from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute( name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type='string', is_collection=True, order=1, choices={ '001': models.Choice(name=u'001', title=u'Foo', order=0), '002': models.Choice(name=u'002', title=u'Bar', order=1) }) }) }) dbsession.add(schema1) dbsession.flush() columns = reporting.build_columns(dbsession, u'A') assert 'a' in columns assert sorted(['001', '002']) == sorted(iterkeys(columns['a'].choices)) schema2 = deepcopy(schema1) schema2.publish_date = today + timedelta(1) schema2.attributes['s1'].attributes['a'].choices['003'] = \ models.Choice(name=u'003', title=u'Baz', order=3) dbsession.add(schema2) dbsession.flush() columns = reporting.build_columns(dbsession, u'A') assert sorted(['001', '002', '003']) == \ sorted(iterkeys(columns['a'].choices))
def test_copy_schema_basic(dbsession): """ It should let the user copy schemata """ from copy import deepcopy from occams import models schema = models.Schema(name='Foo', title=u'Foo', attributes={ 'section1': models.Attribute( name=u'section1', title=u'Section 1', type='section', order=0, attributes={ 'foo': models.Attribute( name='foo', title=u'Enter Foo', type='choice', order=1, choices={ '001': models.Choice(name='001', title=u'Foo', order=0), '002': models.Choice(name='002', title=u'Bar', order=1), '003': models.Choice(name='003', title=u'Baz', order=2) }, ) }) }) dbsession.add(schema) dbsession.flush() schema_copy = deepcopy(schema) dbsession.add(schema_copy) dbsession.flush() # The ones that matter for checksums assert schema.name == schema_copy.name attribute = schema.attributes['foo'] for prop in ('name', 'title', 'description', 'type', 'is_collection', 'is_required'): attribute_copy = schema_copy.attributes['foo'] assert getattr(attribute, prop) == getattr(attribute_copy, prop) for choice in schema.attributes['foo'].choices.values(): choice_copy = schema_copy.attributes['foo'].choices[choice.name] for prop in ('name', 'title', 'order'): assert getattr(choice, prop) == getattr(choice_copy, prop)
def test_datadict_duplicate_vocabulary_term(dbsession): """ It should use the most recent version of a choice label """ from copy import deepcopy from datetime import date, timedelta from six import itervalues from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute( name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type='string', is_collection=True, order=1, choices={ '001': models.Choice(name=u'001', title=u'Foo', order=0), '002': models.Choice(name=u'002', title=u'Bar', order=1) }) }) }) schema2 = deepcopy(schema1) schema2.state = u'published' schema2.publish_date = today + timedelta(1) for choice in itervalues(schema2.attributes['s1'].attributes['a'].choices): choice.title = 'New ' + choice.title dbsession.add_all([schema1, schema2]) dbsession.flush() columns = reporting.build_columns(dbsession, u'A') assert '001' in columns['a'].choices assert '002' in columns['a'].choices assert 'New Foo' == columns['a'].choices['001'] assert 'New Bar' == columns['a'].choices['002']
def test_choice_constraint(dbsession): """ It should validate against choice constraints """ from datetime import date from occams import models from occams.exc import ConstraintError schema = models.Schema(name=u'Foo', title=u'', publish_date=date(2000, 1, 1)) s1 = models.Attribute(schema=schema, name='s1', title=u'Section 1', type='section', order=0) models.Attribute(schema=schema, parent_attribute=s1, name=u'test', title=u'', type=u'choice', is_required=False, order=0, choices={ '001': models.Choice(name=u'001', title=u'Foo', order=0), '002': models.Choice(name=u'002', title=u'Bar', order=1), '003': models.Choice(name=u'003', title=u'Baz', order=2) }) dbsession.add(schema) dbsession.flush() entity = models.Entity(schema=schema) dbsession.add(entity) entity['test'] = None entity['test'] = u'002' dbsession.flush() entry = (dbsession.query(models.ValueChoice).filter( models.ValueChoice.value.has(name=u'002')).one()) assert entry.value.name == '002' # Should not be able to set it to something outside of the specified # choice constraints with pytest.raises(ConstraintError): entity['test'] = u'999'
def test_build_report_context(dbsession): """ It should be able to associate with a context. (for easier joins) """ from datetime import date from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute(name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type='string', is_private=True, order=1) }) }) dbsession.add(schema1) dbsession.flush() entity1 = models.Entity(schema=schema1) entity1['a'] = u'002' dbsession.add(entity1) dbsession.flush() dbsession.add(models.Context(external='sometable', key=123, entity=entity1)) dbsession.flush() # not specified report = reporting.build_report(dbsession, u'A') assert 'context_key' not in report.c # specified report = reporting.build_report(dbsession, u'A', context='sometable') result = dbsession.query(report).one() assert 'context_key' in report.c assert result.context_key == 123
def test_build_report_ids(dbsession): """ It should be able to include only the schemata with the specified ids """ from copy import deepcopy from datetime import date, timedelta from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute(name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type='string', is_private=True, order=1) }) }) dbsession.add(schema1) dbsession.flush() schema2 = deepcopy(schema1) schema2.publish_date = today + timedelta(1) schema2.attributes['s1'].attributes['b'] = models.Attribute( name=u'b', title=u'', type='string', is_private=True, order=1) dbsession.add(schema2) dbsession.flush() # all report = reporting.build_report(dbsession, u'A') assert 'a' in report.c assert 'b' in report.c # Only v1 report = reporting.build_report(dbsession, u'A', ids=[schema1.id]) assert 'a' in report.c assert 'b' not in report.c
def test_build_report_ignore_private(dbsession): """ It should be able to de-identify private data upon request """ from datetime import date from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute(name=u's1', title=u'S1', type='section', order=0, attributes={ 'name': models.Attribute( name=u'name', title=u'', type='string', is_private=True, order=1) }) }) dbsession.add(schema1) dbsession.flush() # add some entries for the schema entity1 = models.Entity(schema=schema1) entity1['name'] = u'Jane Doe' dbsession.add(entity1) dbsession.flush() # not de-identified report = reporting.build_report(dbsession, u'A', ignore_private=False) result = dbsession.query(report).one() assert entity1[u'name'] == result.name # de-identified report = reporting.build_report(dbsession, u'A', ignore_private=True) result = dbsession.query(report).one() assert '[PRIVATE]' == result.name
def test_choice_defaults(dbsession): """ It should set choice defaults """ from occams import models schema = models.Schema(name=u'Foo', title=u'Foo') attribute = models.Attribute(schema=schema, name=u'foo', title=u'Enter Foo', type=u'choice', order=0) choice1 = models.Choice(attribute=attribute, name='001', title=u'Foo', order=0) choice2 = models.Choice(attribute=attribute, name='002', title=u'Bar', order=1) choice3 = models.Choice(attribute=attribute, name='003', title=u'Baz', order=2) dbsession.add_all([schema, attribute, choice1, choice2, choice3]) dbsession.flush() count = dbsession.query(models.Choice).count() assert count, 3 == 'Did not find any choices'
def test_file(self, req, dbsession): """ It should return the json rows for the codebook fragment """ from datetime import date from webob.multidict import MultiDict from occams import models from occams.exports.schema import SchemaPlan dbsession.add(models.Schema( name=u'aform', title=u'', publish_date=date.today(), attributes={ u'myfield': models.Attribute( name=u'myfield', title=u'', type=u'string', order=0 ) } )) dbsession.flush() req.GET = MultiDict([('file', 'aform')]) req.registry.settings['studies.export.plans'] = [SchemaPlan.list_all] res = self._call_fut(models.ExportFactory(req), req) assert res is not None
def test_list_not_include_private(self, dbsession): """ It should not include private data if specified. Note this is not the same as de-identification) """ from datetime import date from occams import models as datastore from occams.exports.schema import SchemaPlan schema = datastore.Schema(name=u'contact', title=u'Contact Details', publish_date=date.today(), attributes={ 'foo': datastore.Attribute(name='foo', title=u'', type='string', order=0, is_private=True) }) dbsession.add_all([schema]) dbsession.flush() plans = SchemaPlan.list_all(dbsession, include_private=True) assert len(plans) == 1 plans = SchemaPlan.list_all(dbsession, include_private=False) assert len(plans) == 0
def check_value_min_constraint(dbsession, type_, limit, below, equal, over): """ It should validate against minimum constratins """ from datetime import date from occams import models from occams.exc import ConstraintError schema = models.Schema(name=u'Foo', title=u'', publish_date=date(2000, 1, 1)) s1 = models.Attribute(schema=schema, name='s1', title=u'Section 1', type='section', order=0) entity = models.Entity(schema=schema) dbsession.add(entity) dbsession.flush() models.Attribute(schema=schema, parent_attribute=s1, name=type_, title=u'', type=type_, is_required=False, value_min=limit, order=0) with pytest.raises(ConstraintError): entity[type_] = below entity[type_] = None entity[type_] = equal entity[type_] = over models.Attribute(schema=schema, parent_attribute=s1, name=u'boolean', title=u'', type=u'boolean', value_min=10, order=1) with pytest.raises(NotImplementedError): entity['boolean'] = True
def test_valid_upload(self, req, dbsession, check_csrf_token): """ It should be able to upload a perfectly valid CSV """ import tempfile import csv from datetime import date from occams import models as datastore from occams import models schema = datastore.Schema(name='rand', title=u'Rand', publish_date=date.today(), attributes={ 'criteria': datastore.Attribute(name='criteria', title=u'Criteria', type='string', order=0) }) study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', is_randomized=True, randomization_schema=schema, consent_date=date.today()) dbsession.add_all([study]) dbsession.flush() class DummyUpload: pass with tempfile.NamedTemporaryFile(prefix='nose-', suffix='.exe') as fp: upload = DummyUpload() upload.file = fp upload.filename = fp.name # forget the schema keys writer = csv.writer(fp) writer.writerow( [u'ARM', u'STRATA', u'BLOCKID', u'RANDID', u'CRITERIA']) # noqa writer.writerow( [u'UCSD', u'hints', u'1234567', u'987654', u'is smart']) # noqa fp.flush() req.POST = {'upload': upload} self._call_fut(study, req) stratum = dbsession.query(models.Stratum).one() entity = dbsession.query(datastore.Entity).one() assert stratum.arm.name == 'UCSD' assert entity in stratum.entities assert entity['criteria'] == 'is smart'
def test_build_report_datetime(dbsession): """ It should be able to cast DATE/DATETIME """ from datetime import date from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute(name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type='date', order=1) }) }) dbsession.add(schema1) dbsession.flush() # add some entries for the schema entity1 = models.Entity(schema=schema1) entity1['a'] = date(1976, 7, 4) dbsession.add(entity1) dbsession.flush() report = reporting.build_report(dbsession, u'A') result = dbsession.query(report).one() assert str(result.a) == '1976-07-04' schema1.attributes['s1'].attributes['a'].type = 'datetime' dbsession.flush() report = reporting.build_report(dbsession, u'A') result = dbsession.query(report).one() assert str(result.a) == '1976-07-04 00:00:00'
def test_datadict_multpile_versions(dbsession): """ It should keep track of schema versions while generating column plans """ from copy import deepcopy from datetime import date, timedelta from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute(name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type='string', order=1) }) }) schema2 = deepcopy(schema1) schema2.publish_date = today + timedelta(1) schema3 = deepcopy(schema2) schema3.publish_date = today + timedelta(2) schema3.attributes['s1'].attributes['a'].title = u'prime' dbsession.add_all([schema1, schema2, schema3]) dbsession.flush() columns = reporting.build_columns(dbsession, u'A') assert 'a' in columns assert len(columns['a'].attributes) == 3
def test_entity_blob_type(dbsession): """ It should be able to keep track of file uploads (will not be storing in DB) """ from occams import models from datetime import date schema = models.Schema(name='HasBlob', title=u'', publish_date=date(2000, 1, 1)) s1 = models.Attribute(schema=schema, name='s1', title=u'Section 1', type='section', order=0) schema.attributes['theblob'] = models.Attribute(parent_attribute=s1, name=u'theblob', title=u'', type='blob', order=0) entity = models.Entity(schema=schema) dbsession.add(entity) dbsession.flush() entity_id = entity.id # Add value entity['theblob'] = models.BlobInfo(file_name=u'foo', path='bar/baz.gif') dbsession.add(entity) dbsession.flush() entity = dbsession.query(models.Entity).get(entity_id) blob = entity['theblob'] assert u'foo' == blob.file_name assert 'bar/baz.gif' == blob.path # Clear value entity['theblob'] = None dbsession.flush() entity = dbsession.query(models.Entity).get(entity_id) blob = entity['theblob'] assert blob is None
def test_build_report_attributes(dbsession): """ It should only include the specified columns (useful for large forms) """ from datetime import date from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute( name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute(name=u'a', title=u'', type='string', is_private=True, order=1), 'b': models.Attribute(name=u'b', title=u'', type='string', is_private=True, order=2) }) }) dbsession.add(schema1) dbsession.flush() report = reporting.build_report(dbsession, u'A', attributes=['b']) assert 'a' not in report.c assert 'b' in report.c
def test_build_report_scalar_values(dbsession): """ It should properly report scalar values """ from datetime import date from occams import models, reporting today = date.today() schema1 = models.Schema(name=u'A', title=u'A', publish_date=today, attributes={ 's1': models.Attribute(name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type='string', order=1) }) }) dbsession.add(schema1) dbsession.flush() # add some entries for the schema entity1 = models.Entity(schema=schema1) entity1['a'] = u'foovalue' dbsession.add(entity1) dbsession.flush() report = reporting.build_report(dbsession, u'A') result = dbsession.query(report).one() assert entity1[u'a'] == result.a
def test_validator_pattern_constraint(dbsession): """ It should validate against string pattern constraints """ from datetime import date from occams import models from occams.exc import ConstraintError schema = models.Schema(name=u'Foo', title=u'', publish_date=date(2000, 1, 1)) s1 = models.Attribute(schema=schema, name='s1', title=u'Section 1', type='section', order=0) models.Attribute( schema=schema, parent_attribute=s1, name=u'test', title=u'', type=u'string', is_required=False, # Valid US phone number pattern=r'\d{3}-\d{3}-\d{4}', order=0) dbsession.add(schema) dbsession.flush() entity = models.Entity(schema=schema) dbsession.add(entity) entity['test'] = None with pytest.raises(ConstraintError): entity['test'] = u'trollol' entity['test'] = u'123-456-7890' dbsession.flush() assert '123-456-7890' == entity['test']
def test_datadict_published_schema(dbsession): """ It should only generate a report for published schemata """ from datetime import date, timedelta from occams import models, reporting schema = models.Schema(name=u'A', title=u'A', attributes={ 's1': models.Attribute(name=u's1', title=u'S1', type='section', order=0, attributes={ 'a': models.Attribute( name=u'a', title=u'', type='string', order=0) }) }) dbsession.add(schema) dbsession.flush() columns = reporting.build_columns(dbsession, u'A') assert 'a' not in columns schema.publish_date = date.today() dbsession.flush() columns = reporting.build_columns(dbsession, u'A') assert 'a' in columns schema.retract_date = date.today() + timedelta(1) dbsession.flush() columns = reporting.build_columns(dbsession, u'A') assert 'a' not in columns
def test_incomplete_header(self, req, dbsession, check_csrf_token): """ It should include randomization schema attribute names in the header """ import tempfile import csv from datetime import date from pyramid.httpexceptions import HTTPBadRequest from occams import models as datastore from occams import models schema = datastore.Schema(name='rand', title=u'Rand', publish_date=date.today(), attributes={ 'criteria': datastore.Attribute(name='criteria', title=u'Criteria', type='string', order=0) }) study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', is_randomized=True, randomization_schema=schema, consent_date=date.today()) dbsession.add(study) dbsession.flush() class DummyUpload: pass with tempfile.NamedTemporaryFile(prefix='nose-', suffix='.exe') as fp: upload = DummyUpload() upload.file = fp upload.filename = fp.name # forget the schema keys writer = csv.writer(fp) writer.writerow(['ARM', 'STRATA', 'BLOCKID', 'RANDID']) fp.flush() with pytest.raises(HTTPBadRequest) as excinfo: req.POST = {'upload': upload} self._call_fut(study, req) assert check_csrf_token.called assert 'missing' in excinfo.value.body
def test_enrollment(self, dbsession): """ It should add enrollment-specific metadata to the report """ from datetime import date, timedelta from occams import models as datastore from occams import models from occams.exports.schema import SchemaPlan schema = datastore.Schema(name=u'termination', title=u'Termination', publish_date=date.today(), attributes={ 'foo': datastore.Attribute( name='foo', title=u'', type='string', order=0, ) }) entity = datastore.Entity(schema=schema, collect_date=date.today()) patient = models.Patient(site=models.Site(name='ucsd', title=u'UCSD'), pid=u'12345', entities=[entity]) study = models.Study(name=u'cooties', short_title=u'CTY', code=u'999', consent_date=date.today() - timedelta(365), title=u'Cooties') enrollment = models.Enrollment( patient=patient, study=study, consent_date=date.today() - timedelta(5), latest_consent_date=date.today() - timedelta(3), termination_date=date.today(), entities=[entity]) dbsession.add_all([schema, entity, patient, study, enrollment]) plan = SchemaPlan.from_schema(dbsession, schema.name) codebook = list(plan.codebook()) query = plan.data() codebook_columns = [c['field'] for c in codebook] data_columns = [c['name'] for c in query.column_descriptions] record = query.one() assert sorted(codebook_columns) == sorted(data_columns) assert record.site == patient.site.name assert record.pid == patient.pid assert record.enrollment == enrollment.study.name assert record.enrollment_ids == str(enrollment.id) assert record.visit_cycles is None assert record.collect_date == entity.collect_date
def test_validator_max_constraint(dbsession): """ It should validate string/number value min/max """ from datetime import date from occams import models from occams.exc import ConstraintError schema = models.Schema(name=u'Foo', title=u'', publish_date=date(2000, 1, 1)) s1 = models.Attribute(schema=schema, name='s1', title=u'Section 1', type='section', order=0) models.Attribute(schema=schema, parent_attribute=s1, name=u'test', title=u'', type=u'string', is_required=False, value_max=3, order=0) dbsession.add(schema) dbsession.flush() entity = models.Entity(schema=schema) dbsession.add(entity) entity['test'] = None with pytest.raises(ConstraintError): entity['test'] = u'foobar' entity['test'] = u'foo' dbsession.flush() assert 'foo' == entity['test']
def test_schema_attribute(dbsession): """ It should implement full schema/attribute/subattribute hierarchies """ from datetime import date from occams import models schema = models.Schema(name=u'aform', title=u'A Form', publish_date=date.today(), attributes={ 'section1': models.Attribute(name=u'section1', title=u'Section 1', type='section', order=0, attributes={ 'foo': models.Attribute( name=u'foo', title=u'Foo', type=u'choice', order=0, choices={ '001': models.Choice( name=u'001', title=u'Green', order=0) }) }) }) dbsession.add(schema) dbsession.flush() assert 'section1' in schema.attributes # Works both ways assert 'foo' in schema.attributes assert 'foo' in schema.attributes['section1'].attributes
def test_attribute_valid_regexp_name(dbsession, name): """ It should vallow valid names (See RE_VALID_NAME) """ from datetime import date from occams import models schema = models.Schema(name='SomeForm', title=u'Foo', publish_date=date(2014, 3, 31)) schema.attributes[name] = models.Attribute(name=name, title=u'My Attribute', type=u'string', order=1)
def test_attribute_defaults(dbsession): """ It should set attribute defaults """ from occams import models schema = models.Schema(name=u'Foo', title=u'Foo') attribute = models.Attribute(schema=schema, name=u'foo', title=u'Enter Foo', type=u'string', order=0) dbsession.add(attribute) dbsession.flush() count = dbsession.query(models.Attribute).count() assert count, 1 == 'Found more than one entry'
def test_attributea_invalid_reserved_name(dbsession): """ It should prevent reserved words as attribute names """ from datetime import date from occams import models schema = models.Schema(name='SomeForm', title=u'Foo', publish_date=date(2014, 3, 31)) dbsession.add(schema) dbsession.flush() with pytest.raises(ValueError): schema.attributes['while'] = models.Attribute(name=u'while', title=u'My Attribute', type=u'string', order=1)