Birger Moell
Updated iws
b12bd6f
import streamlit as st
import numpy as np
import scipy.stats as stats
from fpdf import FPDF
import base64
import os
from plots import test_profile
import matplotlib.pyplot as plt
from PIL import Image
test_dict = {
"animal": {
"low_age_low_education": {
"mean": 21.0,
"std": 7.0
}, "low_age_high_education": {
"mean": 22.4,
"std": 6.8
}, "high_age_low_education": {
"mean": 22.1,
"std": 5.7
}, "high_age_high_education": {
"mean": 25.6,
"std": 5.6
}
}, "verb": {
"low_age_low_education": {
"mean": 17.6,
"std": 4.3
}, "low_age_high_education": {
"mean": 20.5,
"std": 5.4
}, "high_age_low_education": {
"mean": 16.7,
"std": 6.1
}, "high_age_high_education": {
"mean": 22.7,
"std": 5.1
}
}, "repetition": {
"low_age_low_education": {
"mean": 24.8,
"std": 4.8
}, "low_age_high_education": {
"mean": 25.9,
"std": 3.7
}, "high_age_low_education": {
"mean": 24.0,
"std": 4.9
}, "high_age_high_education": {
"mean": 25.8,
"std": 3.8
}
}, "months_backward": {
"low_age_low_education": {
"mean": 10.3,
"std": 4.5
}, "low_age_high_education": {
"mean": 9.8,
"std": 3.2
}, "high_age_low_education": {
"mean": 10.0,
"std": 3.2
}, "high_age_high_education": {
"mean": 9.9,
"std": 3.5
}
},
"logicogrammatic": {
"low_age_low_education": {
"mean": 27.2,
"std": 3.8
}, "low_age_high_education": {
"mean": 27.4,
"std": 3.1
}, "high_age_low_education": {
"mean": 23.5,
"std": 4.2
}, "high_age_high_education": {
"mean": 27.2,
"std": 3.3
}
},"inference": {
"low_age_low_education": {
"mean": 28.2,
"std": 2.4
}, "low_age_high_education": {
"mean": 28.4,
"std": 2.8
}, "high_age_low_education": {
"mean": 25.2,
"std": 3.9
}, "high_age_high_education": {
"mean": 27.3,
"std": 2.9
}
}, "reading_speed": {
"low_age_low_education": {
"mean": 21.5,
"std": 5.4
}, "low_age_high_education": {
"mean": 27.3,
"std": 5.5
}, "high_age_low_education": {
"mean": 23.0,
"std": 7.2
}, "high_age_high_education": {
"mean": 28.8,
"std": 5.2
}
}, "decoding_words": {
"low_age_low_education": {
"mean": 107.6,
"std": 27.8
}, "low_age_high_education": {
"mean": 112.9,
"std": 22.7
}, "high_age_low_education": {
"mean": 111.4,
"std": 26.1
}, "high_age_high_education": {
"mean": 122.9,
"std": 28.2
}
}, "decoding_non_words": {
"low_age_low_education": {
"mean": 95.8,
"std": 26.6
}, "low_age_high_education": {
"mean": 105.4,
"std": 29.5
}, "high_age_low_education": {
"mean": 103.4,
"std": 25.2
}, "high_age_high_education": {
"mean": 118.0,
"std": 26.8
}
},
# jönsson och winnerstam 2012
"pataka": {
"low_age_low_education": {
"mean": 5.8,
"std": 1.0
}, "low_age_high_education": {
"mean": 5.8,
"std": 1.0
}, "high_age_low_education": {
"mean": 5.8,
"std": 1.0
}, "high_age_high_education": {
"mean": 5.8,
"std": 1.0
}
}
}
# Function to calculate z-score
def calculate_z_score(test_score, mean, std_dev):
return (test_score - mean) / std_dev
def z_score_calculator(value, norm_mean, norm_sd, invert=False):
z_value = (value - norm_mean) / norm_sd
if invert:
z_value = -z_value
stanine_value = zscore_to_stanine(z_value)
z_score = round(z_value, 2)
return z_score, stanine_value
def zscore_to_stanine(zscore):
if zscore <= -1.76:
return 1
elif zscore <= -1.26:
return 2
elif zscore <= -0.76:
return 3
elif zscore <= -0.26:
return 4
elif zscore <= 0.25:
return 5
elif zscore <= 0.75:
return 6
elif zscore <= 1.25:
return 7
elif zscore <= 1.75:
return 8
else:
return 9
def test_calculator(age, education, values, test, invert=False):
if age <= 60 and education <= 12:
norm_mean = test_dict[test]["low_age_low_education"]["mean"]
norm_sd = test_dict[test]["low_age_low_education"]["std"]
z_score, stanine_value = z_score_calculator(values, norm_mean, norm_sd, invert=invert)
return norm_mean, norm_sd, z_score, stanine_value
elif age <= 60 and education > 12:
norm_mean = test_dict[test]["low_age_high_education"]["mean"]
norm_sd = test_dict[test]["low_age_high_education"]["std"]
z_score, stanine_value = z_score_calculator(values, norm_mean, norm_sd, invert=invert)
return norm_mean, norm_sd, z_score, stanine_value
elif age > 60 and education <= 12:
norm_mean = test_dict[test]["high_age_low_education"]["mean"]
norm_sd = test_dict[test]["high_age_low_education"]["std"]
z_score, stanine_value = z_score_calculator(values, norm_mean, norm_sd, invert=invert)
return norm_mean, norm_sd, z_score, stanine_value
elif age > 60 and education > 12:
norm_mean = test_dict[test]["high_age_high_education"]["mean"]
norm_sd = test_dict[test]["high_age_high_education"]["std"]
z_score, stanine_value = z_score_calculator(values, norm_mean, norm_sd, invert=invert)
return norm_mean, norm_sd, z_score, stanine_value
else:
print("missing value/ wrong format")
def isw_calculator(age, education, isw):
print("the isw is", isw)
isw_score = 74.38 + isw*0.66 - age*0.20 + education*1.77
# round to nearest integer
isw_score = round(isw_score)
print("the score is: ", isw_score)
isw_score_stanine = isw_stanine_calculator(isw_score)
return 0, 0, isw_score, isw_score_stanine
def isw_stanine_calculator(isw_score):
print("the score is: ", isw_score)
if isw_score <= 73:
return 1
elif isw_score <= 81:
return 2
elif isw_score <= 88:
return 3
elif isw_score <= 96:
return 4
elif isw_score <= 103:
return 5
elif isw_score <= 111:
return 6
elif isw_score <= 118:
return 7
elif isw_score <= 126:
return 8
elif isw_score >= 127:
return 9
def bnt_calculator(age, education, bnt):
if age <= 60 and education <= 12:
norm_mean = 54.5
norm_sd = 3.2
z_score, stanine_value = z_score_calculator(bnt, norm_mean, norm_sd)
return norm_mean, norm_sd, z_score, stanine_value
elif age <= 60 and education > 12:
norm_mean = 54.0
norm_sd = 4.4
z_score, stanine_value = z_score_calculator(bnt, norm_mean, norm_sd)
return norm_mean, norm_sd, z_score, stanine_value
elif age > 60 and education <= 12:
norm_mean = 54.8
norm_sd = 3.3
z_score, stanine_value = z_score_calculator(bnt, norm_mean, norm_sd)
return norm_mean, norm_sd, z_score, stanine_value
elif age > 60 and education > 12:
norm_mean = 56.2
norm_sd = 3.4
z_score, stanine_value = z_score_calculator(bnt, norm_mean, norm_sd)
return norm_mean, norm_sd, z_score, stanine_value
else:
print("missing value/ wrong format")
def fas_calculator(age, education, fas):
if age <= 60 and education <= 12:
norm_mean = 42.7
norm_sd = 13.7
z_score, stanine_value = z_score_calculator(fas, norm_mean, norm_sd)
return norm_mean, norm_sd, z_score, stanine_value
elif age <= 60 and education > 12:
norm_mean = 46.7
norm_sd = 13.7
z_score, stanine_value = z_score_calculator(fas, norm_mean, norm_sd)
return norm_mean, norm_sd, z_score, stanine_value
elif age > 60 and education <= 12:
norm_mean = 46.9
norm_sd = 10.4
z_score, stanine_value = z_score_calculator(fas, norm_mean, norm_sd)
return norm_mean, norm_sd, z_score, stanine_value
elif age > 60 and education > 12:
norm_mean = 51.6
norm_sd = 12.6
z_score, stanine_value = z_score_calculator(fas, norm_mean, norm_sd)
return norm_mean, norm_sd, z_score, stanine_value
else:
print("missing value/ wrong format")
def generate_graph(test_dict):
# Create a plot
fig, ax = plt.subplots()
# Adjust the margins to fix the labels being cut off
fig.subplots_adjust(left=0.25)
# Set axis labels and title
ax.set_xlabel('Stanine values')
ax.set_ylabel('Test')
# Set the x-axis to display the stanine values
ax.set_xticks([1, 2, 3, 4, 5, 6, 7, 8, 9])
ax.set_xticklabels(['1', '2', '3', '4', '5', '6', '7', '8', '9'])
# Set the y-axis to display the tests
ax.set_yticks([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
# Set the test labels to the keys in test_dict (in reverse order)
ax.set_yticklabels(reversed(list(test_dict.keys())))
# Set the range of the x-axis
ax.set_xlim([0, 10])
i = 1
for test in reversed(list(test_dict.keys())):
#print("the test is", test)
# check if values are not empty
ax.scatter(test_dict[test][4], i, s=50, color='black', label=test)
i = i + 1
# Save the graph as a png file
fig.savefig('test_profile.png')
return 'test_profile.png'
def create_pdf(test_dict, logo_path, plot_path):
pdf = FPDF()
pdf.add_page()
pdf.set_xy(0, 0)
pdf.set_font("Arial", size=12)
# Add logos
x_positions = [25, 85, 145]
for i, logo_path in enumerate(logo_paths):
pdf.image(logo_path, x=x_positions[i], y=8, w=40)
pdf.set_xy(10, 50)
# Add title and center it
title = "Patient Summary"
pdf.set_font("Times", style="B", size=12)
title_width = pdf.get_string_width(title) + 6
pdf.cell((210 - title_width) / 2)
pdf.cell(title_width, 10, title, 0, 1, "C")
# Add z-score and center it
pdf.set_font("Times", size=12)
tests_per_line = 1
tests_count = len(test_dict)
line_count = tests_count // tests_per_line + (1 if tests_count % tests_per_line > 0 else 0)
test_index = 0
left_margin = 15
col_width = (210 - 2 * left_margin) / tests_per_line
for line in range(line_count):
pdf.set_x(left_margin)
for test in list(test_dict.keys())[test_index:test_index + tests_per_line]:
# Set the font to bold for the test value
pdf.set_font("Arial", style="B", size=8)
test_text = "{}: ".format(test)
test_width = pdf.get_string_width(test_text)
pdf.cell(test_width, 6, test_text, 0, 0, "C") # Changed the line height to 6
# Set the font to normal for the rest of the text
pdf.set_font("Arial", size=8)
z_score_text = "{:.2f} (Mean: {:.2f}, Std: {:.2f})".format(
test_dict[test][3], test_dict[test][1], test_dict[test][2]
)
z_score_width = pdf.get_string_width(z_score_text) + 2 # Reduced the additional width to 2
pdf.cell((210 / tests_per_line - test_width - z_score_width) / 2)
pdf.cell(z_score_width, 6, z_score_text, 0, 0, "C") # Changed the line height to 6
test_index += 1
pdf.ln(12) # Reduced the line spacing to 12
# Add logo
pdf.add_page()
pdf.image(plot_path, x=10, y=20, w=200)
# pdf.set_xy(10, 40)
# Add tool description and center it
pdf.set_xy(10, 200)
pdf.set_font("Arial", size=10)
description = "This PDF report was generated using the Patient Summary App."
pdf.multi_cell(0, 10, description, 0, "C")
# Add explanatory text about the collaboration between KI and KTH
pdf.set_xy(10, 220)
pdf.set_font("Arial", size=8)
collaboration_text = (
"Den här PDF:en är en del av ett samarbetsprojekt mellan Karolinska Institutet (KI) och "
"Kungliga Tekniska Högskolan (KTH) med målsättningen att använda artificiell intelligens (AI) och "
"teknik för att minska administration i sjukhusarbete. Projektet fokuserar på att utveckla och "
"implementera AI-baserade lösningar för att förbättra arbetsflöden, öka effektiviteten och "
"minska den administrativa bördan för sjukvårdspersonal. För frågor om formuläret kontakta Fredrik Sand fredrik.sand-aronsson@regionstockholm.se, för frågor om teknik kontakta Birger Moëll bmoell@kth.se."
)
line_width = 190
line_height = pdf.font_size_pt * 0.6
lines = collaboration_text.split(' ')
current_line = ''
for word in lines:
if pdf.get_string_width(current_line + word) < line_width:
current_line += word + ' '
else:
pdf.cell(line_width, line_height, current_line, 0, 1)
current_line = word + ' '
pdf.cell(line_width, line_height, current_line, 0, 1)
return pdf
def pdf_to_base64(pdf):
with open(pdf, "rb") as file:
return base64.b64encode(file.read()).decode('utf-8')
# Title and description
st.title("Language profile")
st.write("Enter your test score, to calculate scores and create a PDF with the results.")
# Input fields
#test_score = st.number_input("Test Score", min_value=0, value=0, step=1)
age = st.number_input("Age", min_value=0, value=18, step=1)
education_level = st.number_input("Education Level in years", min_value=0, value=18, step=1)
isw = st.number_input("ISW", min_value=0.0, value=0.0, step=0.01)
bnt = st.number_input("BNT", min_value=0, value=0, step=1)
fas = st.number_input("FAS", min_value=0, value=0, step=1)
animal = st.number_input("Animal", min_value=0, value=0, step=1)
verb = st.number_input("Verb", min_value=0, value=0, step=1)
repetition = st.number_input("Repetition", min_value=0, value=0, step=1)
logicogrammatic = st.number_input("Logicogrammatic", min_value=0, value=0, step=1)
inference = st.number_input("Inference", min_value=0, value=0, step=1)
reading_speed = st.number_input("Reading Speed", min_value=0, value=0, step=1)
decoding_words = st.number_input("Decoding Words", min_value=0.0, value=0.0, step=0.01)
decoding_non_words = st.number_input("Decoding Non-Words", min_value=0.0, value=0.0, step=0.01)
months_backward = st.number_input("Months Backward", min_value=0.0, value=0.0, step=0.01)
pataka = st.number_input("Pataka", min_value=0.0, value=0.0, step=0.01)
# add all the tests
# Calculate mean and standard deviation based on age and education level
# For simplicity, we will use made-up values for mean and std_dev
mean = np.random.randint(50, 100)
std_dev = np.random.randint(10, 30)
# Calculate z-score and display result
if st.button("Calculate Z-Score"):
profile = test_profile(age, education_level, isw, bnt, fas)
# calculate the isw iq score
isw_mean, isw_std, isw_iq, isw_stanine = isw_calculator(age, education_level, isw)
# for each value in the profile, calculate the z-score
bnt_mean, bnt_std, z_bnt, stanine_bnt = bnt_calculator(age, education_level, bnt)
fas_mean, fas_std, z_fas, stanine_fas = fas_calculator(age, education_level, fas)
animal_mean, animal_std, animal_z, animal_stanine = test_calculator(age, education_level, animal, "animal")
verb_mean, verb_std, verb_z, verb_stanine = test_calculator(age, education_level, verb, "verb")
repetition_mean, repetition_std, repetition_z, repetition_stanine = test_calculator(age, education_level, repetition, "repetition")
months_backward_mean, months_backward_std, months_backward_z, months_backward_stanine = test_calculator(age, education_level, months_backward, "months_backward", invert=True)
# months_backward_z = months_backward_z * -1
# print("the months backw z is: ", months_backward_z)
logicogrammatic_mean, logicogrammatic_std, logicogrammatic_z, logicogrammatic_stanine = test_calculator(age, education_level, logicogrammatic, "logicogrammatic")
inference_mean, inference_std, inference_z, inference_stanine = test_calculator(age, education_level, inference, "inference")
reading_speed_mean, reading_speed_std, reading_speed_z, reading_speed_stanine = test_calculator(age, education_level, reading_speed, "reading_speed")
decoding_words_mean, decoding_words_std, decoding_words_z, decoding_words_stanine = test_calculator(age, education_level, decoding_words, "decoding_words")
decoding_non_words_mean, decoding_non_words_std, decoding_non_words_z, decoding_non_words_stanine = test_calculator(age, education_level, decoding_non_words, "decoding_non_words")
pataka_mean, pataka_std, pataka_z, pataka_stanine = test_calculator(age, education_level, pataka, "pataka")
## add all the tests with their values to an array
## CREATA A DICTORINARY WITH ALL THE TESTS AND THEIR Z-SCORES AND STANINE VALUES
test_dict = {
"bnt": [bnt, bnt_mean, bnt_std, z_bnt, stanine_bnt],
"fas": [fas, fas_mean, fas_std, z_fas, stanine_fas],
"isw": [isw, isw_mean, isw_std, isw_iq, isw_stanine],
"animal": [animal, animal_mean, animal_std, animal_z, animal_stanine],
"verb": [verb, verb_mean, verb_std, verb_z, verb_stanine],
"repetition": [repetition, repetition_mean, repetition_std, repetition_z, repetition_stanine],
"months_backward": [months_backward, months_backward_mean, months_backward_std, months_backward_z, months_backward_stanine],
"logicogrammatic": [logicogrammatic, logicogrammatic_mean, logicogrammatic_std, logicogrammatic_z, logicogrammatic_stanine],
"inference": [inference, inference_mean, inference_std, inference_z, inference_stanine],
"reading_speed": [reading_speed, reading_speed_mean, reading_speed_std, reading_speed_z, reading_speed_stanine],
"decoding_words": [decoding_words, decoding_words_mean, decoding_words_std, decoding_words_z, decoding_words_stanine],
"decoding_non_words": [decoding_non_words, decoding_non_words_mean, decoding_non_words_std, decoding_non_words_z, decoding_non_words_stanine],
"pataka": [pataka, pataka_mean, pataka_std, pataka_z, pataka_stanine]
}
# z_values = [z_bnt, z_fas, animal_z, verb_z, repetition_z, months_backward_z, logicogrammatic_z, inference_z, reading_speed_z, decoding_words_z, decoding_non_words_z, pataka_z]
# ## add all the stanines to a list
# stanine_values = [stanine_bnt, stanine_fas, animal_stanine, verb_stanine, repetition_stanine, months_backward_stanine, logicogrammatic_stanine, inference_stanine, reading_speed_stanine, decoding_words_stanine, decoding_non_words_stanine, pataka_stanine]
# loop over and write out all the z values
# loop over all the values in test dict and print out the z-score, mean and std_dev and stanine
for key, value in test_dict.items():
st.write(f"Your {key} z-score is: {value[3]:.2f}")
st.write(f"Mean: {value[1]}, Standard Deviation: {value[2]}")
st.write(f"Stanine: {value[4]}")
# Create PDF
logo_paths = ["logo.jpg", "logo2.jpg", "logo3.jpg"]
# create the plot from the dataframe
# check if education level is more than 12 years, if more than 12, set value to one, otherwise zero
plot_path = generate_graph(test_dict)
# create an image from the plot and add to streamlit display
image = Image.open(plot_path)
st.image(image, caption='Stanine plot', use_column_width=True)
pdf_filename = "z_score_report.pdf"
pdf = create_pdf(test_dict, logo_paths, plot_path)
pdf.output(name=pdf_filename)
# Download PDF
with open(pdf_filename, "rb") as file:
base64_pdf = base64.b64encode(file.read()).decode('utf-8')
pdf_display = f'<a href="data:application/octet-stream;base64,{base64_pdf}" download="{pdf_filename}">Download PDF</a>'
st.markdown(pdf_display, unsafe_allow_html=True)
# Remove PDF file after download
if os.path.exists(pdf_filename):
os.remove(pdf_filename)