def test_reference_unique(self, req, db_session, check_csrf_token): """ It should enforce unique reference_types """ from pyramid.httpexceptions import HTTPBadRequest from occams_studies import models site_la = models.Site(name=u'la', title=u'LA') reftype = models.ReferenceType(name=u'foo', title=u'Foo') other = models.Patient(site=site_la, pid=u'ABCDE', references=[ models.PatientReference( reference_type=reftype, reference_number=u'XYZ') ]) patient = models.Patient(site=site_la, pid=u'12345') db_session.add_all([patient, other]) db_session.flush() req.json_body = { 'site': site_la.id, 'references': [{ 'reference_type': reftype.id, 'reference_number': u'XYZ' }] } with pytest.raises(HTTPBadRequest) as excinfo: self._call_fut(patient, req) assert check_csrf_token.called assert 'Already assigned' in \ excinfo.value.json['errors']['references-0-reference_number']
def test_by_enrollment_number(self, req, db_session): """ It should be able to search by Enrollment Number """ from datetime import date from occams_studies import models from webob.multidict import MultiDict study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today()) site_la = models.Site(name=u'la', title=u'LA') patient = models.Patient(site=site_la, pid=u'12345', enrollments=[ models.Enrollment( study=study, reference_number=u'xyz', consent_date=date.today()) ]) db_session.add_all([site_la, patient]) db_session.flush() req.GET = MultiDict([('query', u'xyz')]) res = self._call_fut(models.PatientFactory(req), req) assert patient.pid == res['patients'][0]['pid']
def test_data_with_early_test(self, db_session, study_code): """ It should output earlytest ids (for backwards-compatibilty) """ from datetime import date from occams_studies import models plan = self._create_one(db_session) patient = models.Patient( pid=u'xxx-xxx', site=models.Site(name=u'someplace', title=u'Some Place'), enrollments=[ models.Enrollment(consent_date=date.today(), reference_number=u'76C000000', study=models.Study(name=u'some_study', code=study_code, consent_date=date.today(), short_title=u'smstdy', title=u'Some Study')) ]) db_session.add(patient) query = plan.data() data = query.one()._asdict() assert data['early_id'] == patient.enrollments[0].reference_number
def test_has_enrollments(self, req, db_session, config, check_csrf_token): """ It should not allow deletion of a study if it has enrollments (unless administrator) """ from datetime import date from pyramid.httpexceptions import HTTPForbidden from occams_studies import models study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today()) enrollment = models.Enrollment(study=study, consent_date=date.today(), patient=models.Patient(site=models.Site( name='ucsd', title=u'UCSD'), pid=u'12345')) db_session.add_all([study, enrollment]) db_session.flush() # Should not be able to delete if not an admin config.testing_securitypolicy(permissive=False) with pytest.raises(HTTPForbidden): self._call_fut(study, req) config.testing_securitypolicy(permissive=True) self._call_fut(study, req) assert 0 == db_session.query(models.Study).count()
def test_data_without_refs(self, db_session): """ It should be able to generate reports without refs """ from occams_studies import models plan = self._create_one(db_session) patient = models.Patient(pid=u'xxx-xxx', site=models.Site(name=u'someplace', title=u'Some Place')) db_session.add(patient) 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] assert sorted(codebook_columns) == sorted(data_columns) data = query.one()._asdict() assert data['pid'] == patient.pid assert data['site'] == patient.site.name assert data['early_id'] is None
def populate(self, app, db_session): import transaction from occams_studies import models as studies from occams_datastore import models as datastore from datetime import date # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) db_session.flush() site = studies.Site(name=u'UCSD', title=u'UCSD', description=u'UCSD Campus', create_date=date.today()) patient = studies.Patient(initials=u'ian', nurse=u'*****@*****.**', site=site, pid=u'123') study = studies.Study( name=u'test_study', code=u'test_code', consent_date=date(2014, 12, 23), is_randomized=False, title=u'test_title', short_title=u'test_short', ) db_session.add(study) db_session.add(patient)
def test_update_patient(self, req, db_session, check_csrf_token): """ It should also mark the patient as modified """ from datetime import date from occams_studies import models study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today()) cycle1 = models.Cycle(study=study, name='week-1', title=u'', week=1) patient = models.Patient(site=models.Site(name=u'ucsd', title=u'UCSD'), pid=u'12345') db_session.add_all([patient, study]) db_session.flush() old_modify_date = patient.modify_date req.json_body = { 'cycles': [str(cycle1.id)], 'visit_date': str(date.today()) } self._call_fut(patient['visits'], req) assert old_modify_date < patient.modify_date
def test_data_with_refs(self, db_session): """ It should generate a basic listing of all the PIDs in the database """ from occams_studies import models plan = self._create_one(db_session) reference_type = models.ReferenceType(name=u'med_num', title=u'Medical Number') patient = models.Patient(pid=u'xxx-xxx', references=[ models.PatientReference( reference_type=reference_type, reference_number=u'999') ], site=models.Site(name=u'someplace', title=u'Some Place')) db_session.add(patient) 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] assert sorted(codebook_columns) == sorted(data_columns) data = query.one()._asdict() assert data['med_num'] == '999'
def test_by_ids( self, req, db_session, ): """ It should allow search via direct ids (for pre-entered values) """ from datetime import date from occams_studies import models from webob.multidict import MultiDict study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today()) cycle1 = models.Cycle(name='week-1', title=u'Foo Week 1', week=1) cycle2 = models.Cycle(name='week-2', title=u'Bar Week 2', week=2) patient = models.Patient(site=models.Site(name=u'ucsd', title=u'UCSD'), pid=u'12345') study.cycles.append(cycle1) study.cycles.append(cycle2) db_session.add_all([patient, study]) db_session.flush() req.GET = MultiDict([('ids', cycle1.id)]) res = self._call_fut(patient['visits'], req) assert cycle1.id == res['cycles'][0]['id']
def test_add_to_patient(self, req, db_session): from datetime import date from occams_datastore import models as datastore from occams_studies import models schema = datastore.Schema(name=u'schema', title=u'Schema', publish_date=date.today()) study = models.Study(name='some-study', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today(), schemata=set([schema])) site = models.Site(name=u'somewhere', title=u'Somewhere') patient = models.Patient(pid=u'12345', site=site) db_session.add_all([study, patient]) db_session.flush() req.method = 'POST' req.matchdict = {'patient': patient} req.json_body = { 'schema': schema.id, 'collect_date': str(date.today()), } factory = models.FormFactory(req, patient) self._call_fut(factory, req) contexts = db_session.query(datastore.Context).all() assert len(contexts) == 1 assert contexts[0].entity.schema == schema
def test_reference_valid_number(self, req, db_session, check_csrf_token): """ It should check reference patterns if they are supported by the type """ from pyramid.httpexceptions import HTTPBadRequest from occams_studies import models site_la = models.Site(name=u'la', title=u'LA') reftype = models.ReferenceType(name=u'foo', title=u'Foo', reference_pattern=u'^[0-9]+$') patient = models.Patient(site=site_la, pid=u'12345') db_session.add_all([patient, reftype]) db_session.flush() req.json_body = { 'site': site_la.id, 'references': [{ 'reference_type': reftype.id, 'reference_number': u'XYZ' }] } with pytest.raises(HTTPBadRequest) as excinfo: self._call_fut(patient, req) assert check_csrf_token.called assert 'Invalid format' in \ excinfo.value.json['errors']['references-0-reference_number']
def test_track_limit(self, req, db_session): """ It should only keep track of the last 10 recently viewed patients """ from collections import OrderedDict from datetime import datetime import mock from occams_studies import models site_la = models.Site(name=u'la', title=u'LA') patient = models.Patient(site=site_la, pid=u'12345') db_session.add(patient) db_session.flush() req.session['viewed'] = OrderedDict() req.session.changed = mock.Mock() previous = [str(i) for i in range(10)] for pid in previous: req.session['viewed'][pid] = \ {'pid': pid, 'view_date': datetime.now()} self._call_fut(patient, req) assert '12345' in req.session['viewed'] assert previous[0] not in req.session['viewed'] assert 10 == len(req.session['viewed'])
def test_reference_type_invalid(self, req, db_session, check_csrf_token): """ It should enforce valid reference_types """ from pyramid.httpexceptions import HTTPBadRequest from occams_studies import models site_la = models.Site(name=u'la', title=u'LA') patient = models.Patient(site=site_la, pid=u'12345') db_session.add(patient) db_session.flush() req.json_body = { 'site': patient.site.id, 'references': [{ 'reference_type': 123, 'reference_number': u'ABC' }] } with pytest.raises(HTTPBadRequest) as excinfo: self._call_fut(patient, req) assert check_csrf_token.called assert 'not found' in \ excinfo.value.json['errors']['references-0-reference_type'].lower()
def test_success(self, req, db_session, check_csrf_token): """ It should allow removal of entities from a visit. """ from datetime import date, timedelta from pyramid.httpexceptions import HTTPOk from occams_datastore import models as datastore from occams_studies import models cycle = models.Cycle(name='week-1', title=u'', week=1) schema = datastore.Schema(name=u'sample', title=u'', publish_date=date.today()) study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today(), cycles=[cycle], schemata=set([schema])) site = models.Site(name=u'ucsd', title=u'UCSD') default_state = (db_session.query( datastore.State).filter_by(name=u'pending-entry').one()) t_a = date.today() + timedelta(days=5) patient_a = models.Patient(site=site, pid=u'12345') visit_a = models.Visit(patient=patient_a, cycles=[cycle], visit_date=t_a) entity_a_1 = datastore.Entity(schema=schema, collect_date=t_a, state=default_state) entity_a_2 = datastore.Entity(schema=schema, collect_date=t_a, state=default_state) entity_a_3 = datastore.Entity(schema=schema, collect_date=t_a, state=default_state) list(map(visit_a.entities.add, [entity_a_1, entity_a_2, entity_a_3])) db_session.add_all([visit_a, study]) db_session.flush() req.json_body = {'forms': [entity_a_2.id, entity_a_3.id]} res = self._call_fut(visit_a['forms'], req) # refresh the session so we can get a correct listing db_session.expunge_all() visit_a = db_session.query(models.Visit).get(visit_a.id) assert isinstance(res, HTTPOk) assert sorted([e.id for e in [entity_a_1]]) == \ sorted([e.id for e in visit_a.entities])
def populate(self, app, db_session): import transaction from occams_studies import models as studies from occams_datastore import models as datastore from datetime import date # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) db_session.flush() site = studies.Site(name=u'UCSD', title=u'UCSD', description=u'UCSD Campus', create_date=date.today()) patient = studies.Patient(initials=u'ian', nurse=u'*****@*****.**', site=site, pid=u'123') form = datastore.Schema(name=u'test_schema', title=u'test_title', publish_date=date(2015, 1, 1)) study = studies.Study(name=u'test_study', code=u'test_code', consent_date=date(2014, 12, 23), is_randomized=False, title=u'test_title', short_title=u'test_short', schemata=set([form])) cycle = studies.Cycle(name=u'TestCycle', title=u'TestCycle', week=39, study=study) visit = studies.Visit(patient=patient, cycles=[cycle], visit_date='2015-01-01') entity = datastore.Entity(schema=form, collect_date=date(2015, 1, 1)) db_session.add(study) db_session.add(patient) db_session.add(visit) db_session.add(entity) patient.entities.add(entity)
def test_enrollment(self, db_session): """ It should add enrollment-specific metadata to the report """ from datetime import date, timedelta from occams_datastore import models as datastore from occams_studies import models from occams_studies.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]) db_session.add_all([schema, entity, patient, study, enrollment]) plan = SchemaPlan.from_schema(db_session, 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_by_pid(self, req, db_session): """ It should search by PID """ from occams_studies import models from webob.multidict import MultiDict site_la = models.Site(name=u'la', title=u'LA') patient = models.Patient(site=site_la, pid=u'12345') db_session.add_all([site_la, patient]) db_session.flush() req.GET = MultiDict([('query', u'12345')]) res = self._call_fut(models.PatientFactory(req), req) assert patient.pid == res['patients'][0]['pid']
def populate(self, app, db_session): import transaction from occams_studies import models as studies from occams_datastore import models as datastore from datetime import date # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) db_session.flush() site = studies.Site(name=u'UCSD', title=u'UCSD', description=u'UCSD Campus', create_date=date.today()) patient = studies.Patient(initials=u'ian', nurse=u'*****@*****.**', site=site, pid=u'123') form = datastore.Schema(name=u'test_schema', title=u'test_title', publish_date=date(2015, 1, 1)) study = studies.Study(name=u'test_study', code=u'test_code', consent_date=date(2014, 12, 23), is_randomized=False, title=u'test_title', short_title=u'test_short', schemata=set([form])) state = (db_session.query( datastore.State).filter_by(name=u'pending-entry').one()) db_session.add( datastore.Entity(state=state, schema=form, collect_date=date(2015, 2, 1))) db_session.add( studies.Enrollment(patient=patient, study=study, consent_date=date(2014, 12, 22)))
def test_site(self, req, db_session, check_csrf_token): """ It should update sites """ from occams_studies import models site_la = models.Site(name=u'la', title=u'LA') site_sd = models.Site(name=u'sd', title=u'SD') patient = models.Patient(site=site_la, pid=u'12345') db_session.add_all([site_la, site_sd, patient]) db_session.flush() req.json_body = {'site': site_sd.id} self._call_fut(patient, req) assert check_csrf_token.called assert patient.site.id == site_sd.id
def test_cascade_forms(self, req, db_session, check_csrf_token): """ It should remove all visit-associated forms. """ from datetime import date from occams_datastore import models as datastore from occams_studies import models schema = datastore.Schema(name=u'sample', title=u'Some Sample', publish_date=date.today()) study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today()) cycle = models.Cycle(name=u'week-10', title=u'Week 10', week=10) study.cycles.append(cycle) patient = models.Patient(site=models.Site(name=u'ucsd', title=u'UCSD'), pid=u'12345') enrollment = models.Enrollment(study=study, patient=patient, consent_date=date.today()) visit = models.Visit(patient=patient, cycles=[cycle], visit_date=date.today()) visit.entities.add( datastore.Entity(schema=schema, collect_date=date.today())) db_session.add_all([patient, enrollment, study, visit]) db_session.flush() visit_id = visit.id self._call_fut(visit, req) assert db_session.query(models.Visit).get(visit_id) is None assert 0 == db_session.query(datastore.Entity).count()
def test_include_not_retracted_form(self, req, db_session, check_csrf_token): """ It should not use retracted forms, even if there are the most recent """ from datetime import date, timedelta from occams_datastore import models as datastore from occams_studies import models t0 = date.today() t1 = t0 + timedelta(days=1) t2 = t1 + timedelta(days=1) study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today()) cycle1 = models.Cycle(name='week-1', title=u'', week=1) cycle1.schemata.update([ datastore.Schema(name='form1', title=u'', publish_date=t0), datastore.Schema(name='form1', title=u'', publish_date=t2, retract_date=t2) ]) study.cycles.append(cycle1) patient = models.Patient(site=models.Site(name=u'ucsd', title=u'UCSD'), pid=u'12345') db_session.add_all([patient, study]) db_session.flush() req.json_body = { 'cycles': [cycle1.id], 'visit_date': str(t2), 'include_forms': True } res = self._call_fut(patient['visits'], req) assert 1 == len(res['entities']) assert str(t0) == res['entities'][0]['schema']['publish_date']
def test_unique_cycle(self, req, db_session, check_csrf_token): """ It should not allow repeat cycles (unless it's interim) """ from datetime import date, timedelta from pyramid.httpexceptions import HTTPBadRequest from occams_studies import models study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today()) cycle = models.Cycle(name='week-1', title=u'', week=1) study.cycles.append(cycle) patient = models.Patient(site=models.Site(name=u'ucsd', title=u'UCSD'), pid=u'12345') visit = models.Visit(patient=patient, cycles=[cycle], visit_date=date.today()) db_session.add_all([patient, study, visit]) db_session.flush() req.json_body = { 'cycles': [cycle.id], 'visit_date': str(date.today() + timedelta(days=1)) } with pytest.raises(HTTPBadRequest) as excinfo: self._call_fut(patient['visits'], req) assert 'already in use' in \ excinfo.value.json['errors']['cycles-0'].lower() # The exception is interims cycle.is_interim = True db_session.flush() res = self._call_fut(patient['visits'], req) assert res is not None
def test_track_recently_viewed(self, req, db_session): """ It should track recently viewed patients """ import mock from occams_studies import models site_la = models.Site(name=u'la', title=u'LA') patient = models.Patient(site=site_la, pid=u'12345') db_session.add(patient) db_session.flush() req.session.changed = mock.Mock() self._call_fut(patient, req) assert '12345' in req.session['viewed'] assert 1 == len(req.session['viewed']) assert req.session.changed.called
def test_site_invalid(self, req, db_session, check_csrf_token): """ It should enforce valid sites """ from pyramid.httpexceptions import HTTPBadRequest from occams_studies import models site_la = models.Site(name=u'la', title=u'LA') patient = models.Patient(site=site_la, pid=u'12345') db_session.add_all([site_la, patient]) db_session.flush() req.json_body = {'site': site_la.id + 100} with pytest.raises(HTTPBadRequest) as excinfo: self._call_fut(patient, req) assert check_csrf_token.called assert 'not found' in excinfo.value.json['errors']['site'].lower()
def test_update_patient(self, req, db_session, check_csrf_token): """ It should also mark the patient as modified """ from datetime import date from occams_studies import models patient = models.Patient(site=models.Site(name=u'ucsd', title=u'UCSD'), pid=u'12345') visit = models.Visit(patient=patient, visit_date=date.today()) db_session.add_all([patient, visit]) db_session.flush() old_modify_date = patient.modify_date self._call_fut(visit, req) assert old_modify_date < patient.modify_date
def test_unique_visit_date(self, req, db_session, check_csrf_token): """ It should not allow duplicate visit dates """ from datetime import date from pyramid.httpexceptions import HTTPBadRequest from occams_studies import models study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today()) cycle1 = models.Cycle(name='week-1', title=u'', week=1) cycle2 = models.Cycle(name='week-2', title=u'', week=2) study.cycles.append(cycle1) study.cycles.append(cycle2) patient = models.Patient(site=models.Site(name=u'ucsd', title=u'UCSD'), pid=u'12345') visit = models.Visit(patient=patient, cycles=[cycle1], visit_date=date.today()) db_session.add_all([patient, study, visit]) db_session.flush() req.json_body = { 'cycles': [cycle2.id], 'visit_date': str(date.today()) } # Update the visit, should allow to update the date self._call_fut(visit, req) # New visits cannot share dates with pytest.raises(HTTPBadRequest) as excinfo: self._call_fut(patient['visits'], req) assert 'already exists' in \ excinfo.value.json['errors']['visit_date']
def test_add_drsc_entity_already_has_entity(self, db_session): """Should not add entity becuase it exists already.""" from datetime import date from occams_studies import models as studies from occams_datastore import models as datastore patient_site = studies.Site(name=u'test_site', title=u'test_clinic') patient = studies.Patient(pid=u'1234', site=patient_site) default_state = (db_session.query( datastore.State).filter_by(name='complete').one()) target_schema = datastore.Schema(name=u'Demographics', title=u'Demographics', publish_date=date.today(), attributes={ 'yob': datastore.Attribute( name=u'yob', title=u'yob', type=u'number', order=0) }) collect_date = date.today() entity = datastore.Entity(schema=target_schema, collect_date=collect_date, state=default_state) patient.entities.add(entity) db_session.flush() target_schema_name = u'Demographics' self._call_fut(db_session, patient, target_schema_name, target_schema, collect_date) patient = (db_session.query( studies.Patient).filter_by(pid=u'1234').one()) assert len(patient.entities) == 1
def test_not_in_study(self, req, db_session): """ It should fail if the form is not part of the study """ from datetime import date, timedelta from pyramid.httpexceptions import HTTPBadRequest from occams_datastore import models as datastore from occams_studies import models cycle = models.Cycle(name='week-1', title=u'', week=1) schema = datastore.Schema(name=u'sample', title=u'', publish_date=date.today()) study = models.Study(name=u'somestudy', title=u'Some Study', short_title=u'sstudy', code=u'000', consent_date=date.today(), cycles=[cycle]) site = models.Site(name=u'ucsd', title=u'UCSD') t_a = date.today() + timedelta(days=5) patient_a = models.Patient(site=site, pid=u'12345') visit_a = models.Visit(patient=patient_a, cycles=[cycle], visit_date=t_a) db_session.add_all([schema, visit_a, study]) db_session.flush() req.json_body = { 'schema': schema.id, } with pytest.raises(HTTPBadRequest) as excinfo: self._call_fut(visit_a['forms'], req) assert 'is not part of the studies' in \ excinfo.value.json['errors']['schema']
def test_delete(self, req, db_session, check_csrf_token): """ It should allow a valid principal to delete a patient """ from collections import OrderedDict import mock from occams_studies import models site_la = models.Site(name=u'la', title=u'LA') patient = models.Patient(site=site_la, pid=u'12345') db_session.add(patient) db_session.flush() patient_id = patient.id req.session['viewed'] = OrderedDict([('12345', {})]) req.session.changed = mock.Mock() self._call_fut(patient, req) assert db_session.query(models.Patient).get(patient_id) is None assert u'12345' not in req.session['viewed']
def test_call_fail( self, req, db_session, ): """ It should return an validation error string if the cycles are invalid """ from webob.multidict import MultiDict from occams_studies import models patient = models.Patient(site=models.Site(name=u'ucsd', title=u'UCSD'), pid=u'12345') db_session.add_all([patient]) db_session.flush() req.GET = MultiDict([('cycles', '123')]) res = self._call_fut(patient['visits'], req) assert 'not found' in res.lower()