def test_skip_validation_if_to_pending_entry(self, db_session):
        from webob.multidict import MultiDict
        from occams_forms.renderers import make_form, states, modes

        schema = self._make_schema(db_session)
        Form = make_form(db_session, schema, transition=modes.ALL)
        form = Form(MultiDict({
            'ofworkflow_-state': states.PENDING_ENTRY,
        }))
        assert form.validate(), form.errors
    def test_skip_validation_if_to_pending_entry(self, db_session):
        from webob.multidict import MultiDict
        from occams_forms.renderers import make_form, states, modes

        schema = self._make_schema(db_session)
        Form = make_form(db_session, schema, transition=modes.ALL)
        form = Form(MultiDict({
            'ofworkflow_-state': states.PENDING_ENTRY,
        }))
        assert form.validate(), form.errors
    def test_skip_validation_if_not_collected(self, db_session):
        from datetime import date
        from webob.multidict import MultiDict
        from occams_forms.renderers import make_form

        schema = self._make_schema(db_session)
        Form = make_form(db_session, schema)

        form = Form(MultiDict({
            'ofmetadata_-collect_date': str(date.today()),
            'ofmetadata_-version': str(schema.publish_date),
            'ofmetadata_-not_done': '1',
        }))
        assert form.validate(), form.errors
Exemple #4
0
def _get_randomized_form(context, request):
    db_session = request.db_session
    try:
        entity = (db_session.query(datastore.Entity).join(
            datastore.Entity.contexts).filter_by(external='stratum',
                                                 key=context.stratum.id).one())
    except orm.exc.MultipleResultsFound:
        raise Exception('Should only have one...')
    except orm.exc.NoResultFound:
        raise HTTPNotFound()
    else:
        Form = make_form(db_session, entity.schema, show_metadata=False)
        form = Form(data=entity_data(entity))
    return form
    def test_skip_validation_if_not_collected(self, db_session):
        from datetime import date
        from webob.multidict import MultiDict
        from occams_forms.renderers import make_form

        schema = self._make_schema(db_session)
        Form = make_form(db_session, schema)

        form = Form(MultiDict({
            'ofmetadata_-collect_date': str(date.today()),
            'ofmetadata_-version': str(schema.publish_date),
            'ofmetadata_-not_done': '1',
        }))
        assert form.validate(), form.errors
def _get_randomized_form(context, request):
    db_session = request.db_session
    try:
        entity = (
            db_session.query(datastore.Entity)
            .join(datastore.Entity.contexts)
            .filter_by(external='stratum', key=context.stratum.id)
            .one())
    except orm.exc.MultipleResultsFound:
        raise Exception('Should only have one...')
    except orm.exc.NoResultFound:
        raise HTTPNotFound()
    else:
        Form = make_form(db_session, entity.schema, show_metadata=False)
        form = Form(data=entity_data(entity))
    return form
    def test_skip_validation_if_from_complete(self, db_session):
        from webob.multidict import MultiDict
        from occams_datastore import models as datastore
        from occams_forms.renderers import \
            make_form, states, modes, entity_data

        schema = self._make_schema(db_session)
        entity = datastore.Entity(
            schema=schema,
            state=(
                db_session.query(datastore.State)
                .filter_by(name=states.COMPLETE)
                .one()))
        Form = make_form(
            db_session, schema, entity=entity, transition=modes.ALL)
        formdata = MultiDict({
            'ofworkflow_-state': states.PENDING_CORRECTION,
        })
        form = Form(formdata, data=entity_data(entity))
        assert form.validate(), form.errors
    def test_skip_validation_if_from_complete(self, db_session):
        from webob.multidict import MultiDict
        from occams_datastore import models as datastore
        from occams_forms.renderers import \
            make_form, states, modes, entity_data

        schema = self._make_schema(db_session)
        entity = datastore.Entity(
            schema=schema,
            state=(
                db_session.query(datastore.State)
                .filter_by(name=states.COMPLETE)
                .one()))
        Form = make_form(
            db_session, schema, entity=entity, transition=modes.ALL)
        formdata = MultiDict({
            'ofworkflow_-state': states.PENDING_CORRECTION,
        })
        form = Form(formdata, data=entity_data(entity))
        assert form.validate(), form.errors
Exemple #9
0
def markup_ajax(context, request):
    """
    Returns the HTML markup of a form.

    This usually happens when a user has requested a different version
    of the form that they are trying to enter.
    """
    db_session = request.db_session
    version = request.GET.get('version')
    if not version:
        raise HTTPBadRequest()
    if version == context.schema.publish_date.isoformat():
        data = entity_data(context)
        schema = context.schema
    else:
        schema = (db_session.query(datastore.Schema).filter_by(
            name=context.schema.name, publish_date=version).one())
        data = None
    Form = make_form(db_session, schema, show_metadata=False)
    form = Form(request.POST, data=data)
    return render_form(form)
Exemple #10
0
    def _make_form(self, db_session):
        from datetime import date
        from occams_datastore import models as datastore
        from occams_forms.renderers import make_form

        schema = datastore.Schema(name=u'dymmy_schema',
                                  title=u'Dummy Schema',
                                  publish_date=date.today(),
                                  attributes={
                                      'dummy_field':
                                      datastore.Attribute(name=u'dummy_field',
                                                          title=u'Dummy Field',
                                                          type='string',
                                                          order=0)
                                  })

        entity = datastore.Entity(schema=schema)
        db_session.add(entity)
        db_session.flush()

        return make_form(db_session, schema, entity=entity)
Exemple #11
0
def markup_ajax(context, request):
    """
    Returns the HTML markup of a form.

    This usually happens when a user has requested a different version
    of the form that they are trying to enter.
    """
    db_session = request.db_session
    version = request.GET.get('version')
    if not version:
        raise HTTPBadRequest()
    if version == context.schema.publish_date.isoformat():
        data = entity_data(context)
        schema = context.schema
    else:
        schema = (
            db_session.query(datastore.Schema)
            .filter_by(name=context.schema.name, publish_date=version)
            .one())
        data = None
    Form = make_form(db_session, schema, show_metadata=False)
    form = Form(request.POST, data=data)
    return render_form(form)
    def _make_form(self, db_session):
        from datetime import date
        from occams_datastore import models as datastore
        from occams_forms.renderers import make_form

        schema = datastore.Schema(
            name=u'dymmy_schema',
            title=u'Dummy Schema',
            publish_date=date.today(),
            attributes={
                'dummy_field': datastore.Attribute(
                    name=u'dummy_field',
                    title=u'Dummy Field',
                    type='string',
                    order=0
                )
            })

        entity = datastore.Entity(schema=schema)
        db_session.add(entity)
        db_session.flush()

        return make_form(db_session, schema, entity=entity)
Exemple #13
0
def form(context, request):
    """
    XXX: Cannot merge into single view
        because of the way patient forms are handled
    """
    db_session = request.db_session

    patient = context.__parent__.__parent__
    schema = context.schema

    (is_phi, ) = (db_session.query(
        db_session.query(models.patient_schema_table).filter_by(
            schema_id=schema.id).exists()).one())

    if not is_phi:
        previous_url = request.current_route_path(
            _route_name='studies.patient_forms')
        show_metadata = True
        # We cannot determine which study this form will be applied to
        # so just use any version from active studies
        available_schemata = (db_session.query(datastore.Schema).join(
            models.study_schema_table).join(models.Study).filter(
                datastore.Schema.name == context.schema.name).filter(
                    datastore.Schema.publish_date != sa.null()).filter(
                        datastore.Schema.retract_date == sa.null()))
        allowed_versions = sorted(
            set(s.publish_date for s in available_schemata))
    else:
        previous_url = request.current_route_path(
            _route_name='studies.patient')
        show_metadata = False
        allowed_versions = None

    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,
        context.schema,
        entity=context,
        show_metadata=show_metadata,
        transition=transition,
        allowed_versions=allowed_versions,
    )

    form = Form(request.POST, data=entity_data(context))

    if request.method == 'POST':
        if not request.has_permission('edit', context):
            raise HTTPForbidden()
        if form.validate():
            upload_dir = request.registry.settings['studies.blob.dir']
            apply_data(db_session, context, form.data, upload_dir)
            db_session.flush()
            request.session.flash(
                _(u'Changes saved to: %s' % context.schema.title), 'success')
            return HTTPFound(location=previous_url)

    return {
        'phi':
        get_phi_entities(patient, request),
        'patient':
        view_json(patient, request),
        'form':
        render_form(form,
                    disabled=not request.has_permission('edit'),
                    cancel_url=previous_url,
                    attr={
                        'method': 'POST',
                        'action': request.current_route_path(),
                        'role': 'form'
                    }),
    }
Exemple #14
0
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'
        })
Exemple #15
0
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'
                    })
            })
    }
Exemple #16
0
def form(context, request):
    """
    XXX: Cannot merge into single view
        because of the way patient forms are handled
    """

    db_session = request.db_session
    visit = context.__parent__.__parent__
    allowed_schemata = (db_session.query(datastore.Schema).join(
        models.study_schema_table).join(models.Study).join(
            models.Cycle).filter(
                datastore.Schema.name == context.schema.name).filter(
                    models.Cycle.id.in_([cycle.id for cycle in visit.cycles])))
    allowed_versions = [s.publish_date for s in allowed_schemata]

    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,
        context.schema,
        entity=context,
        show_metadata=True,
        transition=transition,
        allowed_versions=allowed_versions,
    )

    form = Form(request.POST, data=entity_data(context))

    if request.method == 'POST':

        if not request.has_permission('edit', context):
            raise HTTPForbidden()

        if form.validate():
            upload_dir = request.registry.settings['studies.blob.dir']
            apply_data(db_session, context, form.data, upload_dir)
            db_session.flush()
            request.session.flash(
                _(u'Changes saved for: ${form}',
                  mapping={'form': context.schema.title}), 'success')
            return HTTPFound(location=request.current_route_path(
                _route_name='studies.visit'))

    return {
        'visit':
        view_json(visit, request),
        'form':
        render_form(
            form,
            disabled=not request.has_permission('edit'),
            cancel_url=request.current_route_path(_route_name='studies.visit'),
            attr={
                'method': 'POST',
                'action': request.current_route_path(),
                'role': 'form'
            }),
    }
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'
                })
        })
    }
Exemple #18
0
def form(context, request):
    """
    XXX: Cannot merge into single view
        because of the way patient forms are handled
    """
    db_session = request.db_session

    patient = context.__parent__.__parent__
    schema = context.schema

    (is_phi,) = (
        db_session.query(
            db_session.query(models.patient_schema_table)
            .filter_by(schema_id=schema.id)
            .exists())
        .one())

    if not is_phi:
        previous_url = request.current_route_path(
            _route_name='studies.patient_forms')
        show_metadata = True
        # We cannot determine which study this form will be applied to
        # so just use any version from active studies
        available_schemata = (
            db_session.query(datastore.Schema)
            .join(models.study_schema_table)
            .join(models.Study)
            .filter(datastore.Schema.name == context.schema.name)
            .filter(datastore.Schema.publish_date != sa.null())
            .filter(datastore.Schema.retract_date == sa.null()))
        allowed_versions = sorted(set(
            s.publish_date for s in available_schemata))
    else:
        previous_url = request.current_route_path(
            _route_name='studies.patient')
        show_metadata = False
        allowed_versions = None

    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,
        context.schema,
        entity=context,
        show_metadata=show_metadata,
        transition=transition,
        allowed_versions=allowed_versions,
    )

    form = Form(request.POST, data=entity_data(context))

    if request.method == 'POST':
        if not request.has_permission('edit', context):
            raise HTTPForbidden()
        if form.validate():
            upload_dir = request.registry.settings['studies.blob.dir']
            apply_data(db_session, context, form.data, upload_dir)
            db_session.flush()
            request.session.flash(
                _(u'Changes saved to: %s' % context.schema.title), 'success')
            return HTTPFound(location=previous_url)

    return {
        'phi': get_phi_entities(patient, request),
        'patient': view_json(patient, request),
        'form': render_form(
            form,
            disabled=not request.has_permission('edit'),
            cancel_url=previous_url,
            attr={
                'method': 'POST',
                'action': request.current_route_path(),
                'role': 'form'
            }
        ),
    }
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'
        }
    )
Exemple #20
0
def form(context, request):
    """
    XXX: Cannot merge into single view
        because of the way patient forms are handled
    """

    db_session = request.db_session
    visit = context.__parent__.__parent__
    allowed_schemata = (
        db_session.query(datastore.Schema)
        .join(models.study_schema_table)
        .join(models.Study)
        .join(models.Cycle)
        .filter(models.Cycle.id.in_([cycle.id for cycle in visit.cycles])))
    allowed_versions = [s.publish_date for s in allowed_schemata]

    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,
        context.schema,
        entity=context,
        show_metadata=True,
        transition=transition,
        allowed_versions=allowed_versions,
    )

    form = Form(request.POST, data=entity_data(context))

    if request.method == 'POST':

        if not request.has_permission('edit', context):
            raise HTTPForbidden()

        if form.validate():
            upload_dir = request.registry.settings['studies.blob.dir']
            apply_data(db_session, context, form.data, upload_dir)
            db_session.flush()
            request.session.flash(
                _(u'Changes saved for: ${form}', mapping={
                    'form': context.schema.title}),
                'success')
            return HTTPFound(location=request.current_route_path(
                _route_name='studies.visit'))

    return {
        'visit': view_json(visit, request),
        'form': render_form(
            form,
            disabled=not request.has_permission('edit'),
            cancel_url=request.current_route_path(_route_name='studies.visit'),
            attr={
                'method': 'POST',
                'action': request.current_route_path(),
                'role': 'form'
            }
        ),
    }