Beispiel #1
0
def submit_answered_survey(account_id, source_id, language_tag, body,
                           token_info):
    _validate_account_access(token_info, account_id)

    if body['survey_template_id'] == SurveyTemplateRepo.VIOSCREEN_ID:
        return _submit_vioscreen_status(account_id, source_id,
                                        body["survey_text"]["key"])

    # TODO: Is this supposed to return new survey id?
    # TODO: Rename survey_text to survey_model/model to match Vue's naming?
    with Transaction() as t:
        survey_answers_repo = SurveyAnswersRepo(t)
        survey_answers_id = survey_answers_repo.submit_answered_survey(
            account_id, source_id, language_tag, body["survey_template_id"],
            body["survey_text"])
        t.commit()

        response = flask.Response()
        response.status_code = 201
        response.headers['Location'] = '/api/accounts/%s' \
                                       '/sources/%s' \
                                       '/surveys/%s' % \
                                       (account_id,
                                        source_id,
                                        survey_answers_id)
        return response
def create_human_source_from_consent(account_id, body, token_info):
    _validate_account_access(token_info, account_id)

    # Must convert consent form body into object processable by create_source.

    # Not adding any error handling here because if 'participant_name' isn't
    # here, we SHOULD be getting an error.
    source = {
        'source_type': Source.SOURCE_TYPE_HUMAN,
        'source_name': body['participant_name'],
        'consent': {
            'participant_email': body['participant_email'],
            'age_range': body['age_range']
        }
    }

    deceased_parent_key = 'deceased_parent'
    child_keys = {
        'parent_1_name', 'parent_2_name', deceased_parent_key, 'obtainer_name'
    }

    intersection = child_keys.intersection(body)
    if intersection:
        source['consent']['child_info'] = {}
        for key in intersection:
            if key == deceased_parent_key:
                body[deceased_parent_key] = body[deceased_parent_key] == 'true'
            source['consent']['child_info'][key] = body[key]

    # NB: Don't expect to handle errors 404, 422 in this function; expect to
    # farm out to `create_source`
    return create_source(account_id, source, token_info)
Beispiel #3
0
def top_food_report(account_id, source_id, survey_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        vioscreen_repo = VioscreenRepo(t)

        # Vioscreen username is our survey_id
        status = vioscreen_repo.get_vioscreen_status(account_id, source_id,
                                                     survey_id)
        if status != 3:
            # Oops, we don't have results available for this one
            raise NotFound("No such survey recorded")

        vio = VioscreenAdminAPI()
        sessions = vio.sessions(survey_id)
        # Looks like vioscreen supports multiple sessions per user, do we care?
        session_id = sessions[0]['sessionId']
        report = vio.top_food_report(session_id)

        response = make_response(report)
        response.headers.set("Content-Type", "application/pdf")
        # TODO: Do we want it to download a file or be embedded in the html?
        # response.headers.set('Content-Disposition',
        #                      'attachment',
        #                      filename='top-food-report.pdf')

        return response
Beispiel #4
0
def read_survey_templates(account_id, source_id, language_tag, token_info):
    _validate_account_access(token_info, account_id)

    # TODO: I don't think surveys have names... only survey groups have names.
    #  So what can I pass down to the user that will make any sense here?

    # Note to future maintainers,
    # 2/21/20: currently the only way to figure this out
    # is to look through the "surveys" and "survey_group" tables, try:
    # select survey_id, american from surveys left join survey_group on
    # survey_group = group_order;

    with Transaction() as t:
        source_repo = SourceRepo(t)
        source = source_repo.get_source(account_id, source_id)
        if source is None:
            return jsonify(code=404, message="No source found"), 404
        template_repo = SurveyTemplateRepo(t)
        if source.source_type == Source.SOURCE_TYPE_HUMAN:
            return jsonify([
                template_repo.get_survey_template_link_info(x)
                for x in [1, 3, 4, 5, 6, SurveyTemplateRepo.VIOSCREEN_ID]
            ]), 200
        elif source.source_type == Source.SOURCE_TYPE_ANIMAL:
            return jsonify([
                template_repo.get_survey_template_link_info(x) for x in [2]
            ]), 200
        else:
            return jsonify([]), 200
def update_sample_association(account_id, source_id, sample_id, body,
                              token_info):
    _validate_account_access(token_info, account_id)

    # TODO: API layer doesn't understand that BadRequest can be thrown,
    #  but that looks to be the right result if sample_site bad.
    #  Need to update the api layer if we want to specify 400s.
    #  (Or we leave api as is and say 400's can always be thrown if your
    #  request is bad)
    with Transaction() as t:
        sample_repo = SampleRepo(t)
        source_repo = SourceRepo(t)

        source = source_repo.get_source(account_id, source_id)
        if source is None:
            return jsonify(code=404, message="No such source"), 404

        needs_sample_site = source.source_type in [
            Source.SOURCE_TYPE_HUMAN, Source.SOURCE_TYPE_ANIMAL
        ]

        precludes_sample_site = source.source_type == \
            Source.SOURCE_TYPE_ENVIRONMENT

        sample_site_present = "sample_site" in body and \
                              body["sample_site"] is not None

        if needs_sample_site and not sample_site_present:
            # Human/Animal sources require sample_site to be set
            raise BadRequest("human/animal samples require sample_site")
        if precludes_sample_site and sample_site_present:
            raise BadRequest("environmental samples cannot specify "
                             "sample_site")

        sample_datetime = body['sample_datetime']
        try:
            sample_datetime = fromisotime(sample_datetime)
        except ValueError:
            raise BadRequest("Invalid sample_datetime")
        curdate = datetime.now(sample_datetime.tzinfo)
        lower_limit = curdate + relativedelta(years=-10)
        upper_limit = curdate + relativedelta(months=+1)
        if sample_datetime < lower_limit or sample_datetime > upper_limit:
            raise BadRequest('Invalid sample date')
        # sample_site will not be present if its environmental. this will
        # default to None if the key is not present
        sample_site = body.get('sample_site')
        sample_info = SampleInfo(sample_id, sample_datetime, sample_site,
                                 body["sample_notes"])

        is_admin = token_grants_admin_access(token_info)
        sample_repo.update_info(account_id,
                                source_id,
                                sample_info,
                                override_locked=is_admin)

        final_sample = sample_repo.get_sample(account_id, source_id, sample_id)
        t.commit()
    return jsonify(final_sample), 200
def read_sample_associations(account_id, source_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        samples = sample_repo.get_samples_by_source(account_id, source_id)

    api_samples = [x.to_api() for x in samples]
    return jsonify(api_samples), 200
def read_source(account_id, source_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        source_repo = SourceRepo(t)
        source = source_repo.get_source(account_id, source_id)
        if source is None:
            return jsonify(code=404, message=SRC_NOT_FOUND_MSG), 404
        return jsonify(source.to_api()), 200
def read_sources(account_id, token_info, source_type=None):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        source_repo = SourceRepo(t)
        sources = source_repo.get_sources_in_account(account_id, source_type)
        api_sources = [x.to_api() for x in sources]
        # TODO: Also support 404? Or is that not necessary?
        return jsonify(api_sources), 200
def read_sample_association(account_id, source_id, sample_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        sample = sample_repo.get_sample(account_id, source_id, sample_id)
        if sample is None:
            return jsonify(code=404, message="Sample not found"), 404

        return jsonify(sample.to_api()), 200
def delete_source(account_id, source_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        source_repo = SourceRepo(t)
        if not source_repo.delete_source(account_id, source_id):
            return jsonify(code=404, message=SRC_NOT_FOUND_MSG), 404
        # TODO: 422?
        t.commit()
        return '', 204
def dissociate_answered_survey(account_id, source_id, sample_id, survey_id,
                               token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        answers_repo = SurveyAnswersRepo(t)
        answers_repo.dissociate_answered_survey_from_sample(
            account_id, source_id, sample_id, survey_id)
        t.commit()
    return '', 204
Beispiel #12
0
def read_sample_vioscreen_session(account_id, source_id, sample_id,
                                  token_info):
    _validate_account_access(token_info, account_id)

    is_error, vioscreen_session = _get_session_by_account_details(
        account_id, source_id, sample_id)
    if is_error:
        return vioscreen_session

    return jsonify(vioscreen_session[0].to_api()), 200
Beispiel #13
0
def read_survey_template(account_id, source_id, survey_template_id,
                         language_tag, token_info, survey_redirect_url=None):
    _validate_account_access(token_info, account_id)

    # TODO: can we get rid of source_id?  I don't have anything useful to do
    #  with it...  I guess I could check if the source is a dog before giving
    #  out a pet information survey?

    with Transaction() as t:
        survey_template_repo = SurveyTemplateRepo(t)
        info = survey_template_repo.get_survey_template_link_info(
            survey_template_id)

        # For external surveys, we generate links pointing out
        if survey_template_id == SurveyTemplateRepo.VIOSCREEN_ID:

            url = vioscreen.gen_survey_url(
                language_tag, survey_redirect_url
            )
            # TODO FIXME HACK: This field's contents are not specified!
            info.survey_template_text = {
                "url": url
            }
            return jsonify(info), 200

        # For local surveys, we generate the json representing the survey
        survey_template = survey_template_repo.get_survey_template(
            survey_template_id, language_tag)
        info.survey_template_text = vue_adapter.to_vue_schema(survey_template)

        # TODO FIXME HACK: We need a better way to enforce validation on fields
        #  that need it, can this be stored adjacent to the survey questions?
        client_side_validation = {
            "108": {
                # Height
                "inputType": "number",
                "validator": "number",
                "min": 0,
                "max": None
            },
            "113": {
                # Weight
                "inputType": "number",
                "validator": "number",
                "min": 0,
                "max": None
            }
        }
        for group in info.survey_template_text.groups:
            for field in group.fields:
                if field.id in client_side_validation:
                    field.set(**client_side_validation[field.id])

        return jsonify(info), 200
def render_consent_doc(account_id, language_tag, token_info):
    _validate_account_access(token_info, account_id)

    # NB: Do NOT need to explicitly pass account_id into template for
    # integration into form submission URL because form submit URL builds on
    # the base of the URL that called it (which includes account_id)

    localization_info = localization.LANG_SUPPORT[language_tag]
    content = localization_info[localization.NEW_PARTICIPANT_KEY]

    return jsonify(content), 200
Beispiel #15
0
def associate_sample(account_id, source_id, body, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        sample_repo.associate_sample(account_id, source_id, body['sample_id'])
        t.commit()
    response = flask.Response()
    response.status_code = 201
    response.headers['Location'] = '/api/accounts/%s/sources/%s/samples/%s' % \
                                   (account_id, source_id, body['sample_id'])
    return response
Beispiel #16
0
def read_answered_surveys(account_id, source_id, language_tag, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        survey_answers_repo = SurveyAnswersRepo(t)
        survey_template_repo = SurveyTemplateRepo(t)
        answered_surveys = survey_answers_repo.list_answered_surveys(
            account_id, source_id)
        api_objs = []
        for ans in answered_surveys:
            template_id = survey_answers_repo.find_survey_template_id(ans)
            if template_id is None:
                continue
            o = survey_template_repo.get_survey_template_link_info(template_id)
            api_objs.append(o.to_api(ans))
        return jsonify(api_objs), 200
Beispiel #17
0
def read_sample_vioscreen_mpeds(account_id, source_id, sample_id, token_info):
    _validate_account_access(token_info, account_id)

    is_error, vioscreen_session = _get_session_by_account_details(
        account_id, source_id, sample_id)
    if is_error:
        return vioscreen_session

    with Transaction() as t:
        vio_mped = VioscreenMPedsRepo(t)

        vioscreen_mpeds = vio_mped.get_mpeds(vioscreen_session[0].sessionId)
        if vioscreen_mpeds is None:
            return jsonify(code=404, message="MPeds not found"), 404

        return jsonify(vioscreen_mpeds.to_api()), 200
def delete_source(account_id, source_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        source_repo = SourceRepo(t)
        survey_answers_repo = SurveyAnswersRepo(t)

        answers = survey_answers_repo.list_answered_surveys(
            account_id, source_id)
        for survey_id in answers:
            survey_answers_repo.delete_answered_survey(account_id, survey_id)

        if not source_repo.delete_source(account_id, source_id):
            return jsonify(code=404, message=SRC_NOT_FOUND_MSG), 404
        # TODO: 422?
        t.commit()
        return '', 204
Beispiel #19
0
def read_sample_vioscreen_food_consumption(account_id, source_id, sample_id,
                                           token_info):
    _validate_account_access(token_info, account_id)

    is_error, vioscreen_session = _get_session_by_account_details(
        account_id, source_id, sample_id)
    if is_error:
        return vioscreen_session

    with Transaction() as t:
        vio_cons = VioscreenFoodConsumptionRepo(t)

        vioscreen_food_consumption = vio_cons.get_food_consumption(
            vioscreen_session[0].sessionId)
        if vioscreen_food_consumption is None:
            return jsonify(code=404, message="Food Consumption not found"), 404

        return jsonify(vioscreen_food_consumption.to_api()), 200
Beispiel #20
0
def dissociate_sample(account_id, source_id, sample_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        answers_repo = SurveyAnswersRepo(t)
        answered_survey_ids = answers_repo.list_answered_surveys_by_sample(
            account_id, source_id, sample_id)

        for curr_answered_survey_id in answered_survey_ids:
            answers_repo.dissociate_answered_survey_from_sample(
                account_id, source_id, sample_id, curr_answered_survey_id)

        sample_repo = SampleRepo(t)
        sample_repo.dissociate_sample(account_id, source_id, sample_id)

        t.commit()

        return '', 204
Beispiel #21
0
def read_sample_vioscreen_dietary_score(account_id, source_id, sample_id,
                                        token_info):
    _validate_account_access(token_info, account_id)

    is_error, vioscreen_session = _get_session_by_account_details(
        account_id, source_id, sample_id)
    if is_error:
        return vioscreen_session

    with Transaction() as t:
        vio_diet = VioscreenDietaryScoreRepo(t)

        vioscreen_dietary_scores = vio_diet.get_dietary_scores(
            vioscreen_session[0].sessionId)
        if vioscreen_dietary_scores is None:
            return jsonify(code=404, message="Dietary Score not found"), 404

        return jsonify([vds.to_api() for vds in vioscreen_dietary_scores]), 200
Beispiel #22
0
def read_sample_vioscreen_percent_energy(account_id, source_id, sample_id,
                                         token_info):
    _validate_account_access(token_info, account_id)

    is_error, vioscreen_session = _get_session_by_account_details(
        account_id, source_id, sample_id)
    if is_error:
        return vioscreen_session

    with Transaction() as t:
        vio_perc = VioscreenPercentEnergyRepo(t)

        vioscreen_percent_energy = vio_perc.get_percent_energy(
            vioscreen_session[0].sessionId)
        if vioscreen_percent_energy is None:
            return jsonify(code=404, message="Percent Energy not found"), 404

        return jsonify(vioscreen_percent_energy.to_api()), 200
def associate_answered_survey(account_id, source_id, sample_id, body,
                              token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        answers_repo = SurveyAnswersRepo(t)
        answers_repo.associate_answered_survey_with_sample(
            account_id, source_id, sample_id, body['survey_id'])
        t.commit()

    response = flask.Response()
    response.status_code = 201
    response.headers['Location'] = '/api/accounts/%s' \
                                   '/sources/%s' \
                                   '/surveys/%s' % \
                                   (account_id,
                                    source_id,
                                    body['survey_id'])
    return response
Beispiel #24
0
def read_answered_survey_associations(account_id, source_id, sample_id,
                                      token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        answers_repo = SurveyAnswersRepo(t)
        template_repo = SurveyTemplateRepo(t)
        answered_surveys = answers_repo.list_answered_surveys_by_sample(
            account_id, source_id, sample_id)

        resp_obj = []
        for answered_survey in answered_surveys:
            template_id = answers_repo.find_survey_template_id(answered_survey)
            if template_id is None:
                continue
            info = template_repo.get_survey_template_link_info(template_id)
            resp_obj.append(info.to_api(answered_survey))

        t.commit()
        return jsonify(resp_obj), 200
Beispiel #25
0
def read_answered_survey(account_id, source_id, survey_id, language_tag,
                         token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        survey_answers_repo = SurveyAnswersRepo(t)
        survey_answers = survey_answers_repo.get_answered_survey(
            account_id, source_id, survey_id, language_tag)
        if not survey_answers:
            return jsonify(code=404, message="No survey answers found"), 404

        template_id = survey_answers_repo.find_survey_template_id(survey_id)
        if template_id is None:
            return jsonify(code=422, message="No answers in survey"), 422

        template_repo = SurveyTemplateRepo(t)
        link_info = template_repo.get_survey_template_link_info(template_id)
        link_info.survey_id = survey_id
        link_info.survey_text = survey_answers
        return jsonify(link_info), 200
def create_source(account_id, body, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        source_repo = SourceRepo(t)
        source_id = str(uuid.uuid4())
        name = body["source_name"]
        source_type = body['source_type']

        if source_type == Source.SOURCE_TYPE_HUMAN:
            # TODO: Unfortunately, humans require a lot of special handling,
            #  and we started mixing Source calls used for transforming to/
            #  from the database with source calls to/from the api.
            #  Would be nice to split this out better.
            source_info = HumanInfo.from_dict(body,
                                              consent_date=date.today(),
                                              date_revoked=None)
            # the "legacy" value of the age_range enum is not valid to use when
            # creating a new source, so do not allow that.
            # NB: Not necessary to do this check when updating a source as
            # only source name and description (not age_range) may be updated.
            if source_info.age_range == "legacy":
                raise RepoException("Age range may not be set to legacy.")
        else:
            source_info = NonHumanInfo.from_dict(body)

        new_source = Source(source_id, account_id, source_type, name,
                            source_info)
        source_repo.create_source(new_source)

        # Must pull from db to get creation_time, update_time
        s = source_repo.get_source(account_id, new_source.id)
        t.commit()

    response = jsonify(s.to_api())
    response.status_code = 201
    response.headers['Location'] = '/api/accounts/%s/sources/%s' % \
                                   (account_id, source_id)
    return response
def read_sample_association(account_id, source_id, sample_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        sample = sample_repo.get_sample(account_id, source_id, sample_id)
        if sample is None:
            return jsonify(code=404, message="Sample not found"), 404

    qiita_body = {'sample_ids': ["10317." + str(sample.barcode)]}

    try:
        qiita_data = qclient.post('/api/v1/study/10317/samples/status',
                                  json=qiita_body)
        accession_urls = []
        for barcode_info in qiita_data:
            experiment_accession = barcode_info.get("ebi_experiment_accession")
            if experiment_accession is None:
                continue
            accession_urls.append("https://www.ebi.ac.uk/ena/browser/view/" +
                                  experiment_accession + "?show=reads")
        sample.set_accession_urls(accession_urls)
    except NotFoundError:
        # I guess qiita doesn't know about this barcode,
        # so probably no ebi accession info
        pass
    except BadRequestError:
        # How do I log these to gunicorn??
        app.logger.warning("Couldn't communicate with qiita", exc_info=True)
    except ForbiddenError:
        # How do I log these to gunicorn??
        app.logger.warning("Couldn't communicate with qiita", exc_info=True)
    except RuntimeError:
        # How do I log these to gunicorn??
        app.logger.warning("Couldn't communicate with qiita", exc_info=True)
        raise

    return jsonify(sample.to_api()),
def update_source(account_id, source_id, body, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        source_repo = SourceRepo(t)
        source = source_repo.get_source(account_id, source_id)
        if source is None:
            return jsonify(code=404, message=SRC_NOT_FOUND_MSG), 404

        source.name = body["source_name"]
        # every type of source has a name but not every type has a description
        if getattr(source.source_data, "description", False):
            source.source_data.description = body.get("source_description",
                                                      None)
        source_repo.update_source_data_api_fields(source)

        # I wonder if there's some way to get the creation_time/update_time
        # during the insert/update...
        source = source_repo.get_source(account_id, source_id)
        t.commit()

        # TODO: 422? Not sure this can actually happen anymore ...
        return jsonify(source.to_api()), 200
Beispiel #29
0
def read_survey_template(account_id,
                         source_id,
                         survey_template_id,
                         language_tag,
                         token_info,
                         survey_redirect_url=None,
                         vioscreen_ext_sample_id=None):
    _validate_account_access(token_info, account_id)

    # TODO: can we get rid of source_id?  I don't have anything useful to do
    #  with it...  I guess I could check if the source is a dog before giving
    #  out a pet information survey?

    with Transaction() as t:
        survey_template_repo = SurveyTemplateRepo(t)
        info = survey_template_repo.get_survey_template_link_info(
            survey_template_id)

        # For external surveys, we generate links pointing out
        if survey_template_id == SurveyTemplateRepo.VIOSCREEN_ID:
            if vioscreen_ext_sample_id:
                # User is about to start a vioscreen survey for this sample
                # record this in the database.
                db_vioscreen_id = survey_template_repo.create_vioscreen_id(
                    account_id, source_id, vioscreen_ext_sample_id)
            else:
                raise ValueError("Vioscreen Template requires "
                                 "vioscreen_ext_sample_id parameter.")
            (birth_year, gender
             ) = survey_template_repo.fetch_user_birth_year_gender(account_id)
            url = vioscreen.gen_survey_url(db_vioscreen_id,
                                           language_tag,
                                           survey_redirect_url,
                                           birth_year=birth_year,
                                           gender=gender)
            # TODO FIXME HACK: This field's contents are not specified!
            info.survey_template_text = {"url": url}
            t.commit()
            return jsonify(info), 200

        # For local surveys, we generate the json representing the survey
        survey_template = survey_template_repo.get_survey_template(
            survey_template_id, language_tag)
        info.survey_template_text = vue_adapter.to_vue_schema(survey_template)

        # TODO FIXME HACK: We need a better way to enforce validation on fields
        #  that need it, can this be stored adjacent to the survey questions?
        client_side_validation = {
            "108": {
                # Height
                "inputType": "number",
                "validator": "number",
                "min": 0,
                "max": None
            },
            "113": {
                # Weight
                "inputType": "number",
                "validator": "number",
                "min": 0,
                "max": None
            }
        }
        for group in info.survey_template_text.groups:
            for field in group.fields:
                if field.id in client_side_validation:
                    field.set(**client_side_validation[field.id])

        return jsonify(info), 200
def read_survey_template(account_id,
                         source_id,
                         survey_template_id,
                         language_tag,
                         token_info,
                         survey_redirect_url=None,
                         vioscreen_ext_sample_id=None):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        survey_template_repo = SurveyTemplateRepo(t)
        info = survey_template_repo.get_survey_template_link_info(
            survey_template_id)
        remote_surveys = set(survey_template_repo.remote_surveys())

        # For external surveys, we generate links pointing out
        if survey_template_id in remote_surveys:
            if survey_template_id == SurveyTemplateRepo.VIOSCREEN_ID:
                url = _remote_survey_url_vioscreen(t, account_id, source_id,
                                                   language_tag,
                                                   survey_redirect_url,
                                                   vioscreen_ext_sample_id)
            elif survey_template_id == SurveyTemplateRepo.MYFOODREPO_ID:
                url = _remote_survey_url_myfoodrepo(t, account_id, source_id,
                                                    language_tag)
            elif survey_template_id == SurveyTemplateRepo.POLYPHENOL_FFQ_ID:
                url = _remote_survey_url_polyphenol_ffq(
                    t, account_id, source_id, language_tag)
            else:
                raise ValueError(f"Cannot generate URL for survey "
                                 f"{survey_template_id}")

            # TODO FIXME HACK: This field's contents are not specified!
            info.survey_template_text = {"url": url}
            t.commit()
            return jsonify(info), 200

        # For local surveys, we generate the json representing the survey
        survey_template = survey_template_repo.get_survey_template(
            survey_template_id, language_tag)
        info.survey_template_text = vue_adapter.to_vue_schema(survey_template)

        # TODO FIXME HACK: We need a better way to enforce validation on fields
        #  that need it, can this be stored adjacent to the survey questions?
        client_side_validation = {
            "108": {
                # Height
                "inputType": "number",
                "validator": "integer",
                "min": 0,
                "max": None
            },
            "113": {
                # Weight
                "inputType": "number",
                "validator": "integer",
                "min": 0,
                "max": None
            }
        }
        for group in info.survey_template_text.groups:
            for field in group.fields:
                if field.id in client_side_validation:
                    field.set(**client_side_validation[field.id])

        return jsonify(info), 200