def validate_populate_imports(request, records): """ Return list of errors, imports and forms :request: request obj :records: a list of csv row data :return: errors, imports, and forms for template rendering """ errors, imports, forms = ([], [], []) for record in records: schema_dict = { 'name': record['schema_name'], 'title': record['schema_title'], 'publish_date': record['publish_date'].strftime('%Y-%m-%d') } FormForm = FormFormFactory(context=None, request=request) form_form = FormForm.from_json(schema_dict) if not form_form.validate(): schema_error = {} schema_error['errors'] = wtferrors(form_form) schema_error['schema_name'] = schema_dict['name'] schema_error['schema_title'] = schema_dict['title'] schema_error['name'] = 'N/A' schema_error['title'] = 'N/A' errors.append(schema_error) else: schema = datastore.Schema.from_json(schema_dict) if schema.to_json() not in forms: forms.append(schema.to_json()) choices = parse.get_choices(record['choices']) # below needed because we are calling from_json on record record['choices'] = choices FieldForm = FieldFormFactory(context=schema, request=request) form = FieldForm.from_json(record) if not form.validate(): output = log_errors(wtferrors(form), record) errors.append(output) else: imports.append((datastore.Attribute( name=record['name'], title=record['title'], description=record['description'], is_required=record['is_required'], is_collection=record['is_collection'], is_private=record['is_private'], type=record['type'], order=record['order'], choices=choices ), schema)) return errors, imports, forms
def edit_json(context, request): """ Add/Edit form for fields. """ check_csrf_token(request) db_session = request.db_session form = FieldFormFactory(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) is_new = isinstance(context, models.AttributeFactory) if not is_new: attribute = context else: # Add the attribute and temporarily set to large display order attribute = datastore.Attribute(schema=context.__parent__, order=-1) db_session.add(attribute) attribute.apply(form.data) if is_new: # now we can move the attribute move_json(attribute, request) db_session.flush() return view_json(attribute, request)
def edit_json(context, request): """ Adds/Edits a external service record. If the operation was successful, a redirect to the new record details will be returns. Otherwise a json record of validation errors will be returned. """ check_csrf_token(request) db_session = request.db_session form = ExternalServiceForm(context, request).from_json(request.json_body) if not form.validate(): return HTTPBadRequest(json={'errors': wtferrors(form)}) if isinstance(context, models.ExternalServiceFactory): study = context.__parent__ service = models.ExternalService(study=study) else: study = context.study service = context service.name = slugify(form.title.data) service.title = form.title.data service.description = form.description.data service.url_template = form.url_template.data db_session.flush() success_url = request.route_path('studies.external_service', study=study.name, service=service.name) return HTTPSeeOther(location=success_url)
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session form = EnrollmentSchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if isinstance(context, models.EnrollmentFactory): enrollment = models.Enrollment( patient=context.__parent__, study=form.study.data) else: enrollment = context enrollment.patient.modify_date = datetime.now() enrollment.consent_date = form.consent_date.data enrollment.latest_consent_date = form.latest_consent_date.data enrollment.reference_number = form.reference_number.data if not form.study.data.termination_schema: enrollment.termination_date = form.termination_date.data db_session.flush() return view_json(enrollment, request)
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session form = StudySchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if isinstance(context, models.StudyFactory): study = models.Study() db_session.add(study) else: study = context study.name = slugify(form.title.data) study.title = form.title.data study.code = form.code.data study.short_title = form.short_title.data study.consent_date = form.consent_date.data study.termination_schema = form.termination_form.data study.is_randomized = form.is_randomized.data study.is_blinded = \ None if not study.is_randomized else form.is_blinded.data study.randomization_schema = \ None if not study.is_randomized else form.randomization_form.data db_session.flush() return view_json(study, request)
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session form = EnrollmentSchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if isinstance(context, models.EnrollmentFactory): enrollment = models.Enrollment(patient=context.__parent__, study=form.study.data) else: enrollment = context enrollment.patient.modify_date = datetime.now() enrollment.consent_date = form.consent_date.data enrollment.latest_consent_date = form.latest_consent_date.data enrollment.reference_number = form.reference_number.data if not form.study.data.termination_schema: enrollment.termination_date = form.termination_date.data db_session.flush() return view_json(enrollment, request)
def bulk_delete_json(context, request): """ Deletes forms in bulk """ check_csrf_token(request) db_session = request.db_session class DeleteForm(Form): forms = wtforms.FieldList( ModelField(db_session=db_session, class_=datastore.Entity), validators=[wtforms.validators.DataRequired()]) form = DeleteForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) entity_ids = [entity.id for entity in form.forms.data] external = context.__parent__.__tablename__ key = context.__parent__.id (db_session.query(datastore.Entity).filter( datastore.Entity.id.in_( db_session.query(datastore.Context.entity_id).filter( datastore.Context.entity_id.in_(entity_ids)).filter( datastore.Context.external == external).filter( datastore.Context.key == key))).delete('fetch')) db_session.flush() return HTTPOk()
def edit_schedule_json(context, request): """ Enables/Disables a form for a cycle Request body json parameters: schema -- name of the schema (will used study-enabled versions) cycle -- cycle id enabled -- true/false """ check_csrf_token(request) db_session = request.db_session def check_cycle_association(form, field): if field.data.study != context: raise wtforms.ValidationError( request.localizer.translate(_(u'Not a valid choice'))) def check_form_association(form, field): query = (db_session.query(datastore.Schema).join( models.study_schema_table).filter( datastore.Schema.name == field.data).filter( models.study_schema_table.c.study_id == context.id)) (exists, ) = db_session.query(query.exists()).one() if not exists: raise wtforms.ValidationError( request.localizer.translate(_(u'Not a valid choice'))) class ScheduleForm(Form): schema = wtforms.StringField(validators=[ wtforms.validators.InputRequired(), check_form_association ]) cycle = ModelField(db_session=db_session, class_=models.Cycle, validators=[ wtforms.validators.InputRequired(), check_cycle_association ]) enabled = wtforms.BooleanField() form = ScheduleForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) schema_name = form.schema.data cycle = form.cycle.data enabled = form.enabled.data study_items = set(i for i in context.schemata if i.name == schema_name) cycle_items = set(i for i in cycle.schemata if i.name == schema_name) if enabled: # Match cycle schemata to the study's schemata for the given name cycle.schemata.difference_update(cycle_items - study_items) cycle.schemata.update(study_items) else: cycle.schemata.difference_update(study_items | cycle_items) return HTTPOk()
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session is_new = isinstance(context, models.PatientFactory) form = PatientSchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if is_new: # if any errors occurr after this, this PID is essentially wasted patient = models.Patient( pid=six.text_type(generate(db_session, form.site.data.name))) db_session.add(patient) else: patient = context patient.site = form.site.data if form.references.data: inputs = dict( ((r['reference_type'].id, r['reference_number']), r) for r in form.references.data) for r in patient.references: try: # Remove already-existing values from the inputs del inputs[(r.reference_type.id, r.reference_number)] except KeyError: # References not in the inputs indicate they have been removed db_session.delete(r) for r in six.itervalues(inputs): db_session.add(models.PatientReference( patient=patient, reference_type=r['reference_type'], reference_number=r['reference_number'])) # Add the patient forms if is_new: schemata_query = ( db_session.query(datastore.Schema) .join(models.patient_schema_table)) pending_entry = ( db_session.query(datastore.State) .filter_by(name=u'pending-entry') .one()) for schema in schemata_query: patient.entities.add(datastore.Entity( schema=schema, state=pending_entry )) db_session.flush() db_session.refresh(patient) return view_json(patient, request)
def move_json(context, request): """ Moves the field to the target section and display order within the form """ check_csrf_token(request) db_session = request.db_session schema = context.schema def not_self(form, field): if field.data == context.name: raise wtforms.ValidationError(_(u'Cannot move value into itself')) def not_section(form, field): if (context.type == 'section' and schema.attributes[field.data].type == 'section'): raise wtforms.ValidationError( _(u'Nested sections are not supported')) class MoveForm(Form): target = wtforms.StringField( validators=[ wtforms.validators.Optional(), wtforms.validators.AnyOf( schema.attributes, message=_(u'Does not exist')), not_self, not_section]) index = wtforms.IntegerField( validators=[wtforms.validators.NumberRange(min=0)]) form = MoveForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if form.target.data: section = target = schema.attributes[form.target.data] else: target = schema section = None attributes = [a for a in target.itertraverse() if a != context] context.parent_attribute = section attributes.insert(form.index.data, context) # Apply new display orders before re-sorting the entire list for i, a in enumerate(attributes): a.order = i # We need to resort the fields to avoid ordering collisions for i, a in enumerate(schema.iterlist()): a.order = i db_session.flush() return HTTPOk()
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session is_new = isinstance(context, models.ReferenceTypeFactory) def check_unique(form, field): query = ( db_session.query(models.ReferenceType) .filter_by(name=field.data)) if not is_new: query = query.filter(models.ReferenceType.id != context.id) exists = ( db_session.query(sa.literal(True)).filter(query.exists()).scalar()) if exists: raise wtforms.ValidationError(request.localizer.translate( _(u'Already exists'))) class ReferenceTypeForm(Form): name = wtforms.StringField( validators=[ wtforms.validators.InputRequired(), check_unique]) title = wtforms.StringField( validators=[ wtforms.validators.InputRequired()]) description = wtforms.TextAreaField( validators=[ wtforms.validators.Optional()]) reference_pattern = wtforms.StringField( validators=[ wtforms.validators.Optional()]) reference_hint = wtforms.StringField( validators=[ wtforms.validators.Optional()]) form = ReferenceTypeForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if is_new: reference_type = models.ReferenceType() db_session.add(reference_type) else: reference_type = context form.populate_obj(reference_type) db_session.flush() return view_json(reference_type, request)
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session is_new = isinstance(context, models.PatientFactory) form = PatientSchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if is_new: # if any errors occurr after this, this PID is essentially wasted patient = models.Patient( pid=six.text_type(generate(db_session, form.site.data.name))) db_session.add(patient) else: patient = context patient.site = form.site.data if form.references.data: inputs = dict(((r['reference_type'].id, r['reference_number']), r) for r in form.references.data) for r in patient.references: try: # Remove already-existing values from the inputs del inputs[(r.reference_type.id, r.reference_number)] except KeyError: # References not in the inputs indicate they have been removed db_session.delete(r) for r in six.itervalues(inputs): db_session.add( models.PatientReference( patient=patient, reference_type=r['reference_type'], reference_number=r['reference_number'])) # Add the patient forms if is_new: schemata_query = (db_session.query(datastore.Schema).join( models.patient_schema_table)) pending_entry = (db_session.query( datastore.State).filter_by(name=u'pending-entry').one()) for schema in schemata_query: patient.entities.add( datastore.Entity(schema=schema, state=pending_entry)) db_session.flush() db_session.refresh(patient) return view_json(patient, request)
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session is_new = isinstance(context, models.ReferenceTypeFactory) def check_unique(form, field): query = (db_session.query( models.ReferenceType).filter_by(name=field.data)) if not is_new: query = query.filter(models.ReferenceType.id != context.id) exists = (db_session.query(sa.literal(True)).filter( query.exists()).scalar()) if exists: raise wtforms.ValidationError( request.localizer.translate(_(u'Already exists'))) class ReferenceTypeForm(Form): name = wtforms.StringField( validators=[wtforms.validators.InputRequired(), check_unique]) title = wtforms.StringField( validators=[wtforms.validators.InputRequired()]) description = wtforms.TextAreaField( validators=[wtforms.validators.Optional()]) reference_pattern = wtforms.StringField( validators=[wtforms.validators.Optional()]) reference_hint = wtforms.StringField( validators=[wtforms.validators.Optional()]) form = ReferenceTypeForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if is_new: reference_type = models.ReferenceType() db_session.add(reference_type) else: reference_type = context form.populate_obj(reference_type) db_session.flush() return view_json(reference_type, request)
def edit_json(context, request): db_session = request.db_session check_csrf_token(request) form = SiteSchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json=wtferrors(form)) if isinstance(context, models.Site): site = context else: site = models.Site() db_session.add(site) site.name = form.name.data site.title = form.title.data db_session.flush() return view_json(site, request)
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session form = CycleSchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if isinstance(context, models.CycleFactory): cycle = models.Cycle(study=context.__parent__) db_session.add(cycle) else: cycle = context cycle.name = six.text_type(slugify(form.title.data)) cycle.title = form.title.data cycle.week = form.week.data cycle.is_interim = form.is_interim.data db_session.flush() return view_json(cycle, request)
def edit_json(context, request): """ Adds/Edits a external service record. If the operation was successful, a redirect to the new record details will be returns. Otherwise a json record of validation errors will be returned. """ check_csrf_token(request) db_session = request.db_session form = ExternalServiceForm(context, request).from_json(request.json_body) if not form.validate(): return HTTPBadRequest(json={'errors': wtferrors(form)}) if isinstance(context, models.ExternalServiceFactory): study = context.__parent__ service = models.ExternalService(study=study) else: study = context.study service = context service.name = slugify(form.title.data) service.title = form.title.data service.description = form.description.data service.url_template = form.url_template.data db_session.flush() success_url = request.route_path( 'studies.external_service', study=study.name, service=service.name ) return HTTPSeeOther(location=success_url)
def bulk_delete_json(context, request): """ Deletes forms in bulk """ check_csrf_token(request) db_session = request.db_session class DeleteForm(Form): forms = wtforms.FieldList( ModelField( db_session=db_session, class_=datastore.Entity), validators=[ wtforms.validators.DataRequired()]) form = DeleteForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) entity_ids = [entity.id for entity in form.forms.data] external = context.__parent__.__tablename__ key = context.__parent__.id (db_session.query(datastore.Entity) .filter(datastore.Entity.id.in_( db_session.query(datastore.Context.entity_id) .filter(datastore.Context.entity_id.in_(entity_ids)) .filter(datastore.Context.external == external) .filter(datastore.Context.key == key))) .delete('fetch')) db_session.flush() return HTTPOk()
def add_json(context, request): check_csrf_token(request) db_session = request.db_session def check_study_form(form, field): if isinstance(context.__parent__, models.Patient): query = ( db_session.query(datastore.Schema) .join(models.study_schema_table) .join(models.Study) .filter(datastore.Schema.id == field.data.id)) (exists,) = db_session.query(query.exists()).one() if not exists: raise wtforms.ValidationError(request.localizer.translate( _(u'This form is not assosiated with a study'))) elif isinstance(context.__parent__, models.Visit): query = ( db_session.query(models.Visit) .filter(models.Visit.id == context.__parent__.id) .join(models.Visit.cycles) .join(models.Cycle.study) .filter( models.Cycle.schemata.any(id=field.data.id) | models.Study.schemata.any(id=field.data.id))) (exists,) = db_session.query(query.exists()).one() if not exists: raise wtforms.ValidationError(request.localizer.translate( _('${schema} is not part of the studies for this visit'), mapping={'schema': field.data.title})) class AddForm(Form): schema = ModelField( db_session=db_session, class_=datastore.Schema, validators=[ wtforms.validators.InputRequired(), check_study_form]) collect_date = DateField( validators=[ wtforms.validators.InputRequired(), DateRange(min=date(1900, 1, 1)), ]) form = AddForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) default_state = ( db_session.query(datastore.State) .filter_by(name='pending-entry') .one()) entity = datastore.Entity( schema=form.schema.data, collect_date=form.collect_date.data, state=default_state) if isinstance(context.__parent__, models.Visit): context.__parent__.entities.add(entity) context.__parent__.patient.entities.add(entity) next = request.current_route_path( _route_name='studies.visit_form', form=entity.id) elif isinstance(context.__parent__, models.Patient): context.__parent__.entities.add(entity) next = request.current_route_path( _route_name='studies.patient_form', form=entity.id) db_session.flush() request.session.flash( _('Successfully added new ${form}', mapping={'form': entity.schema.title}), 'success') return {'__next__': next}
def edit_schedule_json(context, request): """ Enables/Disables a form for a cycle Request body json parameters: schema -- name of the schema (will used study-enabled versions) cycle -- cycle id enabled -- true/false """ check_csrf_token(request) db_session = request.db_session def check_cycle_association(form, field): if field.data.study != context: raise wtforms.ValidationError(request.localizer.translate(_( u'Not a valid choice'))) def check_form_association(form, field): query = ( db_session.query(datastore.Schema) .join(models.study_schema_table) .filter(datastore.Schema.name == field.data) .filter(models.study_schema_table.c.study_id == context.id)) (exists,) = db_session.query(query.exists()).one() if not exists: raise wtforms.ValidationError(request.localizer.translate(_( u'Not a valid choice'))) class ScheduleForm(Form): schema = wtforms.StringField( validators=[ wtforms.validators.InputRequired(), check_form_association]) cycle = ModelField( db_session=db_session, class_=models.Cycle, validators=[ wtforms.validators.InputRequired(), check_cycle_association]) enabled = wtforms.BooleanField() form = ScheduleForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) schema_name = form.schema.data cycle = form.cycle.data enabled = form.enabled.data study_items = set(i for i in context.schemata if i.name == schema_name) cycle_items = set(i for i in cycle.schemata if i.name == schema_name) if enabled: # Match cycle schemata to the study's schemata for the given name cycle.schemata.difference_update(cycle_items - study_items) cycle.schemata.update(study_items) else: cycle.schemata.difference_update(study_items | cycle_items) return HTTPOk()
def add_schema_json(context, request): check_csrf_token(request) db_session = request.db_session def check_not_patient_schema(form, field): (exists,) = ( db_session.query( db_session.query(datastore.Schema) .join(models.patient_schema_table) .filter(datastore.Schema.name == field.data) .exists()) .one()) if exists: raise wtforms.ValidationError(request.localizer.translate( _(u'Already a patient form'))) def check_not_randomization_schema(form, field): if (context.randomization_schema and context.randomization_schema.name == field.data): raise wtforms.ValidationError(request.localizer.translate( _(u'Already a randomization form'))) def check_not_termination_schema(form, field): if (context.termination_schema is not None and context.termination_schema.name == field.data): raise wtforms.ValidationError(request.localizer.translate( _(u'Already a termination form'))) def check_same_schema(form, field): versions = form.versions.data schema = form.schema.data invalid = [i.publish_date for i in versions if i.name != schema] if invalid: raise wtforms.ValidationError(request.localizer.translate(_( _(u'Incorrect versions: ${versions}'), mapping={'versions': ', '.join(map(str, invalid))}))) def check_published(form, field): if field.data.publish_date is None: raise wtforms.ValidationError(request.localizer.translate( _(u'Selected version is not published'))) class SchemaManagementForm(Form): schema = wtforms.StringField( validators=[ wtforms.validators.InputRequired(), check_not_patient_schema, check_not_randomization_schema, check_not_termination_schema]) versions = wtforms.FieldList( ModelField( db_session=db_session, class_=datastore.Schema, validators=[ wtforms.validators.InputRequired(), check_published]), validators=[ wtforms.validators.DataRequired(), check_same_schema]) form = SchemaManagementForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) old_items = set(i for i in context.schemata if i.name == form.schema.data) new_items = set(form.versions.data) # Remove unselected context.schemata.difference_update(old_items - new_items) # Add newly selected context.schemata.update(new_items) # Get a list of cycles to update cycles = ( db_session.query(models.Cycle) .options(orm.joinedload(models.Cycle.schemata)) .filter(models.Cycle.study == context) .filter(models.Cycle.schemata.any(name=form.schema.data))) # Also update available cycle schemata versions for cycle in cycles: cycle.schemata.difference_update(old_items - new_items) cycle.schemata.update(new_items) return form2json(new_items)[0]
def checkout(context, request): """ Generating a listing of available data for export. Because the exports can take a while to generate, this view serves as a "checkout" page so that the user can select which files they want. The actual exporting process is then queued in a another thread so the user isn't left with an unresponsive page. """ db_session = request.db_session plans = request.registry.settings['studies.export.plans'] exportables = exports.list_all(plans, request.db_session, include_rand=False) limit = request.registry.settings.get('app.export.limit') exceeded = limit is not None and query_exports(request).count() > limit errors = {} if request.method == 'POST' and check_csrf_token(request) and not exceeded: def check_exportable(form, field): if any(value not in exportables for value in field.data): raise wtforms.ValidationError( request.localizer.translate(_(u'Invalid selection'))) class CheckoutForm(Form): contents = wtforms.SelectMultipleField( choices=[(k, v.title) for k, v in six.iteritems(exportables)], validators=[wtforms.validators.InputRequired()]) expand_collections = wtforms.BooleanField(default=False) use_choice_labels = wtforms.BooleanField(default=False) form = CheckoutForm(request.POST) if not form.validate(): errors = wtferrors(form) else: task_id = six.text_type(str(uuid.uuid4())) db_session.add( models.Export( name=task_id, expand_collections=form.expand_collections.data, use_choice_labels=form.use_choice_labels.data, owner_user=(db_session.query(datastore.User).filter_by( key=request.authenticated_userid).one()), contents=[ exportables[k].to_json() for k in form.contents.data ])) def apply_after_commit(success): if success: tasks.make_export.apply_async(args=[task_id], task_id=task_id, countdown=4) # Avoid race-condition by executing the task after succesful commit transaction.get().addAfterCommitHook(apply_after_commit) msg = _(u'Your request has been received!') request.session.flash(msg, 'success') next_url = request.route_path('studies.exports_status') return HTTPFound(location=next_url) return { 'errors': errors, 'exceeded': exceeded, 'limit': limit, 'exportables': exportables }
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session is_new = isinstance(context, models.VisitFactory) form = VisitSchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if is_new: visit = models.Visit(patient=context.__parent__) db_session.add(visit) else: visit = context visit.patient.modify_date = datetime.now() visit.visit_date = form.visit_date.data # Set the entire list and let sqlalchemy prune the orphans visit.cycles = form.cycles.data db_session.flush() # TODO: hard coded for now, will be removed when workflows are in place default_state = (db_session.query( datastore.State).filter_by(name='pending-entry').one()) # Update collect date for those forms that were never modified if not is_new: for entity in visit.entities: if entity.state == default_state \ and entity.collect_date != visit.visit_date: entity.collect_date = visit.visit_date if form.include_forms.data: relative_schema = ( db_session.query( datastore.Schema.id.label('id'), datastore.Schema.name.label('name'), datastore.Schema.publish_date.label('publish_date'), sa.func.row_number().over( partition_by=datastore.Schema.name, order_by=( # Rank by versions before the visit date or closest (datastore.Schema.publish_date <= visit.visit_date ).desc(), sa.func.abs(datastore.Schema.publish_date - visit.visit_date).asc()), ).label('row_number')).join(models.Cycle.schemata).filter( models.Cycle.id.in_([ c.id for c in visit.cycles ])).filter(datastore.Schema.retract_date == sa.null()).cte( 'relative_schema')) schemata_query = (db_session.query(datastore.Schema).join( relative_schema, relative_schema.c.id == datastore.Schema.id).filter( relative_schema.c.row_number == 1)) # Ignore already-added schemata if isinstance(context, models.Visit) and visit.entities: schemata_query = schemata_query.filter(~datastore.Schema.name.in_( [entity.schema.name for entity in visit.entities])) for schema in schemata_query: entity = datastore.Entity(schema=schema, collect_date=visit.visit_date, state=default_state) visit.patient.entities.add(entity) visit.entities.add(entity) # Lab might not be enabled on a environments, check first if form.include_specimen.data and db_session.bind.has_table('specimen'): from occams_lims import models as lab drawstate = (db_session.query( lab.SpecimenState).filter_by(name=u'pending-draw').one()) location_id = visit.patient.site.lab_location.id for cycle in visit.cycles: if cycle in form.cycles.data: for specimen_type in cycle.specimen_types: db_session.add( lab.Specimen(patient=visit.patient, cycle=cycle, specimen_type=specimen_type, state=drawstate, collect_date=visit.visit_date, location_id=location_id, tubes=specimen_type.default_tubes)) db_session.flush() return view_json(visit, request)
def forms_add_json(context, request): """ Updates the available patient forms """ check_csrf_token(request) db_session = request.db_session def check_not_study_form(form, field): studies = (db_session.query(models.Study).filter( models.Study.schemata.any(id=field.data.id)).order_by( models.Study.title).all()) if studies: raise wtforms.ValidationError( request.localizer.translate( _(u'This form is already used by: {studies}'), mapping={'studies': ', '.join(s.title for s in studies)})) def check_not_termination_form(form, field): studies = (db_session.query(models.Study).filter( models.Study.termination_schema.has( name=field.data.name)).order_by(models.Study.title).all()) if studies: raise wtforms.ValidationError( request.localizer.translate(_( u'This form is already a termination form for: {studies}'), mapping={ 'studies': ', '.join(s.title for s in studies) })) def check_not_randomization_form(form, field): studies = (db_session.query(models.Study).filter( models.Study.randomization_schema.has( name=field.data.name)).order_by(models.Study.title).all()) if studies: raise wtforms.ValidationError( request.localizer.translate(_( u'This form is already a randomization form for: {studies}' ), mapping={ 'studies': ', '.join(s.title for s in studies) })) def check_unique_form(form, field): exists = (db_session.query(sa.literal(True)).filter( db_session.query(datastore.Schema).join( models.patient_schema_table).filter( datastore.Schema.name == field.data.name).exists()).scalar()) if exists: raise wtforms.ValidationError( request.localizer.translate( _(u'Only a single version of forms are currently supported' ))) class AddForm(Form): form = ModelField(db_session=db_session, class_=datastore.Schema, validators=[ wtforms.validators.InputRequired(), check_not_study_form, check_not_randomization_form, check_not_termination_form, check_unique_form ]) form = AddForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) db_session.execute(models.patient_schema_table.insert().values( schema_id=form.form.data.id)) mark_changed(db_session) return form2json(form.form.data)
def randomize_ajax(context, request): """ Procesess a patient's randomiation by completing randomization form Rules: * The user can only randomize one patient at a time. * If another randomization is in progress, both are restarted. * A randomization session may not "continue" from another. In order to address a single randomization at a time, the process assigns a "process id" or ``procid`` for the duration of the process, this way if a new process begins it will have a different token which will not match the current process and nullify everything. This is done by passing the ``procid`` via POST or GET depending on the phase of data entry and matching it against the session-stored ``procid``. If the they do not match, the operation is cancelled. The process goes as follows: # CHALLENGE: Upon first request the user will be issued a ``procid`` token this token will remain unchainged for the duration of the randomization process. If it changes, the process restarts. The goal of the challenge stage is to ensure the user confirms their intent to randomize. # ENTER: After passing the challenge stage, the user will then have opportunity to enter the randomization schema form data that will be used to determine assignement to the study arm. # VERIFY: The user will then have to verify the data again to ensure accurate responses. If the user fails this stage, they will have to pass the ENTER stage again. Upon sucessfull verification the ``procid`` expires and the patient is randomized. The user will not be shown the challenge/entry forms again and only the randomization information information will be rendered for future reference to the user. """ db_session = request.db_session enrollment = context if not enrollment.is_randomized: # Ensure a ``procid`` is assigned for the duration of the process # This way, if a new request mismatches, we can expire the operation if 'procid' not in request.GET and 'procid' not in request.POST: internal_procid = str(uuid.uuid4()) request.session[RAND_INFO_KEY] = { 'procid': internal_procid, 'stage': RAND_CHALLENGE, 'formdata': None } return HTTPFound( location=request.current_route_path( _query={'procid': internal_procid})) external_procid = request.GET.get('procid') or request.POST.get('procid') internal_procid = request.session.get(RAND_INFO_KEY, {}).get('procid') # compare internal and external ID to determine if a new process has # been initiated in a new tab if external_procid is not None and external_procid != internal_procid: try: del request.session[RAND_INFO_KEY] except KeyError: # pragma: no cover pass request.session.flash( _(u'You have another randomization in progress, ' u'starting over.'), 'warning') return HTTPFound(location=request.current_route_path(_query={})) if request.method == 'POST': check_csrf_token(request) if enrollment.is_randomized: request.session.flash( _(u'This patient is already randomized for this study'), 'warning') return HTTPFound(location=request.current_route_path(_query={})) if request.session[RAND_INFO_KEY]['stage'] == RAND_CHALLENGE: Form = _make_challenge_form(enrollment, request) form = Form(request.POST) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) else: request.session[RAND_INFO_KEY]['stage'] = RAND_ENTER request.session.changed() return HTTPFound(location=request.current_route_path( _query={'procid': internal_procid})) elif request.session[RAND_INFO_KEY]['stage'] == RAND_ENTER: Form = make_form( db_session, enrollment.study.randomization_schema, show_metadata=False) form = Form(request.POST) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) else: request.session[RAND_INFO_KEY]['stage'] = RAND_VERIFY request.session[RAND_INFO_KEY]['formdata'] = form.data request.session.changed() return HTTPFound(location=request.current_route_path( _query={'procid': internal_procid})) elif request.session[RAND_INFO_KEY]['stage'] == RAND_VERIFY: Form = make_form( db_session, enrollment.study.randomization_schema, show_metadata=False) form = Form(request.POST) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) else: previous_data = \ request.session[RAND_INFO_KEY].get('formdata') or {} # ensure entered values match previous values for field, value in form.data.items(): if value != previous_data.get(field): # start over request.session[RAND_INFO_KEY]['stage'] = RAND_ENTER request.session[RAND_INFO_KEY]['formdata'] = None request.session.flash( _(u'Your responses do not match previously ' u'entered responses. ' u'You will need to reenter your responses.'), 'warning') return HTTPFound(location=request.current_route_path( _query={'procid': internal_procid})) else: report = build_report( db_session, enrollment.study.randomization_schema.name) data = form.data # Get an unassigned entity that matches the input criteria query = ( db_session.query(models.Stratum) .filter(models.Stratum.study == enrollment.study) .filter(models.Stratum.patient == sa.null()) .join(models.Stratum.contexts) .join(datastore.Context.entity) .add_entity(datastore.Entity) .join(report, report.c.id == datastore.Entity.id) .filter(sa.and_( *[(getattr(report.c, k) == v) for k, v in data.items()])) .order_by(models.Stratum.id.asc()) .limit(1)) try: (stratum, entity) = query.one() except orm.exc.NoResultFound: raise HTTPBadRequest( body=_(u'Randomization numbers depleted')) # so far so good, set the contexts and complete the request stratum.patient = enrollment.patient entity.state = ( db_session.query(datastore.State) .filter_by(name=u'complete') .one()) entity.collect_date = date.today() enrollment.patient.entities.add(entity) enrollment.entities.add(entity) db_session.flush() del request.session[RAND_INFO_KEY] request.session.flash( _(u'Randomization complete'), 'success') return HTTPFound( location=request.current_route_path(_query={})) else: log.warn( u'Detected unknown randomization stage: {}'.format( str(request.session[RAND_INFO_KEY]))) request.session.flash( _(u'Unable to determine randomization state. Restarting'), 'warning') del request.session[RAND_INFO_KEY] return HTTPFound(location=request.current_route_path(_query={})) if enrollment.is_randomized: template = '../templates/enrollment/randomize-view.pt' form = _get_randomized_form(enrollment, request) elif request.session[RAND_INFO_KEY]['stage'] == RAND_CHALLENGE: template = '../templates/enrollment/randomize-challenge.pt' Form = _make_challenge_form(enrollment, request) Form.procid = wtforms.HiddenField() form = Form(procid=internal_procid) form.meta.entity = None form.meta.schema = enrollment.study.randomization_schema elif request.session[RAND_INFO_KEY]['stage'] == RAND_ENTER: template = '../templates/enrollment/randomize-enter.pt' Form = make_form( db_session, enrollment.study.randomization_schema, show_metadata=False) Form.procid = wtforms.HiddenField() form = Form(procid=internal_procid) elif request.session[RAND_INFO_KEY]['stage'] == RAND_VERIFY: template = '../templates/enrollment/randomize-verify.pt' Form = make_form( db_session, enrollment.study.randomization_schema, show_metadata=False) form = Form() Form.procid = wtforms.HiddenField() form = Form(procid=internal_procid) return { 'is_randomized': enrollment.is_randomized, 'enrollment': view_json(enrollment, request), 'content': render(template, { 'context': enrollment, 'request': request, 'form': render_form( form, disabled=enrollment.is_randomized, show_footer=False, attr={ 'id': 'enrollment-randomization', 'method': 'POST', 'action': request.current_route_path(), 'role': 'form', 'data-bind': 'formentry: {}, submit: $root.randomizeEnrollment' }) }) }
def terminate_ajax(context, request): db_session = request.db_session try: entity = ( db_session.query(datastore.Entity) .join(datastore.Entity.schema) .filter(datastore.Schema.name.in_( # Only search for forms being used as temrination forms db_session.query(datastore.Schema.name) .join(models.Study.termination_schema) .subquery())) .join(datastore.Context) .filter_by(external='enrollment', key=context.id) .one()) except orm.exc.MultipleResultsFound: raise Exception('Should only have one...') except orm.exc.NoResultFound: schema = context.study.termination_schema entity = datastore.Entity(schema=schema) # XXX: This is really bad form as we're applying # side-effects to a GET request, but there is no time # to make this look prety... # If you remove this line you will be creating random termination # entries... context.entities.add(entity) else: schema = entity.schema if not entity.state: entity.state = ( db_session.query(datastore.State) .filter_by(name='pending-entry') .one()) if 'termination_date' not in schema.attributes: msg = 'There is no "termination_date" configured on: {}' log.warn(msg.format(schema.name)) if request.has_permission('retract'): transition = modes.ALL elif request.has_permission('transition'): transition = modes.AVAILABLE else: transition = modes.AUTO Form = make_form( db_session, schema, entity=entity, transition=transition, show_metadata=False) form = Form(request.POST, data=entity_data(entity)) def validate_termination_date(form, field): if not (field.data >= context.latest_consent_date): raise wtforms.ValidationError(request.localizer.translate( _(u'Termination must be on or after latest consent (${date})'), mapping={'date': context.latest_consent_date} )) # Inject a validator into the termination form so that we # ensure that the termination date provided is valid form.termination_date.validators.append(validate_termination_date) if request.method == 'POST': check_csrf_token(request) if form.validate(): if not entity.id: # changing termination version *should* not be # allowed, just assign the schema that's already being used context.entities.add(entity) upload_dir = request.registry.settings['studies.blob.dir'] apply_data(db_session, entity, form.data, upload_dir) context.termination_date = form.termination_date.data db_session.flush() return HTTPOk(json=view_json(context, request)) else: return HTTPBadRequest(json={'errors': wtferrors(form)}) return render_form( form, cancel_url=request.current_route_path(_route_name='studies.patient'), attr={ 'method': 'POST', 'action': request.current_route_path(), 'role': 'form', 'data-bind': 'formentry: {}, submit: $root.terminateEnrollment' } )
def forms_add_json(context, request): """ Updates the available patient forms """ check_csrf_token(request) db_session = request.db_session def check_not_study_form(form, field): studies = ( db_session.query(models.Study) .filter(models.Study.schemata.any(id=field.data.id)) .order_by(models.Study.title) .all()) if studies: raise wtforms.ValidationError(request.localizer.translate( _(u'This form is already used by: {studies}'), mapping={'studies': ', '.join(s.title for s in studies)})) def check_not_termination_form(form, field): studies = ( db_session.query(models.Study) .filter(models.Study.termination_schema.has(name=field.data.name)) .order_by(models.Study.title) .all()) if studies: raise wtforms.ValidationError(request.localizer.translate( _(u'This form is already a termination form for: {studies}'), mapping={'studies': ', '.join(s.title for s in studies)})) def check_not_randomization_form(form, field): studies = ( db_session.query(models.Study) .filter(models.Study.randomization_schema.has( name=field.data.name)) .order_by(models.Study.title) .all()) if studies: raise wtforms.ValidationError(request.localizer.translate( _(u'This form is already a randomization form for: {studies}'), mapping={'studies': ', '.join(s.title for s in studies)})) def check_unique_form(form, field): exists = ( db_session.query(sa.literal(True)) .filter( db_session.query(datastore.Schema) .join(models.patient_schema_table) .filter(datastore.Schema.name == field.data.name) .exists()) .scalar()) if exists: raise wtforms.ValidationError(request.localizer.translate( _(u'Only a single version of forms are currently supported'))) class AddForm(Form): form = ModelField( db_session=db_session, class_=datastore.Schema, validators=[ wtforms.validators.InputRequired(), check_not_study_form, check_not_randomization_form, check_not_termination_form, check_unique_form]) form = AddForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) db_session.execute( models.patient_schema_table.insert() .values(schema_id=form.form.data.id)) mark_changed(db_session) return form2json(form.form.data)
def randomize_ajax(context, request): """ Procesess a patient's randomiation by completing randomization form Rules: * The user can only randomize one patient at a time. * If another randomization is in progress, both are restarted. * A randomization session may not "continue" from another. In order to address a single randomization at a time, the process assigns a "process id" or ``procid`` for the duration of the process, this way if a new process begins it will have a different token which will not match the current process and nullify everything. This is done by passing the ``procid`` via POST or GET depending on the phase of data entry and matching it against the session-stored ``procid``. If the they do not match, the operation is cancelled. The process goes as follows: # CHALLENGE: Upon first request the user will be issued a ``procid`` token this token will remain unchainged for the duration of the randomization process. If it changes, the process restarts. The goal of the challenge stage is to ensure the user confirms their intent to randomize. # ENTER: After passing the challenge stage, the user will then have opportunity to enter the randomization schema form data that will be used to determine assignement to the study arm. # VERIFY: The user will then have to verify the data again to ensure accurate responses. If the user fails this stage, they will have to pass the ENTER stage again. Upon sucessfull verification the ``procid`` expires and the patient is randomized. The user will not be shown the challenge/entry forms again and only the randomization information information will be rendered for future reference to the user. """ db_session = request.db_session enrollment = context if not enrollment.is_randomized: # Ensure a ``procid`` is assigned for the duration of the process # This way, if a new request mismatches, we can expire the operation if 'procid' not in request.GET and 'procid' not in request.POST: internal_procid = str(uuid.uuid4()) request.session[RAND_INFO_KEY] = { 'procid': internal_procid, 'stage': RAND_CHALLENGE, 'formdata': None } return HTTPFound(location=request.current_route_path( _query={'procid': internal_procid})) external_procid = request.GET.get('procid') or request.POST.get( 'procid') internal_procid = request.session.get(RAND_INFO_KEY, {}).get('procid') # compare internal and external ID to determine if a new process has # been initiated in a new tab if external_procid is not None and external_procid != internal_procid: try: del request.session[RAND_INFO_KEY] except KeyError: # pragma: no cover pass request.session.flash( _(u'You have another randomization in progress, ' u'starting over.'), 'warning') return HTTPFound(location=request.current_route_path(_query={})) if request.method == 'POST': check_csrf_token(request) if enrollment.is_randomized: request.session.flash( _(u'This patient is already randomized for this study'), 'warning') return HTTPFound(location=request.current_route_path(_query={})) if request.session[RAND_INFO_KEY]['stage'] == RAND_CHALLENGE: Form = _make_challenge_form(enrollment, request) form = Form(request.POST) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) else: request.session[RAND_INFO_KEY]['stage'] = RAND_ENTER request.session.changed() return HTTPFound(location=request.current_route_path( _query={'procid': internal_procid})) elif request.session[RAND_INFO_KEY]['stage'] == RAND_ENTER: Form = make_form(db_session, enrollment.study.randomization_schema, show_metadata=False) form = Form(request.POST) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) else: request.session[RAND_INFO_KEY]['stage'] = RAND_VERIFY request.session[RAND_INFO_KEY]['formdata'] = form.data request.session.changed() return HTTPFound(location=request.current_route_path( _query={'procid': internal_procid})) elif request.session[RAND_INFO_KEY]['stage'] == RAND_VERIFY: Form = make_form(db_session, enrollment.study.randomization_schema, show_metadata=False) form = Form(request.POST) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) else: previous_data = \ request.session[RAND_INFO_KEY].get('formdata') or {} # ensure entered values match previous values for field, value in form.data.items(): if value != previous_data.get(field): # start over request.session[RAND_INFO_KEY]['stage'] = RAND_ENTER request.session[RAND_INFO_KEY]['formdata'] = None request.session.flash( _(u'Your responses do not match previously ' u'entered responses. ' u'You will need to reenter your responses.'), 'warning') return HTTPFound(location=request.current_route_path( _query={'procid': internal_procid})) else: report = build_report( db_session, enrollment.study.randomization_schema.name) data = form.data # Get an unassigned entity that matches the input criteria query = (db_session.query(models.Stratum).filter( models.Stratum.study == enrollment.study).filter( models.Stratum.patient == sa.null()).join( models.Stratum.contexts).join( datastore.Context.entity).add_entity( datastore.Entity).join( report, report.c.id == datastore.Entity.id) .filter( sa.and_( *[(getattr(report.c, k) == v) for k, v in data.items()])).order_by( models.Stratum.id.asc()).limit(1)) try: (stratum, entity) = query.one() except orm.exc.NoResultFound: raise HTTPBadRequest( body=_(u'Randomization numbers depleted')) # so far so good, set the contexts and complete the request stratum.patient = enrollment.patient entity.state = (db_session.query( datastore.State).filter_by(name=u'complete').one()) entity.collect_date = date.today() enrollment.patient.entities.add(entity) enrollment.entities.add(entity) db_session.flush() del request.session[RAND_INFO_KEY] request.session.flash(_(u'Randomization complete'), 'success') return HTTPFound(location=request.current_route_path( _query={})) else: log.warn(u'Detected unknown randomization stage: {}'.format( str(request.session[RAND_INFO_KEY]))) request.session.flash( _(u'Unable to determine randomization state. Restarting'), 'warning') del request.session[RAND_INFO_KEY] return HTTPFound(location=request.current_route_path(_query={})) if enrollment.is_randomized: template = '../templates/enrollment/randomize-view.pt' form = _get_randomized_form(enrollment, request) elif request.session[RAND_INFO_KEY]['stage'] == RAND_CHALLENGE: template = '../templates/enrollment/randomize-challenge.pt' Form = _make_challenge_form(enrollment, request) Form.procid = wtforms.HiddenField() form = Form(procid=internal_procid) form.meta.entity = None form.meta.schema = enrollment.study.randomization_schema elif request.session[RAND_INFO_KEY]['stage'] == RAND_ENTER: template = '../templates/enrollment/randomize-enter.pt' Form = make_form(db_session, enrollment.study.randomization_schema, show_metadata=False) Form.procid = wtforms.HiddenField() form = Form(procid=internal_procid) elif request.session[RAND_INFO_KEY]['stage'] == RAND_VERIFY: template = '../templates/enrollment/randomize-verify.pt' Form = make_form(db_session, enrollment.study.randomization_schema, show_metadata=False) form = Form() Form.procid = wtforms.HiddenField() form = Form(procid=internal_procid) return { 'is_randomized': enrollment.is_randomized, 'enrollment': view_json(enrollment, request), 'content': render( template, { 'context': enrollment, 'request': request, 'form': render_form( form, disabled=enrollment.is_randomized, show_footer=False, attr={ 'id': 'enrollment-randomization', 'method': 'POST', 'action': request.current_route_path(), 'role': 'form', 'data-bind': 'formentry: {}, submit: $root.randomizeEnrollment' }) }) }
def add_schema_json(context, request): check_csrf_token(request) db_session = request.db_session def check_not_patient_schema(form, field): (exists, ) = (db_session.query( db_session.query(datastore.Schema).join( models.patient_schema_table).filter( datastore.Schema.name == field.data).exists()).one()) if exists: raise wtforms.ValidationError( request.localizer.translate(_(u'Already a patient form'))) def check_not_randomization_schema(form, field): if (context.randomization_schema and context.randomization_schema.name == field.data): raise wtforms.ValidationError( request.localizer.translate( _(u'Already a randomization form'))) def check_not_termination_schema(form, field): if (context.termination_schema is not None and context.termination_schema.name == field.data): raise wtforms.ValidationError( request.localizer.translate(_(u'Already a termination form'))) def check_same_schema(form, field): versions = form.versions.data schema = form.schema.data invalid = [i.publish_date for i in versions if i.name != schema] if invalid: raise wtforms.ValidationError( request.localizer.translate( _(_(u'Incorrect versions: ${versions}'), mapping={'versions': ', '.join(map(str, invalid))}))) def check_published(form, field): if field.data.publish_date is None: raise wtforms.ValidationError( request.localizer.translate( _(u'Selected version is not published'))) class SchemaManagementForm(Form): schema = wtforms.StringField(validators=[ wtforms.validators.InputRequired(), check_not_patient_schema, check_not_randomization_schema, check_not_termination_schema ]) versions = wtforms.FieldList( ModelField(db_session=db_session, class_=datastore.Schema, validators=[ wtforms.validators.InputRequired(), check_published ]), validators=[wtforms.validators.DataRequired(), check_same_schema]) form = SchemaManagementForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) old_items = set(i for i in context.schemata if i.name == form.schema.data) new_items = set(form.versions.data) # Remove unselected context.schemata.difference_update(old_items - new_items) # Add newly selected context.schemata.update(new_items) # Get a list of cycles to update cycles = (db_session.query(models.Cycle).options( orm.joinedload(models.Cycle.schemata)).filter( models.Cycle.study == context).filter( models.Cycle.schemata.any(name=form.schema.data))) # Also update available cycle schemata versions for cycle in cycles: cycle.schemata.difference_update(old_items - new_items) cycle.schemata.update(new_items) return form2json(new_items)[0]
def checkout(context, request): """ Generating a listing of available data for export. Because the exports can take a while to generate, this view serves as a "checkout" page so that the user can select which files they want. The actual exporting process is then queued in a another thread so the user isn't left with an unresponsive page. """ db_session = request.db_session plans = request.registry.settings['studies.export.plans'] exportables = exports.list_all( plans, request.db_session, include_rand=False) limit = request.registry.settings.get('app.export.limit') exceeded = limit is not None and query_exports(request).count() > limit errors = {} if request.method == 'POST' and check_csrf_token(request) and not exceeded: def check_exportable(form, field): if any(value not in exportables for value in field.data): raise wtforms.ValidationError(request.localizer.translate( _(u'Invalid selection'))) class CheckoutForm(Form): contents = wtforms.SelectMultipleField( choices=[(k, v.title) for k, v in six.iteritems(exportables)], validators=[ wtforms.validators.InputRequired()]) expand_collections = wtforms.BooleanField(default=False) use_choice_labels = wtforms.BooleanField(default=False) form = CheckoutForm(request.POST) if not form.validate(): errors = wtferrors(form) else: task_id = six.text_type(str(uuid.uuid4())) db_session.add(models.Export( name=task_id, expand_collections=form.expand_collections.data, use_choice_labels=form.use_choice_labels.data, owner_user=(db_session.query(datastore.User) .filter_by(key=request.authenticated_userid) .one()), contents=[exportables[k].to_json() for k in form.contents.data] )) def apply_after_commit(success): if success: tasks.make_export.apply_async( args=[task_id], task_id=task_id, countdown=4) # Avoid race-condition by executing the task after succesful commit transaction.get().addAfterCommitHook(apply_after_commit) msg = _(u'Your request has been received!') request.session.flash(msg, 'success') next_url = request.route_path('studies.exports_status') return HTTPFound(location=next_url) return { 'errors': errors, 'exceeded': exceeded, 'limit': limit, 'exportables': exportables }
def add_json(context, request): check_csrf_token(request) db_session = request.db_session def check_study_form(form, field): if isinstance(context.__parent__, models.Patient): query = (db_session.query(datastore.Schema).join( models.study_schema_table).join( models.Study).filter(datastore.Schema.id == field.data.id)) (exists, ) = db_session.query(query.exists()).one() if not exists: raise wtforms.ValidationError( request.localizer.translate( _(u'This form is not assosiated with a study'))) elif isinstance(context.__parent__, models.Visit): query = (db_session.query(models.Visit).filter( models.Visit.id == context.__parent__.id).join( models.Visit.cycles).join(models.Cycle.study).filter( models.Cycle.schemata.any(id=field.data.id) | models.Study.schemata.any(id=field.data.id))) (exists, ) = db_session.query(query.exists()).one() if not exists: raise wtforms.ValidationError( request.localizer.translate(_( '${schema} is not part of the studies for this visit'), mapping={ 'schema': field.data.title })) class AddForm(Form): schema = ModelField( db_session=db_session, class_=datastore.Schema, validators=[wtforms.validators.InputRequired(), check_study_form]) collect_date = DateField( validators=[wtforms.validators.InputRequired()]) form = AddForm.from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) default_state = (db_session.query( datastore.State).filter_by(name='pending-entry').one()) entity = datastore.Entity(schema=form.schema.data, collect_date=form.collect_date.data, state=default_state) if isinstance(context.__parent__, models.Visit): context.__parent__.entities.add(entity) context.__parent__.patient.entities.add(entity) next = request.current_route_path(_route_name='studies.visit_form', form=entity.id) elif isinstance(context.__parent__, models.Patient): context.__parent__.entities.add(entity) next = request.current_route_path(_route_name='studies.patient_form', form=entity.id) db_session.flush() request.session.flash( _('Successfully added new ${form}', mapping={'form': entity.schema.title}), 'success') return {'__next__': next}
def edit_json(context, request): check_csrf_token(request) db_session = request.db_session is_new = isinstance(context, models.VisitFactory) form = VisitSchema(context, request).from_json(request.json_body) if not form.validate(): raise HTTPBadRequest(json={'errors': wtferrors(form)}) if is_new: visit = models.Visit(patient=context.__parent__) db_session.add(visit) else: visit = context visit.patient.modify_date = datetime.now() visit.visit_date = form.visit_date.data # Set the entire list and let sqlalchemy prune the orphans visit.cycles = form.cycles.data db_session.flush() # TODO: hard coded for now, will be removed when workflows are in place default_state = ( db_session.query(datastore.State) .filter_by(name='pending-entry').one()) # Update collect date for those forms that were never modified if not is_new: for entity in visit.entities: if entity.state == default_state \ and entity.collect_date != visit.visit_date: entity.collect_date = visit.visit_date if form.include_forms.data: # find the most recent cycle form version relative to the visit recents = ( db_session.query( datastore.Schema.name.label('name'), sa.func.max( datastore.Schema.publish_date).label('publish_date')) .join(models.Cycle.schemata) .filter(models.Cycle.id.in_([c.id for c in visit.cycles])) .filter(datastore.Schema.publish_date <= visit.visit_date) .filter(datastore.Schema.retract_date == sa.null()) .group_by(datastore.Schema.name) .subquery('max_version')) # retrive the full schema record of the previous find schemata_query = ( db_session.query(datastore.Schema) .join(recents, ( (datastore.Schema.name == recents.c.name) & (datastore.Schema.publish_date == recents.c.publish_date)))) # Ignore already-added schemata if isinstance(context, models.Visit) and visit.entities: schemata_query = schemata_query.filter( ~datastore.Schema.name.in_( [entity.schema.name for entity in visit.entities])) for schema in schemata_query: entity = datastore.Entity( schema=schema, collect_date=visit.visit_date, state=default_state) visit.patient.entities.add(entity) visit.entities.add(entity) # Lab might not be enabled on a environments, check first if form.include_specimen.data and db_session.bind.has_table('specimen'): from occams_lims import models as lab drawstate = ( db_session.query(lab.SpecimenState) .filter_by(name=u'pending-draw') .one()) location_id = visit.patient.site.lab_location.id for cycle in visit.cycles: if cycle in form.cycles.data: for specimen_type in cycle.specimen_types: db_session.add(lab.Specimen( patient=visit.patient, cycle=cycle, specimen_type=specimen_type, state=drawstate, collect_date=visit.visit_date, location_id=location_id, tubes=specimen_type.default_tubes)) db_session.flush() return view_json(visit, request)
def terminate_ajax(context, request): db_session = request.db_session try: entity = ( db_session.query(datastore.Entity).join( datastore.Entity.schema).filter( datastore.Schema.name.in_( # Only search for forms being used as temrination forms db_session.query(datastore.Schema.name).join( models.Study.termination_schema ).subquery())).join(datastore.Context).filter_by( external='enrollment', key=context.id).one()) except orm.exc.MultipleResultsFound: raise Exception('Should only have one...') except orm.exc.NoResultFound: schema = context.study.termination_schema entity = datastore.Entity(schema=schema) # XXX: This is really bad form as we're applying # side-effects to a GET request, but there is no time # to make this look prety... # If you remove this line you will be creating random termination # entries... context.entities.add(entity) context.patient.entities.add(entity) else: schema = entity.schema if not entity.state: entity.state = (db_session.query( datastore.State).filter_by(name='pending-entry').one()) if 'termination_date' not in schema.attributes: msg = 'There is no "termination_date" configured on: {}' log.warn(msg.format(schema.name)) if request.has_permission('retract'): transition = modes.ALL elif request.has_permission('transition'): transition = modes.AVAILABLE else: transition = modes.AUTO Form = make_form(db_session, schema, entity=entity, transition=transition, show_metadata=False) form = Form(request.POST, data=entity_data(entity)) def validate_termination_date(form, field): if not (field.data >= context.latest_consent_date): raise wtforms.ValidationError( request.localizer.translate(_( u'Termination must be on or after latest consent (${date})' ), mapping={ 'date': context.latest_consent_date })) # Inject a validator into the termination form so that we # ensure that the termination date provided is valid form.termination_date.validators.append(validate_termination_date) if request.method == 'POST': check_csrf_token(request) if form.validate(): if not entity.id: # changing termination version *should* not be # allowed, just assign the schema that's already being used context.entities.add(entity) upload_dir = request.registry.settings['studies.blob.dir'] apply_data(db_session, entity, form.data, upload_dir) context.termination_date = form.termination_date.data db_session.flush() return HTTPOk(json=view_json(context, request)) else: return HTTPBadRequest(json={'errors': wtferrors(form)}) return render_form( form, cancel_url=request.current_route_path(_route_name='studies.patient'), attr={ 'method': 'POST', 'action': request.current_route_path(), 'role': 'form', 'data-bind': 'formentry: {}, submit: $root.terminateEnrollment' })