diff --git a/microsetta_interface/implementation.py b/microsetta_interface/implementation.py index 901aed32..e5ea5e87 100644 --- a/microsetta_interface/implementation.py +++ b/microsetta_interface/implementation.py @@ -13,6 +13,7 @@ from datetime import datetime import base64 import functools +import re from string import ascii_lowercase from microsetta_interface.model_i18n import translate_source, \ translate_sample, translate_survey_template, EN_US_KEY, LANGUAGES, \ @@ -389,6 +390,8 @@ class Source: OTHER_ID ] +DAKLAPACK_KIT_RE = re.compile(r"DMK[234689ACDEFHJKMNPRTVWXY]{6}") + def _render_with_defaults(template_name, **context): defaults = {} @@ -1092,14 +1095,18 @@ def get_account(*, account_id=None): # Determine if the source/profile has any action items. This framework # will evolve over time. + non_human_sources = False for s in sources: alerts = 0 + if s['source_type'] == Source.SOURCE_TYPE_HUMAN: - need_reconsent_d = check_current_consent( - account_id, s[SOURCE_ID], "data" - ) - if need_reconsent_d: - alerts += 1 + need_reconsent_d = check_current_consent( + account_id, s[SOURCE_ID], "data" + ) + if need_reconsent_d: + alerts += 1 + else: + non_human_sources = True s['alerts'] = alerts @@ -1113,7 +1120,8 @@ def get_account(*, account_id=None): return _render_with_defaults('account_overview.jinja2', account=account, sources=sources, - japan_user=japan_user) + japan_user=japan_user, + non_human_sources=non_human_sources) @prerequisite([ACCT_PREREQS_MET]) @@ -1396,11 +1404,6 @@ def get_fill_source_survey(*, ctr = 0 trig_ctr = 0 for field in group['fields']: - if need_reconsent: - # If the user has not agreed to the current consent, we - # disable all of the fields. - field['disabled'] = True - if "triggered_by" in field: field['label'] = str(ctr) + ascii_lowercase[trig_ctr]\ + ". " + field['label'] @@ -1410,10 +1413,22 @@ def get_fill_source_survey(*, ctr += 1 survey_question_count += 1 field['label'] = str(ctr) + ". " + field['label'] - field['label'] = ''\ - + gettext('SKIP')\ - + '' + field['label'] + + if need_reconsent: + # If the user has not agreed to the current consent, we + # disable all of the fields. + field['disabled'] = True + # And we remove the onClick action from the Skip/Display + # text, but leave it to reflect their last response + field['label'] = '' \ + + gettext('SKIP') \ + + '' + field['label'] + else: + field['label'] = ''\ + + gettext('SKIP')\ + + '' + field['label'] return _render_with_defaults( "survey.jinja2", @@ -1481,14 +1496,16 @@ def get_fill_vioscreen_remote_sample_survey(*, account_id=None, source_id=None, sample_id=None, - registration_code=None): + registration_code=None, + vio_id=None): suffix = "vspassthru" redirect_url = SERVER_CONFIG["endpoint"] + \ _make_source_path(account_id, source_id, suffix=suffix) params = { 'survey_redirect_url': redirect_url, 'vioscreen_ext_sample_id': sample_id, - 'registration_code': registration_code + 'registration_code': registration_code, + 'vio_id': vio_id } has_error, survey_output, _ = ApiRequest.get( '/accounts/%s/sources/%s/survey_templates/%s' % @@ -1721,7 +1738,8 @@ def get_source(*, account_id=None, source_id=None): if survey['survey_template_type'] == "local": local_surveys.append(survey) else: - if survey['survey_template_id'] != VIOSCREEN_ID: + if survey['survey_template_id'] != VIOSCREEN_ID and\ + survey['survey_template_id'] != POLYPHENOL_FFQ_ID: if survey['survey_template_id'] == SPAIN_FFQ_ID and\ spain_user is False: continue @@ -1832,17 +1850,23 @@ def get_kits(*, account_id=None, source_id=None, check_survey_date=False): for sample in samples_output: if sample['sample_datetime'] is not None: dt = datetime.fromisoformat(sample['sample_datetime']) + sample['ts_for_sort'] = dt # rebase=True - show in user's locale, rebase=False, UTC (I think?) sample['sample_datetime'] = flask_babel.format_datetime( dt, format=None, # Use babel default (short/medium/long/full) rebase=False) + else: + # We just need a sort value for samples without a collection date + # and we want it to filter to the top when we sort by date desc. + sample['ts_for_sort'] = datetime.fromisoformat("9999-12-31") is_human = source_output['source_type'] == Source.SOURCE_TYPE_HUMAN samples = [translate_sample(s) for s in samples_output] kits = defaultdict(list) + kits_ts = {} for s in samples: if s['sample_site'] == '' or s['sample_datetime'] == '': s['css_class'] = "sample-needs-info" @@ -1852,6 +1876,18 @@ def get_kits(*, account_id=None, source_id=None, check_survey_date=False): s['alert_icon'] = "green_checkmark.svg" kits[s['kit_id']].append(s) + if s['kit_id'] in kits_ts: + if s['ts_for_sort'] > kits_ts[s['kit_id']]: + kits_ts[s['kit_id']] = s['ts_for_sort'] + else: + kits_ts[s['kit_id']] = s['ts_for_sort'] + + sorted_kits = {} + sorted_kits_ts = dict( + sorted(kits_ts.items(), key=lambda x: x[1], reverse=True) + ) + for kit_id in sorted_kits_ts.keys(): + sorted_kits[kit_id] = kits[kit_id] profile_has_samples = _check_if_source_has_samples(account_id, source_id) @@ -1879,7 +1915,7 @@ def get_kits(*, account_id=None, source_id=None, check_survey_date=False): account_id=account_id, source_id=source_id, is_human=is_human, - kits=kits, + kits=sorted_kits, source_name=source_output['source_name'], fundrazr_url=SERVER_CONFIG["fundrazr_url"], account_country=account_country, @@ -1928,6 +1964,9 @@ def get_nutrition(*, account_id=None, source_id=None, new_ffq_code=None): return vioscreen_output profile_has_samples = _check_if_source_has_samples(account_id, source_id) + need_reconsent_data = check_current_consent( + account_id, source_id, "data" + ) return _render_with_defaults( 'nutrition.jinja2', @@ -1940,7 +1979,8 @@ def get_nutrition(*, account_id=None, source_id=None, new_ffq_code=None): nutrition_tab_whitelist=NUTRITION_TAB_WHITELIST, new_ffq_code=new_ffq_code, profile_has_samples=profile_has_samples, - has_basic_info=has_basic_info + has_basic_info=has_basic_info, + need_reconsent_data=need_reconsent_data ) @@ -2011,9 +2051,7 @@ def get_consents(*, account_id=None, source_id=None): ) if has_error: if has_error == 404: - # If historical users who haven't re-consented try to view signed - # consents, we redirect them to the consent page - return render_consent_page(account_id, source_id, "data") + data_consent = None else: return data_consent @@ -2074,6 +2112,11 @@ def get_consent_view(*, account_id=None, source_id=None, consent_type=None): False ) + if consent_type == "biospecimen": + consent_type_display = "Biospecimen" + else: + consent_type_display = "Survey" + return _render_with_defaults( 'signed_consent.jinja2', account_id=account_id, @@ -2081,7 +2124,8 @@ def get_consent_view(*, account_id=None, source_id=None, consent_type=None): source_age=source_output['consent']['age_range'], source_name=source_output['source_name'], consent=consent_output, - tl=consent_assets + tl=consent_assets, + consent_type_display=consent_type_display ) @@ -2480,12 +2524,16 @@ def admin_barcode_search_query_qiita(body): def get_ajax_check_kit_valid(kit_name): + if DAKLAPACK_KIT_RE.match(kit_name.upper()): + kit_name = kit_name.upper() kit, error, _ = _get_kit(kit_name) result = True if error is None else error return flask.jsonify(result) def get_ajax_list_kit_samples(kit_name): + if DAKLAPACK_KIT_RE.match(kit_name.upper()): + kit_name = kit_name.upper() kit, error, code = _get_kit(kit_name) result = kit if error is None else error return flask.jsonify(result), code diff --git a/microsetta_interface/routes.yaml b/microsetta_interface/routes.yaml index 4d56acff..91c98c8a 100644 --- a/microsetta_interface/routes.yaml +++ b/microsetta_interface/routes.yaml @@ -792,6 +792,11 @@ paths: schema: type: string nullable: true + - in: query + name: vio_id + schema: + type: string + nullable: true responses: '200': description: Take the vioscreen remote survey diff --git a/microsetta_interface/static/css/minimal_interface.css b/microsetta_interface/static/css/minimal_interface.css index aadd1ee1..11f35212 100644 --- a/microsetta_interface/static/css/minimal_interface.css +++ b/microsetta_interface/static/css/minimal_interface.css @@ -257,6 +257,11 @@ h2.account-h2 { h2.profile-card-h2 { color: var(--tmi-blue); + height: 40px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 90%; } h3 { @@ -458,7 +463,7 @@ p.tmi-content { @media (max-width: 575.98px) { .card-survey { - height: 230px; + height: 280px; } .card-survey-external { @@ -683,8 +688,8 @@ h1.profile-h1 { color: var(--tmi-gray-80); } -@media (max-width: 575.98px) { - .survey-info-row { +@media (max-width: 359.98px) { + .survey-info-row > .text-end { display: none; } } @@ -894,6 +899,16 @@ li.active-profile { text-decoration: underline; } +.survey-skip-dis { + float: right; + margin-right: 10px; + color: var(--tmi-gray-80) !important; + font-weight: 400 !important; + font-size: 12px !important; + line-height: 20px !important; + text-decoration: none; +} + .skip-text { color: var(--tmi-gray-80) !important; font-weight: 400 !important; @@ -1267,6 +1282,10 @@ input.barcode-checkbox[type=checkbox]:checked+label { margin-left: auto; } +.barcode-col > .btn { + display: inline; +} + .barcode-text { color: var(--tmi-blue); font-weight: 700; diff --git a/microsetta_interface/templates/account_overview.jinja2 b/microsetta_interface/templates/account_overview.jinja2 index 9e3f8d6b..3d2f9d19 100644 --- a/microsetta_interface/templates/account_overview.jinja2 +++ b/microsetta_interface/templates/account_overview.jinja2 @@ -49,6 +49,9 @@
{% if sources|length > 0 %}

{{ _('Active Profiles') }}

+ {% if non_human_sources %} +

{{ _('Please note: animal and environmental profiles are currently unavailable.') }}

+ {% endif %}
{% for source in sources %}
@@ -66,13 +69,31 @@
{% else %} + {% if source.source_type == "animal" %} +
+ +

{{ source.source_name|e}}

+ + {{ _('Unavailable') }} + +
+ {% elif source.source_type == "environmental" %} +
+ +

{{ source.source_name|e}}

+ + {{ _('Unavailable') }} + +
+ {% else %}
-

{{ source.source_name|e}}

+

{{ source.source_name|e}}

{{ _('Go to My Profile') }}
+ {% endif %} {% endif %}
{% endfor %} @@ -106,6 +127,13 @@

{{ _('Coming Soon') }}

+ {% if admin_mode %} +
+
+

{{ _('Account Details') }}

+
+
+ {% endif %} diff --git a/microsetta_interface/templates/consents.jinja2 b/microsetta_interface/templates/consents.jinja2 index 6f248966..0a701e73 100644 --- a/microsetta_interface/templates/consents.jinja2 +++ b/microsetta_interface/templates/consents.jinja2 @@ -24,12 +24,18 @@ {% endblock %} {% block content %}
+ {% if data_consent is none and biospecimen_consent is none %} + + {% endif %}

{{ _('Consent Documents') }}

+ {% if data_consent is not none %}
@@ -38,7 +44,7 @@

{{ _('Survey') }}

@@ -46,6 +52,7 @@
+ {% endif %} {% if biospecimen_consent is not none %}
@@ -55,7 +62,7 @@

{{ _('Biospecimen') }}

diff --git a/microsetta_interface/templates/kits.jinja2 b/microsetta_interface/templates/kits.jinja2 index 798c6bde..c546f556 100644 --- a/microsetta_interface/templates/kits.jinja2 +++ b/microsetta_interface/templates/kits.jinja2 @@ -149,13 +149,15 @@ } } - function updateButtonState(kit_id_value) { - if(kit_id_value != "") { + /* + function updateButtonState() { + if(document.list_kit_form.kit_name.value != "") { document.getElementById("kit_id_button").disabled = false; } else { document.getElementById("kit_id_button").disabled = true; } } + */ function openKitPanel() { document.getElementById('add_kit_container').style.display = ''; @@ -282,12 +284,12 @@

- +
- +
@@ -302,7 +304,8 @@ {{ _('2. Add the date and time of sample collection and select the sample type taken.') }}
{{ _('Important') }}:
{{ _('The sample cannot be processed in the lab until this information is complete.') }}
-{{ _('Keep a record of these details. The barcode is needed to resolve any issues you may have with your sample collection.') }}" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="top" title="">{{ _('What is the barcode and why is it important?') }}


+{{ _('Keep a record of these details. The barcode is needed to resolve any issues you may have with your sample collection.') }}
+{{ _('If the barcode(s) listed do not match the barcode(s) on your collection device(s), please contact us at microsetta@ucsd.edu.') }}" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="top" title="">{{ _('What is the barcode and why is it important?') }}


diff --git a/microsetta_interface/templates/new_participant.jinja2 b/microsetta_interface/templates/new_participant.jinja2 index 70cce74d..3bd7d573 100644 --- a/microsetta_interface/templates/new_participant.jinja2 +++ b/microsetta_interface/templates/new_participant.jinja2 @@ -11,6 +11,9 @@ $("#7-12-div").hide(); $("#13-17-div").hide(); $("#18-plus-div").hide(); + {% if age_range == 'legacy' %} + $("#legacy-cancel-div").hide(); + {% endif %} $("#"+ radio.value +"-div").show(); } @@ -514,6 +517,16 @@
+{% if age_range == 'legacy' %} +
+
+
+ +
+
+
+{% endif %} + diff --git a/microsetta_interface/templates/reports.jinja2 b/microsetta_interface/templates/reports.jinja2 index 902b3244..fb3377b2 100644 --- a/microsetta_interface/templates/reports.jinja2 +++ b/microsetta_interface/templates/reports.jinja2 @@ -42,6 +42,12 @@ }); } + function disable_link(link_obj) { + link_obj.style.opacity = .65; + link_obj.style.pointerEvents = "none"; + return true; + } + // Wait for the DOM to be ready $(document).ready(function(){ {% for sample in samples %} diff --git a/microsetta_interface/templates/sample.jinja2 b/microsetta_interface/templates/sample.jinja2 index d46499bd..f0c32d13 100644 --- a/microsetta_interface/templates/sample.jinja2 +++ b/microsetta_interface/templates/sample.jinja2 @@ -123,7 +123,7 @@ {% else %} defaultTime: '{{ sample.sample_time }}', {% endif %} - startTime: '8', + startTime: '0', dynamic: false, dropdown: true, scrollbar: true, diff --git a/microsetta_interface/templates/signed_consent.jinja2 b/microsetta_interface/templates/signed_consent.jinja2 index fd1d3732..c88c0705 100644 --- a/microsetta_interface/templates/signed_consent.jinja2 +++ b/microsetta_interface/templates/signed_consent.jinja2 @@ -1,14 +1,19 @@ {% extends "sitebase.jinja2" %} {% set page_title = _("Consent") %} -{% set show_breadcrumbs = False %} -{% set hide_header = True %} -{% set hide_footer = True %} +{% set show_breadcrumbs = True %} {% block head %} {% endblock %} +{% block breadcrumb %} + + + + +{% endblock %} {% block content%} +
diff --git a/microsetta_interface/templates/sitebase.jinja2 b/microsetta_interface/templates/sitebase.jinja2 index 90b3676a..bd96202d 100644 --- a/microsetta_interface/templates/sitebase.jinja2 +++ b/microsetta_interface/templates/sitebase.jinja2 @@ -101,6 +101,7 @@
  • {{ _('Skipping a question clears your selected answer and hides the options.') }}
  • {{ _('Click') }} "{{ _('DISPLAY') }}" {{ _('to bring back the hidden response options.') }}
  • {{ _('If you want to reset an incorrect answer quickly, click') }} "{{ _('SKIP') }}" {{ _('then') }} "{{ _('DISPLAY') }}".
  • +
  • {{ _('Skipping a question logs a blank response and counts towards completing a survey.') }}
  • @@ -135,7 +136,7 @@ {% endif %} {% if not admin_mode and not hide_header %}