Skip to content

Commit

Permalink
Merge pull request #339 from biocore/csymons_skin_scoring_app_working
Browse files Browse the repository at this point in the history
Skin Scoring App
  • Loading branch information
cassidysymons authored Feb 12, 2025
2 parents c381a5f + e302620 commit 7b82dfd
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 11 deletions.
75 changes: 68 additions & 7 deletions microsetta_interface/implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class Source:
VIOSCREEN_ID = 10001
MYFOODREPO_ID = 10002
POLYPHENOL_FFQ_ID = 10003
SKIN_SCORING_APP_ID = 10005
SPAIN_FFQ_ID = 10004

SYSTEM_MSG_DICTIONARY = {
Expand Down Expand Up @@ -398,6 +399,21 @@ class Source:
'est_minutes': '30',
'icon': 'survey_external.svg'
},
SKIN_SCORING_APP_ID: {
'description': 'This will direct you to the ModiFace skin-scoring web'
' app. This app allows you to upload a selfie photo, '
'which will be used to generate anonymized data about '
'your skin for researchers and provide you with what '
'the algorithm assesses to be your top two skin '
'concerns. You will be provided a username and study'
' code on the next screen to access the app, which '
'will link your ModiFace results to your skin sample. '
'This app is hosted by a third-party provider; we are '
'unable to provide any assistance if you encounter '
'errors or issues while using the app.',
'est_minutes': '5',
'icon': 'survey_external.svg'
},
}
LOCAL_SURVEY_SEQUENCE = [
BASIC_INFO_ID,
Expand Down Expand Up @@ -466,7 +482,8 @@ def _get_req_survey_templates_by_source_type(source_type):

def _get_opt_survey_templates_by_source_type(source_type):
if source_type == Source.SOURCE_TYPE_HUMAN:
return [3, 4, 5, 7, MYFOODREPO_ID, POLYPHENOL_FFQ_ID, SPAIN_FFQ_ID]
return [3, 4, 5, 7, MYFOODREPO_ID, POLYPHENOL_FFQ_ID,
SPAIN_FFQ_ID, SKIN_SCORING_APP_ID]
elif source_type == Source.SOURCE_TYPE_ANIMAL:
return []
elif source_type == Source.SOURCE_TYPE_ENVIRONMENT:
Expand Down Expand Up @@ -1398,6 +1415,14 @@ def get_fill_source_survey(*,
account_id, source_id, "data", reconsent=True
)

# this is remote, so go to an external url, not our jinja2 template
return redirect(survey_output['survey_template_text']['url'])
elif survey_template_id == SKIN_SCORING_APP_ID:
if need_reconsent:
return render_consent_page(
account_id, source_id, "data", reconsent=True
)

# this is remote, so go to an external url, not our jinja2 template
return redirect(survey_output['survey_template_text']['url'])
else:
Expand Down Expand Up @@ -1542,6 +1567,25 @@ def get_myfoodrepo_no_slots(*, account_id=None, source_id=None):
source_id=source_id)


@prerequisite([SOURCE_PREREQS_MET, BIOSPECIMEN_PREREQS_MET])
def post_ajax_skin_scoring_app_credentials(*, account_id, source_id):
need_reconsent = check_current_consent(account_id, source_id, "data")

if need_reconsent:
return render_consent_page(
account_id, source_id, "data", reconsent=True
)

has_error, credentials, _ = ApiRequest.post(
"/accounts/%s/sources/%s/surveys/skin_scoring_app_credentials"
% (account_id, source_id)
)
if has_error == 404:
return flask.jsonify({"app_username": "", "app_studycode": ""})
else:
return flask.jsonify(credentials)


@prerequisite([SOURCE_PREREQS_MET, BIOSPECIMEN_PREREQS_MET])
def get_fill_vioscreen_remote_sample_survey(*,
account_id=None,
Expand Down Expand Up @@ -1850,7 +1894,10 @@ def get_source(*, account_id=None, source_id=None):
for answer in survey_answers:
template_id = answer['survey_template_id']
for template in local_surveys + remote_surveys:
if template['survey_template_id'] == template_id:
if template['survey_template_id'] == SKIN_SCORING_APP_ID:
template['survey_id'] = answer['survey_id']
template['answered'] = True
else:
template['answered'] = True

for template in local_surveys:
Expand Down Expand Up @@ -1879,11 +1926,24 @@ def get_source(*, account_id=None, source_id=None):
template['est_minutes'] = SURVEY_INFO[template_id]['est_minutes']
template['icon'] = SURVEY_INFO[template_id]['icon']

# TODO: MyFoodRepo logic needs to be refactored when we reactivate it
"""
# any survey specific stuff like opening a tab
# or slot checking
for idx, template in enumerate(remote_surveys[:]):
# NB: change "_" back to "idx" when MyFoodRepo is reactivated or if
# another external survey requires similar functionality
for _, template in enumerate(remote_surveys[:]):
if template['survey_template_id'] == SKIN_SCORING_APP_ID:
has_error, credentials, _ = ApiRequest.get(
'/accounts/%s/sources/%s/surveys/skin_scoring_app_credentials'
% (account_id, source_id)
)

if has_error:
return has_error

template['credentials'] = credentials

# TODO: MyFoodRepo logic needs to be refactored when we reactivate it
"""
if template['survey_template_id'] == MYFOODREPO_ID:
has_error, slots, _ = ApiRequest.get('/slots/myfoodrepo')
if has_error:
Expand All @@ -1896,7 +1956,7 @@ def get_source(*, account_id=None, source_id=None):
per_source_not_taken.pop(idx)
else:
template['new_tab'] = False
"""
"""

local_surveys = [translate_survey_template(s) for s in local_surveys]
remote_surveys = [translate_survey_template(s) for s in remote_surveys]
Expand All @@ -1911,7 +1971,8 @@ def get_source(*, account_id=None, source_id=None):
source_name=source_output['source_name'],
profile_has_samples=profile_has_samples,
need_reconsent=need_reconsent,
show_update_age=show_update_age
show_update_age=show_update_age,
SKIN_SCORING_APP_ID=SKIN_SCORING_APP_ID
)


Expand Down
16 changes: 16 additions & 0 deletions microsetta_interface/routes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,22 @@ paths:
'302':
description: Redirecting to necessary action

'/accounts/{account_id}/sources/{source_id}/create_skin_scoring_app_credentials':
post:
operationId: microsetta_interface.implementation.post_ajax_skin_scoring_app_credentials
tags:
- Survey
parameters:
- $ref: '#/components/parameters/account_id'
- $ref: '#/components/parameters/source_id'
responses:
'200':
description: Credentials for skin scoring app
content:
application/json:
schema:
type: object

'/accounts/{account_id}/sources/{source_id}/vioscreen_ffq':
get:
operationId: microsetta_interface.implementation.get_fill_vioscreen_remote_sample_survey
Expand Down
106 changes: 102 additions & 4 deletions microsetta_interface/templates/source.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,74 @@
}
{% endif %}
let skin_app_username = '';
let skin_app_studycode = '';
function handleSkinAppModal(survey_link, survey_title, survey_description, app_username, app_studycode) {
$("#skin-app-modal-title").empty();
$("#skin-app-modal-title").text(survey_title);
$("#skin-app-survey-description").empty();
$("#skin-app-survey-description").text(survey_description);
// We allow the function to update the global variables, which will happen when the user receives credentials and re-renders the modal.
// However, there's no situation where we'd retract the credentials, so we don't need to do anything with blank values.
if(app_username != '' && app_username != 'None') {
skin_app_username = app_username
skin_app_studycode = app_studycode
}
if(skin_app_username != '') {
$("#skin-app-take-survey").html('{{ _('Take This Survey Now') }}');
document.getElementById("app-credentials-div").style.display = '';
document.getElementById("app-credentials-username").innerHTML = skin_app_username;
document.getElementById("app-credentials-studycode").innerHTML = skin_app_studycode;
// Once we display the credentials, the button action is to open the actual survey
$("#skin-app-take-survey").click(function(e) {
window.open(survey_link, '_blank').focus();
});
} else {
$("#skin-app-take-survey").html('{{ _('Get Username and Study Code') }}');
document.getElementById("app-credentials-div").style.display = 'none';
$("#skin-app-take-survey").click(async function(e) {
let url = '/accounts/{{ account_id }}/sources/{{ source_id }}/create_skin_scoring_app_credentials';
$.ajax({
url: url,
type: "POST",
success: function(data) {
if(!Object.hasOwn(data, 'app_username')) {
// The response doesn't have credentials, something went very wrong. Handle it gracefully.
document.getElementById("app-error").style.display = '';
} else {
let s_user = data['app_username'];
let s_sc = data['app_studycode'];
if (s_user.length < 1) {
// We weren't able to allocate them credentials.
document.getElementById("app-error").style.display = '';
} else {
// We got credentials, refresh this modal
handleSkinAppModal(survey_link, survey_title, survey_description, s_user, s_sc);
}
}
}
});
});
}
$("#skin_app_modal").modal('show');
return false;
}
function hideModal() {
$("#source_modal").modal('hide');
return false;
}
function hideSkinModal() {
$("#skin_app_modal").modal('hide');
return false;
}
</script>
<script src="/static/vendor/js/jquery.form-4.2.2/jquery.form.min.js"></script>
{% endblock %}
Expand Down Expand Up @@ -123,8 +187,12 @@
<div class="row mt-4">
{% for detail in remote_surveys %}
<div class="col-12 col-sm-6 col-md-4 p-2">
{% if not detail.answered %}
<a style="text-decoration: none" onClick="return takeExternalSurvey('/accounts/{{ account_id }}/sources/{{ source_id }}/take_survey?survey_template_id={{ detail.survey_template_id }}', '{{ detail.survey_template_title }}', '{{ detail.description }}');" href="/accounts/{{ account_id }}/sources/{{ source_id }}/take_survey?survey_template_id={{ detail.survey_template_id }}" target="_blank">
{% if detail.survey_template_id == SKIN_SCORING_APP_ID %}
<a style="text-decoration: none" onClick="return handleSkinAppModal('/accounts/{{ account_id }}/sources/{{ source_id }}/take_survey?survey_template_id={{ detail.survey_template_id }}', '{{ detail.survey_template_title }}', '{{ detail.description }}', '{{ detail.credentials['app_username'] }}', '{{ detail.credentials['app_studycode'] }}');" href="/accounts/{{ account_id }}/sources/{{ source_id }}/take_survey?survey_template_id={{ detail.survey_template_id }}" target="_blank">
{% else %}
{% if not detail.answered %}
<a style="text-decoration: none" onClick="return takeExternalSurvey('/accounts/{{ account_id }}/sources/{{ source_id }}/take_survey?survey_template_id={{ detail.survey_template_id }}', '{{ detail.survey_template_title }}', '{{ detail.description }}');" href="/accounts/{{ account_id }}/sources/{{ source_id }}/take_survey?survey_template_id={{ detail.survey_template_id }}" target="_blank">
{% endif %}
{% endif %}
<div class="card card-survey-external">
<div class="row m-2 survey-info-row">
Expand All @@ -136,7 +204,7 @@
<div class="col-4 small-text survey-info text-end">{{ detail.est_minutes }} {{ _('min') }}</div>
</div>
<div class="row m-3 text-center survey-title">
{% if not detail.answered %}
{% if not detail.answered or detail.survey_template_id == SKIN_SCORING_APP_ID %}
<img src="/static/img/survey_external.svg" class="card-survey-icon-external">
{% else %}
<img src="/static/img/survey_external_taken.svg" class="card-survey-icon-external">
Expand All @@ -150,7 +218,7 @@
{% endif %}
</div>
</div>
{% if not detail.answered %}
{% if not detail.answered or detail.survey_template_id == SKIN_SCORING_APP_ID %}
</a>
{% endif %}
</div>
Expand Down Expand Up @@ -179,4 +247,34 @@
</div>
</div>

<! -- Modal for skin-scoring app -->
<div class="modal fade" id="skin_app_modal" tabindex="-1" role="dialog" aria-labelledby="skinAppModal" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<img src="/static/img/survey_external.svg">
<span class="survey-title" id="skin-app-modal-title"></span>
</div>
<div class="modal-body">
<p class="survey-description" id="skin-app-survey-description"></p>
</div>
<div class="modal-body" id="app-credentials-div">
<p class="survey-description">
<strong>Username: </strong><span id="app-credentials-username"></span><br />
<strong>Study Code: </strong><span id="app-credentials-studycode"></span>
</p>
</div>
<div class="modal-body" id="app-error" style="display: none">
<p class="survey-description">
{{ _('Sorry, there was a problem generating a username and study code. Please try again later.') }}
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white-blue-border" id="skin_app_cancel" onClick="hideSkinModal();">{{ _('Maybe Later') }}</button>
<button type="button" class="btn btn-blue-gradient" id="skin-app-take-survey">{{ _('Take This Survey Now') }}</button>
</div>
</div>
</div>
</div>

{% endblock %}
6 changes: 6 additions & 0 deletions microsetta_interface/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def _fake_jwt(email, verified, uniqify=False):
MYFOODREPO_ID = 10002
POLYPHENOL_FFQ_ID = 10003
SPAIN_FFQ_ID = 10004
SKIN_SCORING_APP_ID = 10005

BASIC_INFO_SIMPLE = {"112": "1970"}
BASIC_INFO_SIMPLE_ALT = {"112": "1983"}
Expand Down Expand Up @@ -573,6 +574,11 @@ def _complete_spain_ffq_survey(self, account_id, source_id):
f'take_survey?survey_template_id=10004')
return self.app.get(url), url

def _complete_skin_scoring_app_survey(self, account_id, source_id):
url = (f'/accounts/{account_id}/sources/{source_id}/'
f'take_survey?survey_template_id={SKIN_SCORING_APP_ID}')
return self.app.get(url), url

def test_new_user_to_source_listing(self):
resp, url, user_jwt = self._new_to_create()
account_id, _, _ = self._ids_from_url(url)
Expand Down

0 comments on commit 7b82dfd

Please sign in to comment.