Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul #268

Merged
merged 87 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
8e868a6
Initial commit for testing
cassidysymons Jan 12, 2023
a47ac23
Survey & consent adjustments
cassidysymons Jan 17, 2023
ad62ec3
Survey fix
cassidysymons Jan 17, 2023
02b6438
Survey question numbering
cassidysymons Jan 18, 2023
cd768c1
Initial kit testing
cassidysymons Jan 19, 2023
6b31b7e
Initial kit testing
cassidysymons Jan 19, 2023
2c21408
Fixes
cassidysymons Jan 20, 2023
0b6c141
UI adjustments
cassidysymons Jan 23, 2023
a17715e
UI adjustments
cassidysymons Jan 24, 2023
47878d3
UI adjustments
cassidysymons Jan 25, 2023
ae333c1
UI adjustments
cassidysymons Jan 25, 2023
0d21cae
UI adjustments
cassidysymons Jan 25, 2023
948cf97
UI adjustments
cassidysymons Jan 25, 2023
94ee37a
UI adjustments
cassidysymons Jan 26, 2023
bb8c9e7
UI adjustments
cassidysymons Jan 30, 2023
6ca41a6
UI adjustments
cassidysymons Jan 30, 2023
da00ae5
UI adjustments
cassidysymons Jan 30, 2023
f9d0949
UI adjustments
cassidysymons Jan 31, 2023
cd96913
UI adjustments
cassidysymons Jan 31, 2023
77769b3
UI adjustments
cassidysymons Jan 31, 2023
3c59f09
UI adjustments
cassidysymons Jan 31, 2023
b430281
UI adjustments
cassidysymons Feb 1, 2023
cb19b95
UI adjustments
cassidysymons Feb 1, 2023
86b8e0d
Merge remote-tracking branch 'origin/master-overhaul' into csymons_ov…
cassidysymons Feb 1, 2023
b5ad792
UI adjustments
cassidysymons Feb 1, 2023
04065ef
UI adjustments
cassidysymons Feb 1, 2023
6d8f719
UI adjustments
cassidysymons Feb 1, 2023
d002955
UI adjustments
cassidysymons Feb 1, 2023
cd887b0
UI adjustments
cassidysymons Feb 1, 2023
3b284a3
UI adjustments
cassidysymons Feb 1, 2023
e066557
UI adjustments
cassidysymons Feb 1, 2023
ae4f996
UI adjustments
cassidysymons Feb 1, 2023
68118ff
UI adjustments
cassidysymons Feb 1, 2023
617e761
UI adjustments
cassidysymons Feb 1, 2023
08b5c00
UI adjustments
cassidysymons Feb 2, 2023
93eb6a4
UI adjustments
cassidysymons Feb 2, 2023
86baa75
UI adjustments
cassidysymons Feb 2, 2023
0030a9b
UI adjustments
cassidysymons Feb 2, 2023
bba72ce
UI adjustments
cassidysymons Feb 2, 2023
389cea2
UI adjustments
cassidysymons Feb 2, 2023
7d9b156
UI adjustments
cassidysymons Feb 2, 2023
5358766
UI adjustments
cassidysymons Feb 3, 2023
014039f
Merge branch 'master-overhaul' into csymons_overhaul
cassidysymons Feb 7, 2023
0fb3614
Update microsetta_interface/implementation.py
cassidysymons Feb 14, 2023
f233c2c
Update microsetta_interface/templates/survey.jinja2
cassidysymons Feb 14, 2023
8e30742
Update microsetta_interface/implementation.py
cassidysymons Feb 14, 2023
a8e2966
Update microsetta_interface/implementation.py
cassidysymons Feb 14, 2023
0c1a068
Update microsetta_interface/implementation.py
cassidysymons Feb 14, 2023
f3ba2a8
Update microsetta_interface/implementation.py
cassidysymons Feb 14, 2023
26ec5db
Update microsetta_interface/static/css/minimal_interface.css
cassidysymons Feb 14, 2023
116c89d
Update microsetta_interface/templates/consents.jinja2
cassidysymons Feb 14, 2023
5e421c9
Update microsetta_interface/templates/consents.jinja2
cassidysymons Feb 14, 2023
1e66850
Update microsetta_interface/templates/consents.jinja2
cassidysymons Feb 14, 2023
6b91d58
Update microsetta_interface/templates/kits.jinja2
cassidysymons Feb 14, 2023
a43524f
Update microsetta_interface/implementation.py
cassidysymons Feb 14, 2023
65e4d83
Update microsetta_interface/templates/kits.jinja2
cassidysymons Feb 14, 2023
7afb7d1
Update microsetta_interface/templates/new_participant.jinja2
cassidysymons Feb 14, 2023
13dbb48
Update microsetta_interface/templates/reports.jinja2
cassidysymons Feb 14, 2023
bcd0f6d
Update microsetta_interface/templates/source.jinja2
cassidysymons Feb 14, 2023
d531892
Update microsetta_interface/templates/sample.jinja2
cassidysymons Feb 14, 2023
9a44f38
Update microsetta_interface/templates/survey.jinja2
cassidysymons Feb 14, 2023
e034ca1
Update microsetta_interface/templates/sample.jinja2
cassidysymons Feb 14, 2023
906feb4
Update microsetta_interface/templates/sample.jinja2
cassidysymons Feb 14, 2023
735fd3e
Update microsetta_interface/templates/sitebase.jinja2
cassidysymons Feb 14, 2023
c41bcc8
Update microsetta_interface/templates/survey.jinja2
cassidysymons Feb 14, 2023
96f921a
Update microsetta_interface/templates/survey.jinja2
cassidysymons Feb 14, 2023
bcd8378
Update microsetta_interface/templates/source.jinja2
cassidysymons Feb 14, 2023
717ed51
Update microsetta_interface/templates/source.jinja2
cassidysymons Feb 14, 2023
7dcee3f
Code review changes
cassidysymons Feb 15, 2023
ee38219
Lint fixes
cassidysymons Feb 15, 2023
90ab335
Test adjustments
cassidysymons Feb 15, 2023
c1d33ff
Fix babel issue
cassidysymons Feb 15, 2023
827b738
Fix babel issue
cassidysymons Feb 15, 2023
3bb07a3
Test adjustments
cassidysymons Feb 15, 2023
6f891d8
State machine update + minor adjustments
cassidysymons Feb 22, 2023
1c63149
Lint + tests
cassidysymons Feb 22, 2023
09b19df
Lint + tests
cassidysymons Feb 22, 2023
ad51bd3
Integration
cassidysymons Feb 22, 2023
e564375
Integration
cassidysymons Feb 22, 2023
d370088
Integration
cassidysymons Feb 22, 2023
6391fb5
Integration
cassidysymons Feb 22, 2023
6f0d8b5
Integration
cassidysymons Feb 22, 2023
d5148f2
Integration
cassidysymons Feb 22, 2023
127f54f
Integration
cassidysymons Feb 22, 2023
c634fc9
Integration
cassidysymons Feb 22, 2023
953e491
Update microsetta_interface/tests/test_integration.py
cassidysymons Feb 22, 2023
ee053c3
Update microsetta_interface/tests/test_integration.py
cassidysymons Feb 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 84 additions & 86 deletions microsetta_interface/implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from microsetta_interface.redis_cache import RedisCache
from microsetta_interface.util import has_non_keyword_arguments, \
parse_request_csv_col, parse_request_csv, dict_to_csv
from collections import defaultdict


# TODO: source from a microsetta_private_api endpoint
Expand All @@ -42,7 +43,7 @@ class Source:

PUBKEY_ENVVAR = 'MICROSETTA_INTERFACE_DEBUG_JWT_PUB'
if os.environ.get(PUBKEY_ENVVAR, False):
PUB_KEY = open(os.environ[PUBKEY_ENVVAR]).read()
PUB_KEY = open(os.environ[PUBKEY_ENVVAR]).read()
else:
PUB_KEY = pkg_resources.read_text(
'microsetta_interface',
Expand Down Expand Up @@ -71,8 +72,9 @@ class Source:
ACCT_ADDR_POST_CODE_KEY = "post_code"
ACCT_ADDR_COUNTRY_CODE_KEY = "country_code"
ACCT_LANG_KEY = "language"
ACCT_TERMS_KEY = "consent_privacy_terms"
ACCT_WRITEABLE_KEYS = [ACCT_FNAME_KEY, ACCT_LNAME_KEY, ACCT_EMAIL_KEY,
ACCT_ADDR_KEY, ACCT_LANG_KEY]
ACCT_ADDR_KEY, ACCT_LANG_KEY, ACCT_TERMS_KEY]

# States
NEEDS_REROUTE = "NeedsReroute"
Expand Down Expand Up @@ -120,7 +122,7 @@ class Source:
KITS_TAB_WHITELIST = {'US', 'ES', 'JP'}

# Countries allowed to view the contents of the My Nutrition tab
NUTRITION_TAB_WHITELIST = ['US']
NUTRITION_TAB_WHITELIST = {'US'}

# TODO: Move this sort of survey info ino the database
SURVEY_INFO = {
Expand Down Expand Up @@ -169,16 +171,6 @@ class Source:
'est_minutes': '18',
'icon': 'survey_detailed_diet.svg'
},
MIGRAINE_ID: {
'description': '',
'est_minutes': '',
'icon': 'survey_external.svg'
},
SURFERS_ID: {
'description': '',
'est_minutes': '',
'icon': 'survey_external.svg'
},
COVID19_ID: {
'description': '',
'est_minutes': '8',
Expand All @@ -189,37 +181,6 @@ class Source:
'est_minutes': '2',
'icon': 'survey_other.svg'
},
1: {
'description': 'The general questionnaire for Microsetta',
'est_minutes': '10',
'icon': 'survey_external.svg'
},
3: {
'description': 'A fermented foods specific questionnaire',
'est_minutes': '4',
'icon': 'survey_external.svg'
},
4: {
'description': 'Questions on surfing behavior',
'est_minutes': '5',
'icon': 'survey_external.svg'
},
5: {
'description': 'Questions about your interest in the microbiome',
'est_minutes': '3',
'icon': 'survey_external.svg'
},
6: {
'description': 'Questions specific to COVID19',
'est_minutes': '2',
'icon': 'survey_external.svg'
},
7: {
'description': 'Questions related to cooking oils and oxalate-rich '
'foods',
'est_minutes': '5',
'icon': 'survey_external.svg'
},
VIOSCREEN_ID: {
'description': 'Our standard food frequency questionnaire',
'est_minutes': '30',
Expand Down Expand Up @@ -828,13 +789,17 @@ def get_create_account():

@prerequisite([NEEDS_ACCOUNT])
def post_create_account(*, body=None):
# We want to do server-side validation that they agreed to the Privacy
# Statement and T&C but we don't need to save the information
if "consent_privacy_terms" not in body:
exception_msg = "You must agree to the Privacy Statement and Terms "\
"and Conditions."
raise Exception(exception_msg)

# the form posts consent_privacy_terms as a string but boolean is
# more appropriate for the API
consent_privacy_terms = False
if body['consent_privacy_terms'] == "True":
consent_privacy_terms = True

api_json = {
ACCT_FNAME_KEY: body['first_name'],
ACCT_LNAME_KEY: body['last_name'],
Expand All @@ -847,7 +812,8 @@ def post_create_account(*, body=None):
ACCT_ADDR_POST_CODE_KEY: body['post_code'],
ACCT_ADDR_COUNTRY_CODE_KEY: body['country_code']
},
ACCT_LANG_KEY: body[LANG_KEY]
ACCT_LANG_KEY: body[LANG_KEY],
ACCT_TERMS_KEY: consent_privacy_terms
}

has_error, accts_output, _ = \
Expand Down Expand Up @@ -1001,7 +967,7 @@ def get_consent_page(*, account_id=None):
if has_error:
return consent_output

form_type = "source"
form_type = "data"
reconsent = False

return _render_with_defaults(
Expand Down Expand Up @@ -1150,10 +1116,9 @@ def get_fill_source_survey(*,
previous_survey = {
"survey_name": survey_names[previous_template_id],
"survey_template_id": previous_template_id,
"est_minutes": SURVEY_INFO.get(
previous_template_id
)['est_minutes'],
"icon": SURVEY_INFO.get(previous_template_id)['icon']
"est_minutes":
SURVEY_INFO[previous_template_id]['est_minutes'],
"icon": SURVEY_INFO[previous_template_id]['icon']
}
else:
previous_survey = None
Expand All @@ -1164,16 +1129,19 @@ def get_fill_source_survey(*,
next_survey = {
"survey_name": survey_names[next_template_id],
"survey_template_id": next_template_id,
"est_minutes": SURVEY_INFO.get(
next_template_id
)['est_minutes'],
"icon": SURVEY_INFO.get(next_template_id)['icon']
"est_minutes":
SURVEY_INFO[next_template_id]['est_minutes'],
"icon": SURVEY_INFO[next_template_id]['icon']
}
else:
next_survey = None

survey_question_count = 0
# Add "skip" link to the field labels
# Add "skip" link to the label for every question. We set it up as a
# floating span such that it's consistently positioned, relative to
# questions, without obscuring any question text.
# Additionally, we inject numbers into the question labels, with
# triggered questions receiving sequential letters.
for group in survey_output['survey_template_text']['groups']:
ctr = 0
trig_ctr = 0
Expand Down Expand Up @@ -1211,6 +1179,8 @@ def post_ajax_fill_local_source_survey(*, account_id=None, source_id=None,
survey_template_id=None, body=None,
target=None):
# Private API has no ability to handle a survey submission without
cassidysymons marked this conversation as resolved.
Show resolved Hide resolved
# any questions in the body. Therefore, if the body is null, we're going
# to bypass submitting to the API.
if len(body) > 0:
has_error, surveys_output, _ = ApiRequest.post(
"/accounts/%s/sources/%s/surveys" % (account_id, source_id),
Expand Down Expand Up @@ -1418,7 +1388,7 @@ def get_source(*, account_id=None, source_id=None):
return consent_required

if consent_required["result"]:
return render_consent_page(account_id, source_id, "source")
return render_consent_page(account_id, source_id, "data")

# Retrieve the source
has_error, source_output, _ = ApiRequest.get(
Expand Down Expand Up @@ -1485,16 +1455,22 @@ def get_source(*, account_id=None, source_id=None):
return survey_output

template['date_last_taken'] = survey_output['date_last_taken']

# For local surveys, the answered value is used to control the
# progress indicator and is expressed in degrees. A survey with no
# answered questions is bumped up to 2 degrees, so that we're not
# displaying a totally blank circle. All partially or fully completed
# surveys are calculated as a percentage of 360 degrees.
if survey_output['percentage_completed'] == 0.0:
template['answered'] = 2
cassidysymons marked this conversation as resolved.
Show resolved Hide resolved
else:
template['answered'] = 360 * survey_output['percentage_completed']

for template in local_surveys + remote_surveys:
template_id = template['survey_template_id']
template['description'] = SURVEY_INFO.get(template_id)['description']
template['est_minutes'] = SURVEY_INFO.get(template_id)['est_minutes']
template['icon'] = SURVEY_INFO.get(template_id)['icon']
template['description'] = SURVEY_INFO[template_id]['description']
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
"""
Expand Down Expand Up @@ -1561,10 +1537,10 @@ def get_kits(*, account_id=None, source_id=None):

is_human = source_output['source_type'] == Source.SOURCE_TYPE_HUMAN

samples_output = [translate_sample(s) for s in samples_output]
samples = [translate_sample(s) for s in samples_output]

kits = defaultdict(list)
for s in samples_output:
for s in samples:
if s['sample_site'] == '' or s['sample_datetime'] == '':
wasade marked this conversation as resolved.
Show resolved Hide resolved
s['css_class'] = "sample-needs-info"
s['alert_icon'] = "info-orange.svg"
Expand All @@ -1574,14 +1550,12 @@ def get_kits(*, account_id=None, source_id=None):

kits[s['kit_id']].append(s)

samples_output = kits

return _render_with_defaults(
'kits.jinja2',
account_id=account_id,
source_id=source_id,
is_human=is_human,
samples=samples_output,
kits=kits,
source_name=source_output['source_name'],
fundrazr_url=SERVER_CONFIG["fundrazr_url"],
account_country=account_country,
Expand Down Expand Up @@ -1680,23 +1654,39 @@ def get_consents(*, account_id=None, source_id=None):
return source_output

# Retrieve the latest signed consents
has_error, consents_output, _ = ApiRequest.get(
'/accounts/%s/sources/%s/signed_consents' %
(account_id, source_id)
has_error, data_consent, _ = ApiRequest.get(
'/accounts/%s/sources/%s/signed_consent/%s' %
(account_id, source_id, "data")
)
if has_error:
return data_consent

has_error, biospecimen_consent, _ = ApiRequest.get(
'/accounts/%s/sources/%s/signed_consent/%s' %
(account_id, source_id, "biospecimen")
)
if has_error:
return consents_output
if has_error == 404:
biospecimen_consent = None
else:
return biospecimen_consent

return _render_with_defaults(
'consents.jinja2',
account_id=account_id,
source_id=source_id,
source_name=source_output['source_name'],
consents=consents_output
data_consent=data_consent,
biospecimen_consent=biospecimen_consent
)


def get_consent_view(*, account_id=None, source_id=None, consent_id=None):
def get_consent_view(*, account_id=None, source_id=None, consent_type=None):
endpoint = SERVER_CONFIG["endpoint"]
relative_post_url = _make_acct_path(account_id,
suffix="create_human_source")
post_url = endpoint + relative_post_url

# Retrieve the source
has_error, source_output, _ = ApiRequest.get(
'/accounts/%s/sources/%s' %
Expand All @@ -1705,29 +1695,37 @@ def get_consent_view(*, account_id=None, source_id=None, consent_id=None):
return source_output

# Retrieve the latest signed consents
has_error, consents_output, _ = ApiRequest.get(
'/accounts/%s/sources/%s/signed_consents' %
(account_id, source_id)
has_error, consent_output, _ = ApiRequest.get(
'/accounts/%s/sources/%s/signed_consent/%s' %
(account_id, source_id, consent_type)
)
if has_error:
return consents_output
return consent_output

has_error, consent_assets, _ = ApiRequest.get(
"/accounts/{0}/consent".format(account_id),
params={"consent_post_url": post_url})

consent_output['date_time'] = flask_babel.format_datetime(
datetime.strptime(consent_output['date_time'], '%Y-%m-%dT%H:%M:%S.%f%z'),
'medium',
False
)

return _render_with_defaults(
'consents.jinja2',
'signed_consent.jinja2',
account_id=account_id,
source_id=source_id,
source_age=source_output['consent']['age_range'],
source_name=source_output['source_name'],
consents=consents_output
consent=consent_output,
tl=consent_assets
)


def get_consent_download(*, account_id=None, source_id=None, consent_id=None):
return True


# Note: ideally this would be represented as a DELETE, not as a POST
# However, it is used as a form submission action, and HTML forms do not
# support delete as an action
# Note: ideally this would be represented as a DELETE, not as a GET
# However, it is used as a link, and HTML links do not support a DELETE
# action
@prerequisite([SOURCE_PREREQS_MET])
def get_remove_source(*,
account_id=None,
Expand Down Expand Up @@ -2114,7 +2112,7 @@ def post_claim_samples(*, account_id=None, source_id=None, body=None,
return render_consent_page(
account_id,
source_id,
"Biospecimen",
"biospecimen",
sample_ids=sample_ids
)

Expand Down
Loading