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

Update copy on results pages, rearrange buttons, responsive titles and error message, refactor assessment logic #39

Merged
merged 22 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f85f80a
add notes about updating questions to readme
mooserson Aug 14, 2023
7e2f393
move screener assessment to utility file
mooserson Aug 14, 2023
10a2bc0
refactor short_form diagnosis func out to util file
mooserson Aug 14, 2023
e630ee5
removes empty lines
mooserson Aug 14, 2023
df163d0
adds comment
mooserson Aug 14, 2023
9fb7432
updates screener diag messages and hides continue from negative
mooserson Aug 14, 2023
4d552c9
fix typo
mooserson Aug 14, 2023
8a0336a
adjusts screener graph styling to try and better fit in view
mooserson Aug 14, 2023
798aa06
update continue message text
mooserson Aug 14, 2023
678cc04
updates screener results page style
mooserson Aug 14, 2023
cbc4adb
renames helper function, removes unused lines
mooserson Aug 14, 2023
ad46d9a
removes more unused code
mooserson Aug 14, 2023
319da8d
removes unused import
mooserson Aug 14, 2023
1114c7f
adds list of question comments
mooserson Aug 14, 2023
50fd119
removes hover effects for disabled button
mooserson Aug 14, 2023
327f465
adjusts nav button styling so mobile results pages fit in width
mooserson Aug 15, 2023
4bc8216
updates continue message for graph2 (less redundant) and switches cas…
mooserson Aug 15, 2023
582ca61
removes white space
mooserson Aug 15, 2023
ac1fa96
fixes error message and button display esp. for mobile
mooserson Aug 15, 2023
ab95527
updates question titles to not resize things on mobile
mooserson Aug 15, 2023
4131c25
adjusts reponsiveness of result buttons
mooserson Aug 15, 2023
57aa30a
removes instructions from top of single-questions and adds to legend
mooserson Aug 15, 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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ MacOS:
###NB
If you change the routes please update the route-order "page_flow" tuple in 'utils/back_function.py' so the back button works correctly
+ the routes.txt file which shows the route function and paired route

To make changes to question pages there is one partial for all the F/S questions and then the single-question items are less templated so need to be modified directly (dsq/viral.html and short_form/reduction.html)
72 changes: 72 additions & 0 deletions utils/screener_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from flask import render_template, session
import plotly.graph_objects as go
import json
import plotly.utils

# screener questions
# 'fatigue13c', 'remember36c', 'minimum17c', 'unrefreshed19c',

def screener_diagnose():
# we *25 to scale 4pt scale to 100 pt scale
fatiguescore = fit_domain_score('fatiguescore')
pemscore = fit_domain_score('pemscore')
sleepscore = fit_domain_score('sleepscore')
cogscore = fit_domain_score('cogscore')

responses = [fatiguescore, pemscore, sleepscore, cogscore]
iomfatiguecheck = "No"
iompemcheck = "No"
iomsleepcheck = "No"
iomcogcheck = "No"
dx_met = False

if session['fatiguescoref'] >= 2 and session['fatiguescores'] >= 2:
iomfatiguecheck = "Yes"
if session['minexf'] >= 2 and session['minexs'] >= 2:
iompemcheck = "Yes"
if session['sleepf'] >= 2 and session['sleeps'] >= 2:
iomsleepcheck = "Yes"
if session['rememberf'] >= 2 and session['remembers'] >= 2:
iomcogcheck = "Yes"

screen_message = 'Your responses to the screener questions are scored below. <br> Scores range from 0 to 100, with higher scores indicate more frequent and severe problems.'
if iomfatiguecheck == "Yes" or iompemcheck == "Yes" or iomsleepcheck == "Yes" or iomcogcheck == "Yes":
dx_met = True

composite_scores = responses
categories = ['Fatigue', 'Post-exertional malaise', 'Sleep problems',
'Cognitive problems']

colors = ['#56A8A0' for score in composite_scores]

# composite f/s score graph
fig = go.Figure(
data=[
go.Bar(y=composite_scores, x=categories, name="Your scores", marker=dict(color=colors))],
layout=go.Layout(
title=go.layout.Title(text='Your Summary Score', x=0.5),
showlegend=True,
legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1
),
autosize=True
)
)
fig.update_layout(
yaxis_title='Combined Frequency and Severity Scores',
xaxis_title='Symptom Domains'
)
fig.update_yaxes(range=[0, 100], dtick=25, titlefont=dict(size=15))
fig.update_xaxes(tickfont=dict(size=13), titlefont=dict(size=15))
graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
return render_template("results/graph.html",
graphJSON = graphJSON,
screen_message = screen_message,
dx_met = dx_met)

def fit_domain_score(session_var_string):
return 1 if session[session_var_string] == 0 else session[session_var_string] * 25
187 changes: 187 additions & 0 deletions utils/short_form_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
from flask import render_template, session
import numpy as np
import plotly.graph_objects as go
import json
import plotly.utils

# short form questions:
# 'fatigue13c', 'soreness15c', 'minimum17c', 'unrefreshed19c',
# 'musclepain25c', 'bloating29c', 'remember36c', 'difficulty37c',
# 'bowel46c', 'unsteady48c', 'limbs56c', 'hot58c', 'flu65c','smells66c'

def short_form_diagnose():
import domainScores as ds

fatiguescore = (int(session["fatiguescoref"]) + int(session["fatiguescores"])) / 2
pemscore = (int(session["minexf"]) + int(session["minexs"]) + int(session['soref']) + int(session['sores'])) / 4
sleepscore = (int(session["sleepf"]) + int(session["sleeps"])) / 2
cogscore = (int(session["rememberf"]) + int(session["remembers"]) + int(session['attentionf']) +
int(session['attentions'])) / 4
painscore = (int(session['musclef']) + int(session['muscles'])) / 2
gastroscore = (int(session['bloatf']) + int(session['bloats']) + int(session['bowelf']) +
int(session['bowels'])) / 4
orthoscore = (int(session['unsteadyf']) + int(session['unsteadys'])) / 2
circscore = (int(session['limbsf']) + int(session['limbss']) + int(session['hotf']) + int(session['hots'])) / 4
immunescore = (int(session['fluf']) + int(session['flus'])) / 2
neuroenscore = (int(session['smellf']) + int(session['smells'])) / 2

user_scores = [fatiguescore, pemscore, sleepscore, cogscore, painscore, gastroscore, orthoscore, circscore,
immunescore, neuroenscore]

df = ds.sdf
mecfs = df[(df['dx'] == 1)]
cfsdomains = np.mean(mecfs.iloc[:, 110:120], axis=0)

# This assesses the IOM Criteria
responses = [fatiguescore, pemscore, sleepscore, cogscore]
iomfatiguecheck = "No"
iomreductioncheck = "No"
iompemcheck = "No"
iomsleepcheck = "No"
iomcogcheck = "No"
iomorthocheck = "No"

if int(session['fatiguescoref']) >= 2 and int(session['fatiguescores']) >= 2:
iomfatiguecheck = "Yes"
if int(session['reduction']) == 1:
iomreductioncheck = "Yes"
if (int(session['minexf']) >= 2 and int(session['minexs'] >= 2) or (
int(session['soref']) >= 2 and int(session['sores']) >= 2)):
iompemcheck = "Yes"
if int(session['sleepf']) >= 2 and int(session['sleeps']) >= 2:
iomsleepcheck = "Yes"
if (int(session['rememberf']) >= 2 and int(session['remembers']) >= 2 ) or (
int(session['attentionf']) >= 2 and int(session['attentions']) >= 2):
iomcogcheck = "Yes"
if int(session['unsteadyf']) >= 2 and int(session['unsteadys']) >= 2:
iomorthocheck = "Yes"

if iomfatiguecheck == "Yes" and iomreductioncheck == "Yes" and iompemcheck == "Yes" and iomsleepcheck == "Yes" and (iomcogcheck == "Yes" or iomorthocheck == "Yes"):
iom_msg = "Your responses suggest you meet the IOM Criteria for ME/CFS. To improve the accuracy" \
" of your assessment with more questions continue to the next section."
iomdxcheck = "Met"

else:
iom_msg = "Your responses do not meet the IOM Criteria for ME/CFS."
iomdxcheck = "Not met"

# This assesses the Canadian Consensus Criteria, one of the three major case definitions we use

ccc_dx = False

if int(session['fatiguescoref']) >= 2 and int(session['fatiguescores']) >= 2:
ccc_fatigue = 1
ccc_fatiguecheck = "Yes"
else:
ccc_fatigue = 0
ccc_fatiguecheck = "No"
if int(session['reduction']) == 1:
ccc_reduction = 1
ccc_reductioncheck = "Yes"
else:
ccc_reduction = 0
ccc_reductioncheck = "No"
if int(session['musclef']) >= 2 and int(session['muscles']) >= 2:
ccc_pain = 1
ccc_paincheck = "Yes"
else:
ccc_pain = 0
ccc_paincheck = "No"
if int(session['sleepf']) >= 2 and int(session['sleeps']) >= 2:
ccc_sleep = 1
ccc_sleepcheck = "Yes"
else:
ccc_sleep = 0
ccc_sleepcheck = "No"
if (int(session['minexf']) >= 2 and int(session['minexs']) >= 2) or (
int(session['soref']) >= 2 and int(session['sores']) >= 2):
ccc_pem = 1
ccc_pemcheck = "Yes"
else:
ccc_pem = 0
ccc_pemcheck = "No"
if (int(session['rememberf']) >= 2 and int(session['remembers']) >= 2) or (
int(session['attentionf']) >= 2 and int(session['attentions']) >= 2):
ccc_cog = 1
ccc_cogcheck = "Yes"
else:
ccc_cog = 0
ccc_cogcheck = "No"

if (int(session['unsteadyf']) >= 2 and int(session['unsteadys']) >= 2) or (
int(session['bowelf']) >= 2 and int(session['bowels']) >= 2) or (
int(session['bloatf']) >= 2 and int(session['bloats']) >= 2):
ccc_auto = 1
ccc_autocheck = "Yes"
else:
ccc_auto = 0
ccc_autocheck = "No"
if (int(session['limbsf']) >= 2 and int(session['limbss']) >= 2) or (
int(session['hotf']) >= 2 and int(session['hots']) >= 2):
ccc_neuro = 1
ccc_neurocheck = "Yes"
else:
ccc_neuro = 0
ccc_neurocheck = "No"
if (int(session['fluf']) >= 2 and int(session['flus']) >= 2) or (
int(session['smellf']) >= 2 and int(session['smells']) >= 2):
ccc_immune = 1
ccc_immunecheck = "Yes"
else:
ccc_immune = 0
ccc_immunecheck = "No"
ccc_poly = np.sum([ccc_auto, ccc_neuro, ccc_immune])
# most of the symptoms are required, but there is one polythetic criteria, shown here by ccc_poly
if np.sum([ccc_fatigue, ccc_reduction, ccc_pem, ccc_sleep, ccc_pain, ccc_cog]) >= 6 and ccc_poly >= 2:
ccc_dx = "Met"
ccc_msg = "Your responses suggest that you meet the Canadian Consensus Criteria for ME/CFS. " \
"To improve the accuracy of your assessment with more questions continue to the next section."
else:
ccc_dx = "Not met"
ccc_msg = "Your responses do not meet the Canadian Consensus Criteria for ME/CFS."

# categories = [*feature_list, feature_list[0]]
categories = ['Fatigue', 'PEM', 'Sleep', 'Cognitive Impairment', 'Pain', 'Gastro Problems',
'Orthostatic Intolerance', 'Circulatory Problems', 'Immune System', 'Neuroendocrine Problems']

# converts scores to 100pt scale
user_scores = np.multiply(user_scores, 25).tolist()
cfsdomains = np.multiply(cfsdomains, 25).tolist()

# diagnostic message ccc OR iom
dx_met = False
if ccc_dx == "Met" or iomdxcheck == "Met":
short_form_message = "Based on your responses there is a chance you might have MECFS. <br> Please continue to the next section for a more accurate assessment."
dx_met = True
else:
short_form_message = "Based on your responses it does not appear you have MECFS."

# Creates a figure using the plotly library, which can be dynamically embedded in the HTML page
fig = go.Figure(
data=[
go.Bar(y=user_scores, x=categories, name="Your scores"),
go.Bar(y=cfsdomains, x=categories, name="Average ME/CFS scores")],
layout=go.Layout(
title=go.layout.Title(text='Your scores compared with our dataset of <br>'
'over 2,400 participants with ME/CFS', x=0.5),
showlegend=True, legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1)))
fig.update_layout(yaxis_title='Averaged Frequency and Severity Scores',
xaxis_title='Symptom Domains')
fig.update_yaxes(range=[0, 100], dtick=25)
# This converts to figure fig to a JSON object so it can be dynamically rendered with javascript on the page
graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)

return render_template("results/graph2.html", graphJSON=graphJSON, ccc_msg=ccc_msg, ccc_fatiguecheck=ccc_fatiguecheck,
ccc_pemcheck=ccc_pemcheck, ccc_paincheck=ccc_paincheck, ccc_sleepcheck=ccc_sleepcheck,
ccc_cogcheck=ccc_cogcheck, ccc_autocheck=ccc_autocheck, ccc_immunecheck=ccc_immunecheck,
ccc_neurocheck=ccc_neurocheck, ccc_dx=ccc_dx, ccc_reductioncheck=ccc_reductioncheck, ccc_poly=ccc_poly,
iomfatiguecheck=iomfatiguecheck, iomreductioncheck=iomreductioncheck,
iompemcheck=iompemcheck, iomdxcheck=iomdxcheck, iom_msg=iom_msg,
iomsleepcheck=iomsleepcheck, iomcogcheck=iomcogcheck, iomorthocheck=iomorthocheck,
short_form_message=short_form_message,dx_met=dx_met
)
Loading