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 submit_answered_survey(self, ag_login_id, source_id, language_tag, survey_template_id, survey_model, survey_answers_id=None): # note that "ag_login_id" is the same as account_id # This is actually pretty complicated in the current schema: # We need to filter the model down to questions that are in the # template # We need to ensure that the account has access to write the given # participant # We need to generate a survey_answer id # We need to log that the user submitted this survey # We need to write each answer to one or more rows # TODO: We need to ensure that the account has access to write the # given participant!?! if survey_answers_id is None: survey_answers_id = str(uuid.uuid4()) survey_template_repo = SurveyTemplateRepo(self._transaction) survey_template = survey_template_repo.get_survey_template( survey_template_id, language_tag) with self._transaction.cursor() as cur: # Log that the user submitted this survey cur.execute( "INSERT INTO ag_login_surveys " "(ag_login_id, survey_id, source_id) " "VALUES(%s, %s, %s)", (ag_login_id, survey_answers_id, source_id)) # Write each answer for survey_template_group in survey_template.groups: for survey_question in survey_template_group.questions: survey_question_id = survey_question.id q_type = survey_question.response_type # TODO FIXME HACK: Modify DB to make this unnecessary! # We MUST record at least ONE answer for each survey # (even if the user answered nothing) # or we can't properly track the survey template id later. # Therefore, if the user answered NOTHING, store an empty # string for the first string or text question in the # survey, just so something is recorded. if len(survey_model) == 0 and \ (q_type == "STRING" or q_type == "TEXT"): survey_model[str(survey_question_id)] = "" if str(survey_question_id) not in survey_model: # TODO: Is this supposed to leave the question blank # or write Unspecified? continue answer = survey_model[str(survey_question_id)] if q_type == "SINGLE": # Normalize localized answer normalized_answer = self._unlocalize( answer, language_tag) try: cur.execute( "INSERT INTO survey_answers " "(survey_id, " "survey_question_id, " "response) " "VALUES(%s, %s, %s)", (survey_answers_id, survey_question_id, normalized_answer)) except psycopg2.errors.ForeignKeyViolation: raise BadRequest("Invalid survey response: %s" % answer) if q_type == "MULTIPLE": for ans in answer: normalized_answer = self._unlocalize( ans, language_tag) try: cur.execute( "INSERT INTO survey_answers " "(survey_id, " "survey_question_id, " "response) " "VALUES(%s, %s, %s)", (survey_answers_id, survey_question_id, normalized_answer)) except psycopg2.errors.ForeignKeyViolation: raise BadRequest( "Invalid survey response: %s" % ans) if q_type == "STRING" or q_type == "TEXT": # Note: Can't convert language on free text... cur.execute( "INSERT INTO survey_answers_other " "(survey_id, " "survey_question_id, " "response) " "VALUES(%s, %s, %s)", (survey_answers_id, survey_question_id, answer)) if len(survey_model) == 0: # we should not have gotten to the end without recording at least # ONE answer (even an empty one) ... but it could happen if this # survey template includes NO text or string questions AND the # user doesn't answer any of the questions it does include. Not # worth making the code robust to this case, as this whole "include # one empty answer" is a temporary hack, but at least ensure we # know this problem occurred if it ever does raise RepoException("No answers provided for survey template %s " "and not able to add an empty string default" % survey_template_id) return survey_answers_id
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