Spaces:
Sleeping
Sleeping
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +105 -134
src/streamlit_app.py
CHANGED
|
@@ -481,10 +481,10 @@ if page == t("home"):
|
|
| 481 |
<div class='intro animate__fadeIn'>
|
| 482 |
<p>{t('home_intro_1').format(author="<b>Ngoue David Roger Yannick</b>")}</p>
|
| 483 |
<p>{t('home_intro_2')}</p>
|
| 484 |
-
|
| 485 |
""", unsafe_allow_html=True)
|
| 486 |
|
| 487 |
-
st.markdown(f"<div class='section-header
|
| 488 |
st.markdown(f"""
|
| 489 |
<div class='content animate__fadeIn'>
|
| 490 |
<p>{t('omics_description')}</p>
|
|
@@ -494,8 +494,8 @@ if page == t("home"):
|
|
| 494 |
<li><b>{t('proteomics')}</b>: {t('proteomics_desc')}</li>
|
| 495 |
<li><b>{t('metabolomics')}</b>: {t('metabolomics_desc')}</li>
|
| 496 |
</ul>
|
| 497 |
-
</div>
|
| 498 |
<p>{t('omics_integration')}</p>
|
|
|
|
| 499 |
""", unsafe_allow_html=True)
|
| 500 |
|
| 501 |
# About Omics Data Page
|
|
@@ -517,13 +517,13 @@ elif page == t("about_omics"):
|
|
| 517 |
<li><b>{t('proteomics')}</b>: {t('proteomics_format')}</li>
|
| 518 |
<li><b>{t('metabolomics')}</b>: {t('metabolomics_format')}</li>
|
| 519 |
</ul>
|
| 520 |
-
</div>
|
| 521 |
<p>{t('raw_omics_processing')}</p>
|
|
|
|
| 522 |
""", unsafe_allow_html=True)
|
| 523 |
|
| 524 |
-
st.markdown(f"<div class='section-header'
|
| 525 |
st.markdown(f"""
|
| 526 |
-
<div class='content animate__fadeIn'
|
| 527 |
<p>{t('preprocessing_desc')}</p>
|
| 528 |
<ol>
|
| 529 |
<li><b>{t('quality_control')}</b>: {t('quality_control_desc')}</li>
|
|
@@ -532,8 +532,8 @@ elif page == t("about_omics"):
|
|
| 532 |
<li><b>{t('standardization')}</b>: {t('standardization_desc')}</li>
|
| 533 |
<li><b>{t('feature_selection')}</b>: {t('feature_selection_desc')}</li>
|
| 534 |
</ol>
|
| 535 |
-
</div>
|
| 536 |
<p>{t('preprocessing_importance')}</p>
|
|
|
|
| 537 |
""", unsafe_allow_html=True)
|
| 538 |
|
| 539 |
st.markdown(f"<div class='section-header'>{t('csv_format')}</div>", unsafe_allow_html=True)
|
|
@@ -547,7 +547,6 @@ elif page == t("about_omics"):
|
|
| 547 |
<li><b>{t('separator')}</b>: {t('separator_desc')}</li>
|
| 548 |
<li><b>{t('header')}</b>: {t('header_desc')}</li>
|
| 549 |
</ul>
|
| 550 |
-
</div>
|
| 551 |
<p>{t('csv_example')}</p>
|
| 552 |
<pre class='code-block'>
|
| 553 |
PatientID,UMOD_rs12917707,APOL1_rs73885319,MYH9_rs4821480,Status
|
|
@@ -559,7 +558,7 @@ P001,0,1,0,Unknown
|
|
| 559 |
|
| 560 |
st.markdown(f"<div class='section-header'>{t('data_quality')}</div>", unsafe_allow_html=True)
|
| 561 |
st.markdown(f"""
|
| 562 |
-
<div class='content animate__fadeIn'
|
| 563 |
<p>{t('data_quality_desc')}</p>
|
| 564 |
<ul>
|
| 565 |
<li><b>{t('consistency')}</b>: {t('consistency_desc')}</li>
|
|
@@ -567,8 +566,8 @@ P001,0,1,0,Unknown
|
|
| 567 |
<li><b>{t('accuracy')}</b>: {t('accuracy_desc')}</li>
|
| 568 |
<li><b>{t('alignment')}</b>: {t('alignment_desc')}</li>
|
| 569 |
</ul>
|
| 570 |
-
</div>
|
| 571 |
<p>{t('data_quality_importance')}</p>
|
|
|
|
| 572 |
""", unsafe_allow_html=True)
|
| 573 |
|
| 574 |
# Predict Risk Page
|
|
@@ -599,6 +598,7 @@ elif page == t("predict_risk"):
|
|
| 599 |
with cols[idx]:
|
| 600 |
uploaded_file = st.file_uploader(f"{t(omic)} (CSV)", type=["csv"], key=omic)
|
| 601 |
if uploaded_file:
|
|
|
|
| 602 |
temp_path = os.path.join("data", f"temp_{omic}.csv")
|
| 603 |
with open(temp_path, "wb") as f:
|
| 604 |
f.write(uploaded_file.getbuffer())
|
|
@@ -620,71 +620,50 @@ elif page == t("predict_risk"):
|
|
| 620 |
patient_tensors = [torch.tensor(data_dict[omic].values, dtype=torch.float32) for omic in data_dict]
|
| 621 |
patient_dataset = TensorDataset(*patient_tensors)
|
| 622 |
patient_loader = DataLoader(patient_dataset, batch_size=best_hyperparams['batch_size'],
|
| 623 |
-
|
| 624 |
|
| 625 |
# Extract latent representations
|
| 626 |
encoded_data = extract_latent_representations(model, patient_loader, device)
|
| 627 |
|
| 628 |
# Calculate risk scores
|
| 629 |
-
|
| 630 |
-
umap_df = calculate_risk_scores(encoded_data, data_dict, risk_score, labels, label_encoder)
|
| 631 |
-
st.error_state(t("risk_score_error"))
|
| 632 |
-
except Exception as e:
|
| 633 |
-
print("Error calculating risk scores: {str(e)}")
|
| 634 |
|
| 635 |
# Perform SHAP analysis
|
| 636 |
output_dir = os.path.join("data", "temp_output")
|
| 637 |
os.makedirs(output_dir, exist_ok=True)
|
| 638 |
-
|
| 639 |
-
important_biom, shap_plot_path = perform_shap_analysis(
|
| 640 |
model, combined_data, input_dims, device, feature_names, output_dir, irc_biomarkers
|
| 641 |
)
|
| 642 |
-
except Exception as e:
|
| 643 |
-
print("Error generating SHAP analysis: {str(e)}")
|
| 644 |
|
| 645 |
# Generate dynamic recommendations
|
| 646 |
-
try:
|
| 647 |
risk_score = umap_df[t('risk_score')].iloc[0]
|
| 648 |
-
biomarkers =
|
| 649 |
recommendations = []
|
| 650 |
for intent in ["medication", "diet", "exercise"]:
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
recommendations.append(t(f"{intent}_error", lang))
|
| 659 |
-
except Exception as e:
|
| 660 |
-
print("Error generating recommendation for {str(intent)}: {str(e)}")
|
| 661 |
-
except Exception as e:
|
| 662 |
-
print("Error generating recommendations: {str(e)}")
|
| 663 |
|
| 664 |
# Generate PDF report
|
| 665 |
-
try:
|
| 666 |
pdf_path = os.path.join(output_dir, f"ckd_risk_report_{st.session_state.patient_id}.pdf")
|
| 667 |
-
|
| 668 |
-
lang)
|
| 669 |
-
except Exception as e:
|
| 670 |
-
print("Error generating PDF report: {str(e)}")
|
| 671 |
|
| 672 |
# Save to database
|
| 673 |
-
try:
|
| 674 |
session = Session()
|
| 675 |
patient = Patient(
|
| 676 |
patient_id=st.session_state.patient_id,
|
| 677 |
risk_score=risk_score,
|
| 678 |
biomarkers=', '.join(biomarkers)
|
| 679 |
)
|
| 680 |
-
session.add(
|
| 681 |
session.commit()
|
| 682 |
session.close()
|
| 683 |
-
except Exception as e:
|
| 684 |
-
print("Error saving to database: {str(e)}")
|
| 685 |
|
| 686 |
# Store results
|
| 687 |
-
try:
|
| 688 |
st.session_state.results = {
|
| 689 |
'umap_df': umap_df,
|
| 690 |
'important_biomarkers': important_biomarkers,
|
|
@@ -692,8 +671,6 @@ elif page == t("predict_risk"):
|
|
| 692 |
'pdf_path': pdf_path,
|
| 693 |
'recommendations': recommendations
|
| 694 |
}
|
| 695 |
-
except Exception as e:
|
| 696 |
-
print("Error storing results: {str(e)}")
|
| 697 |
|
| 698 |
# Display results
|
| 699 |
st.markdown(f"<div class='section-header'>{t('prediction_results')}</div>", unsafe_allow_html=True)
|
|
@@ -704,78 +681,75 @@ elif page == t("predict_risk"):
|
|
| 704 |
fig_gauge = go.Figure(go.Indicator(
|
| 705 |
mode="gauge+number",
|
| 706 |
value=risk_score,
|
| 707 |
-
title={'text': t('risk_score', lang), 'font':{'color': '#FFD700'}},
|
| 708 |
gauge={
|
| 709 |
'axis': {'range': [0, 100], 'tickcolor': '#FFF', 'tickfont': {'color': '#FFF'}},
|
| 710 |
'bar': {'color': '#0000FF'},
|
| 711 |
'steps': [
|
| 712 |
{'range': [0, 33], 'color': '#4ADE80'},
|
| 713 |
{'range': [33, 66], 'color': '#FACC15'},
|
| 714 |
-
{'range': [66,
|
| 715 |
],
|
| 716 |
-
},
|
| 717 |
'threshold': {
|
| 718 |
'line': {'color': '#FFD700', 'width': 4},
|
| 719 |
'thickness': 0.75,
|
| 720 |
'value': risk_score
|
| 721 |
}
|
| 722 |
-
}
|
| 723 |
}
|
| 724 |
))
|
| 725 |
fig_gauge.update_layout(
|
| 726 |
paper_bgcolor='#0E1117',
|
| 727 |
font={'color': "#FFFFFF"},
|
| 728 |
-
margin={'l':20,'r':20,'t':50,'b':20}
|
| 729 |
)
|
| 730 |
st.plotly_chart(fig_gauge, use_container_width=True)
|
| 731 |
|
| 732 |
# SHAP bar plot
|
| 733 |
st.markdown(f"<div class='subheader'>{t('key_biomarkers')}</div>", unsafe_allow_html=True)
|
| 734 |
shap_fig = plt.figure(figsize=(12, 8))
|
| 735 |
-
sns.barplot(data=important_biomarkers.head(20)
|
| 736 |
-
|
| 737 |
plt.title(t('shap_top_features'), fontsize=16, color='#FFFFFF')
|
| 738 |
plt.xlabel(t('shap_mean_value'), fontsize=12, color='#FFFFFF')
|
| 739 |
-
plt.ylabel(t('feature'), fontsize=12, color='#
|
| 740 |
-
plt.legend(title=t('omics'), facecolor='#1C2526', edgecolor='#
|
| 741 |
plt.gca().set_facecolor('#0E1117')
|
| 742 |
-
plt.gca().tick_params(colors=
|
| 743 |
plt.gcf().set_facecolor('#0E1117')
|
| 744 |
st.pyplot(shap_fig)
|
| 745 |
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
st.markdown(href_pdf, unsafe=allow_html=True)
|
| 771 |
except Exception as e:
|
| 772 |
st.error(t("file_process_error").format(error=str(e)))
|
| 773 |
st.stop()
|
| 774 |
else:
|
| 775 |
-
st.warning(t("
|
| 776 |
|
| 777 |
# Chatbot Assistant Page
|
| 778 |
-
|
| 779 |
st.markdown(f"<div class='title animate__fadeIn'>{t('chatbot_title')}</div>", unsafe_allow_html=True)
|
| 780 |
st.markdown(f"""
|
| 781 |
<div class='content animate__fadeIn'>
|
|
@@ -785,7 +759,7 @@ else if page == t("chatbot"):
|
|
| 785 |
|
| 786 |
# Chat Interface
|
| 787 |
st.markdown(f"<div class='section-header'>{t('chat_with_assistant')}</div>", unsafe_allow_html=True)
|
| 788 |
-
chat_container = st.container()
|
| 789 |
with chat_container:
|
| 790 |
for message in st.session_state.chat_history:
|
| 791 |
if message['role'] == 'user':
|
|
@@ -794,57 +768,54 @@ else if page == t("chatbot"):
|
|
| 794 |
st.markdown(f"<div class='chat-bubble bot'>{message['content']}</div>", unsafe_allow_html=True)
|
| 795 |
|
| 796 |
# User input
|
| 797 |
-
user_input =
|
| 798 |
if st.button(t("send"), key="button_send"):
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 807 |
risk_score = st.session_state.results['umap_df'][t('risk_score')].iloc[0]
|
| 808 |
-
biomarkers = st.session_state.results['important_biomarkers'][t('feature')].head(5).tolist()
|
| 809 |
-
|
| 810 |
-
response =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 811 |
else:
|
| 812 |
-
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
risk_score = st.session_state.results['umap_df'][t('risk_score')].iloc[0]
|
| 823 |
-
biomarkers = st.session_state.results['important_biomarkers'][t('feature')].head(5).tolist()
|
| 824 |
-
try:
|
| 825 |
-
prompt = formulate_prompt(intent, risk_score, biomarkers)
|
| 826 |
-
try:
|
| 827 |
-
response = generate_recommendation(prompt)
|
| 828 |
-
if validate_recommendation(response):
|
| 829 |
-
response = translate_recommendation(response, lang)
|
| 830 |
-
else:
|
| 831 |
-
response = t(f"{intent}_error", lang)
|
| 832 |
-
else:
|
| 833 |
-
response = send_to_rasa(user_input, lang)
|
| 834 |
-
|
| 835 |
-
# Add bot response to history
|
| 836 |
-
st.session_state.chat_history.append({'role': 'bot', 'content': response})
|
| 837 |
-
|
| 838 |
-
# Rerender chat
|
| 839 |
-
st.rerun()
|
| 840 |
-
except Exception as e:
|
| 841 |
-
print("Error processing chatbot input: {str(e)}")
|
| 842 |
-
st.error("Error processing chatbot input")
|
| 843 |
|
| 844 |
# Dashboard Page
|
| 845 |
-
elif
|
| 846 |
st.markdown(f"<div class='title animate__fadeIn'>{t('dashboard_title')}</div>", unsafe_allow_html=True)
|
| 847 |
-
|
| 848 |
st.markdown(f"""
|
| 849 |
<div class='content animate__fadeIn'>
|
| 850 |
<p>{t('dashboard_intro')}</p>
|
|
@@ -872,7 +843,7 @@ elif if page == t("dashboard"):
|
|
| 872 |
st.dataframe(filtered_df, use_container_width=True)
|
| 873 |
|
| 874 |
# Plot
|
| 875 |
-
fig = px.
|
| 876 |
filtered_df,
|
| 877 |
x=t('risk_score'),
|
| 878 |
y=t('patient_id'),
|
|
@@ -884,9 +855,9 @@ elif if page == t("dashboard"):
|
|
| 884 |
fig.update_layout(
|
| 885 |
paper_bgcolor='#0E1117',
|
| 886 |
plot_bgcolor='#1C2526',
|
| 887 |
-
font={'color': '#
|
| 888 |
)
|
| 889 |
-
st.plotly_chart(fig, use_container_width=True)
|
| 890 |
else:
|
| 891 |
st.info(t("no_patients"))
|
| 892 |
session.close()
|
|
@@ -903,10 +874,10 @@ if rasa_process:
|
|
| 903 |
try:
|
| 904 |
rasa_process.terminate()
|
| 905 |
except Exception as e:
|
| 906 |
-
print("Error terminating Rasa server: {str(e)}")
|
| 907 |
|
| 908 |
st.markdown(f"""
|
| 909 |
-
<div class='footer
|
| 910 |
{t('footer')}
|
| 911 |
</div>
|
| 912 |
""", unsafe_allow_html=True)
|
|
|
|
| 481 |
<div class='intro animate__fadeIn'>
|
| 482 |
<p>{t('home_intro_1').format(author="<b>Ngoue David Roger Yannick</b>")}</p>
|
| 483 |
<p>{t('home_intro_2')}</p>
|
| 484 |
+
</div>
|
| 485 |
""", unsafe_allow_html=True)
|
| 486 |
|
| 487 |
+
st.markdown(f"<div class='section-header'>{t('why_omics')}</div>", unsafe_allow_html=True)
|
| 488 |
st.markdown(f"""
|
| 489 |
<div class='content animate__fadeIn'>
|
| 490 |
<p>{t('omics_description')}</p>
|
|
|
|
| 494 |
<li><b>{t('proteomics')}</b>: {t('proteomics_desc')}</li>
|
| 495 |
<li><b>{t('metabolomics')}</b>: {t('metabolomics_desc')}</li>
|
| 496 |
</ul>
|
|
|
|
| 497 |
<p>{t('omics_integration')}</p>
|
| 498 |
+
</div>
|
| 499 |
""", unsafe_allow_html=True)
|
| 500 |
|
| 501 |
# About Omics Data Page
|
|
|
|
| 517 |
<li><b>{t('proteomics')}</b>: {t('proteomics_format')}</li>
|
| 518 |
<li><b>{t('metabolomics')}</b>: {t('metabolomics_format')}</li>
|
| 519 |
</ul>
|
|
|
|
| 520 |
<p>{t('raw_omics_processing')}</p>
|
| 521 |
+
</div>
|
| 522 |
""", unsafe_allow_html=True)
|
| 523 |
|
| 524 |
+
st.markdown(f"<div class='section-header'>{t('preprocessing_steps')}</div>", unsafe_allow_html=True)
|
| 525 |
st.markdown(f"""
|
| 526 |
+
<div class='content animate__fadeIn'>
|
| 527 |
<p>{t('preprocessing_desc')}</p>
|
| 528 |
<ol>
|
| 529 |
<li><b>{t('quality_control')}</b>: {t('quality_control_desc')}</li>
|
|
|
|
| 532 |
<li><b>{t('standardization')}</b>: {t('standardization_desc')}</li>
|
| 533 |
<li><b>{t('feature_selection')}</b>: {t('feature_selection_desc')}</li>
|
| 534 |
</ol>
|
|
|
|
| 535 |
<p>{t('preprocessing_importance')}</p>
|
| 536 |
+
</div>
|
| 537 |
""", unsafe_allow_html=True)
|
| 538 |
|
| 539 |
st.markdown(f"<div class='section-header'>{t('csv_format')}</div>", unsafe_allow_html=True)
|
|
|
|
| 547 |
<li><b>{t('separator')}</b>: {t('separator_desc')}</li>
|
| 548 |
<li><b>{t('header')}</b>: {t('header_desc')}</li>
|
| 549 |
</ul>
|
|
|
|
| 550 |
<p>{t('csv_example')}</p>
|
| 551 |
<pre class='code-block'>
|
| 552 |
PatientID,UMOD_rs12917707,APOL1_rs73885319,MYH9_rs4821480,Status
|
|
|
|
| 558 |
|
| 559 |
st.markdown(f"<div class='section-header'>{t('data_quality')}</div>", unsafe_allow_html=True)
|
| 560 |
st.markdown(f"""
|
| 561 |
+
<div class='content animate__fadeIn'>
|
| 562 |
<p>{t('data_quality_desc')}</p>
|
| 563 |
<ul>
|
| 564 |
<li><b>{t('consistency')}</b>: {t('consistency_desc')}</li>
|
|
|
|
| 566 |
<li><b>{t('accuracy')}</b>: {t('accuracy_desc')}</li>
|
| 567 |
<li><b>{t('alignment')}</b>: {t('alignment_desc')}</li>
|
| 568 |
</ul>
|
|
|
|
| 569 |
<p>{t('data_quality_importance')}</p>
|
| 570 |
+
</div>
|
| 571 |
""", unsafe_allow_html=True)
|
| 572 |
|
| 573 |
# Predict Risk Page
|
|
|
|
| 598 |
with cols[idx]:
|
| 599 |
uploaded_file = st.file_uploader(f"{t(omic)} (CSV)", type=["csv"], key=omic)
|
| 600 |
if uploaded_file:
|
| 601 |
+
os.makedirs("data", exist_ok=True)
|
| 602 |
temp_path = os.path.join("data", f"temp_{omic}.csv")
|
| 603 |
with open(temp_path, "wb") as f:
|
| 604 |
f.write(uploaded_file.getbuffer())
|
|
|
|
| 620 |
patient_tensors = [torch.tensor(data_dict[omic].values, dtype=torch.float32) for omic in data_dict]
|
| 621 |
patient_dataset = TensorDataset(*patient_tensors)
|
| 622 |
patient_loader = DataLoader(patient_dataset, batch_size=best_hyperparams['batch_size'],
|
| 623 |
+
shuffle=False)
|
| 624 |
|
| 625 |
# Extract latent representations
|
| 626 |
encoded_data = extract_latent_representations(model, patient_loader, device)
|
| 627 |
|
| 628 |
# Calculate risk scores
|
| 629 |
+
umap_df = calculate_risk_scores(encoded_data, data_dict, irc_biomarkers, labels, label_encoder)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 630 |
|
| 631 |
# Perform SHAP analysis
|
| 632 |
output_dir = os.path.join("data", "temp_output")
|
| 633 |
os.makedirs(output_dir, exist_ok=True)
|
| 634 |
+
important_biomarkers, shap_plot_path = perform_shap_analysis(
|
|
|
|
| 635 |
model, combined_data, input_dims, device, feature_names, output_dir, irc_biomarkers
|
| 636 |
)
|
|
|
|
|
|
|
| 637 |
|
| 638 |
# Generate dynamic recommendations
|
|
|
|
| 639 |
risk_score = umap_df[t('risk_score')].iloc[0]
|
| 640 |
+
biomarkers = important_biomarkers[t('feature')].head(5).tolist()
|
| 641 |
recommendations = []
|
| 642 |
for intent in ["medication", "diet", "exercise"]:
|
| 643 |
+
prompt = formulate_prompt(intent, risk_score, biomarkers)
|
| 644 |
+
rec = generate_recommendation(prompt)
|
| 645 |
+
if validate_recommendation(rec):
|
| 646 |
+
rec = translate_recommendation(rec, lang)
|
| 647 |
+
recommendations.append(rec)
|
| 648 |
+
else:
|
| 649 |
+
recommendations.append(t(f"{intent}_error", lang))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 650 |
|
| 651 |
# Generate PDF report
|
|
|
|
| 652 |
pdf_path = os.path.join(output_dir, f"ckd_risk_report_{st.session_state.patient_id}.pdf")
|
| 653 |
+
generate_pdf_report(risk_score, important_biomarkers, shap_plot_path, recommendations, pdf_path, lang)
|
|
|
|
|
|
|
|
|
|
| 654 |
|
| 655 |
# Save to database
|
|
|
|
| 656 |
session = Session()
|
| 657 |
patient = Patient(
|
| 658 |
patient_id=st.session_state.patient_id,
|
| 659 |
risk_score=risk_score,
|
| 660 |
biomarkers=', '.join(biomarkers)
|
| 661 |
)
|
| 662 |
+
session.add(patient)
|
| 663 |
session.commit()
|
| 664 |
session.close()
|
|
|
|
|
|
|
| 665 |
|
| 666 |
# Store results
|
|
|
|
| 667 |
st.session_state.results = {
|
| 668 |
'umap_df': umap_df,
|
| 669 |
'important_biomarkers': important_biomarkers,
|
|
|
|
| 671 |
'pdf_path': pdf_path,
|
| 672 |
'recommendations': recommendations
|
| 673 |
}
|
|
|
|
|
|
|
| 674 |
|
| 675 |
# Display results
|
| 676 |
st.markdown(f"<div class='section-header'>{t('prediction_results')}</div>", unsafe_allow_html=True)
|
|
|
|
| 681 |
fig_gauge = go.Figure(go.Indicator(
|
| 682 |
mode="gauge+number",
|
| 683 |
value=risk_score,
|
| 684 |
+
title={'text': t('risk_score', lang), 'font': {'color': '#FFD700'}},
|
| 685 |
gauge={
|
| 686 |
'axis': {'range': [0, 100], 'tickcolor': '#FFF', 'tickfont': {'color': '#FFF'}},
|
| 687 |
'bar': {'color': '#0000FF'},
|
| 688 |
'steps': [
|
| 689 |
{'range': [0, 33], 'color': '#4ADE80'},
|
| 690 |
{'range': [33, 66], 'color': '#FACC15'},
|
| 691 |
+
{'range': [66, 100], 'color': '#EF4444'}
|
| 692 |
],
|
|
|
|
| 693 |
'threshold': {
|
| 694 |
'line': {'color': '#FFD700', 'width': 4},
|
| 695 |
'thickness': 0.75,
|
| 696 |
'value': risk_score
|
| 697 |
}
|
|
|
|
| 698 |
}
|
| 699 |
))
|
| 700 |
fig_gauge.update_layout(
|
| 701 |
paper_bgcolor='#0E1117',
|
| 702 |
font={'color': "#FFFFFF"},
|
| 703 |
+
margin={'l': 20, 'r': 20, 't': 50, 'b': 20}
|
| 704 |
)
|
| 705 |
st.plotly_chart(fig_gauge, use_container_width=True)
|
| 706 |
|
| 707 |
# SHAP bar plot
|
| 708 |
st.markdown(f"<div class='subheader'>{t('key_biomarkers')}</div>", unsafe_allow_html=True)
|
| 709 |
shap_fig = plt.figure(figsize=(12, 8))
|
| 710 |
+
sns.barplot(data=important_biomarkers.head(20), x='SHAP_Mean_Abs', y=t('feature'), hue=t('omic'),
|
| 711 |
+
palette=['#FF6F61', '#6B5B95', '#4CAF50', '#2196F3'])
|
| 712 |
plt.title(t('shap_top_features'), fontsize=16, color='#FFFFFF')
|
| 713 |
plt.xlabel(t('shap_mean_value'), fontsize=12, color='#FFFFFF')
|
| 714 |
+
plt.ylabel(t('feature'), fontsize=12, color='#FFFFFF')
|
| 715 |
+
plt.legend(title=t('omics'), facecolor='#1C2526', edgecolor='#FFFFFF', labelcolor='#FFFFFF')
|
| 716 |
plt.gca().set_facecolor('#0E1117')
|
| 717 |
+
plt.gca().tick_params(colors='#FFFFFF')
|
| 718 |
plt.gcf().set_facecolor('#0E1117')
|
| 719 |
st.pyplot(shap_fig)
|
| 720 |
|
| 721 |
+
# Display saved SHAP plot
|
| 722 |
+
if os.path.exists(shap_plot_path):
|
| 723 |
+
st.image(shap_plot_path, caption=t('shap'), use_column_width=True)
|
| 724 |
+
|
| 725 |
+
# Recommendations
|
| 726 |
+
st.markdown(f"<div class='subheader'>{t('recommendations')}</div>", unsafe_allow_html=True)
|
| 727 |
+
for rec in recommendations:
|
| 728 |
+
st.markdown(f"<div class='recommendation'>{rec}</div>", unsafe_allow_html=True)
|
| 729 |
+
|
| 730 |
+
# Download results
|
| 731 |
+
st.markdown(f"<div class='subheader'>{t('download_results')}</div>", unsafe_allow_html=True)
|
| 732 |
+
col1, col2 = st.columns(2)
|
| 733 |
+
with col1:
|
| 734 |
+
csv = important_biomarkers.to_csv(index=False).encode('utf-8')
|
| 735 |
+
b64 = base64.b64encode(csv).decode()
|
| 736 |
+
href = f'<a href="data:file/csv;base64,{b64}" download="important_biomarkers.csv" class="download-btn">{t("download_biomarkers", lang)}</a>'
|
| 737 |
+
st.markdown(href, unsafe_allow_html=True)
|
| 738 |
+
with col2:
|
| 739 |
+
with open(pdf_path, "rb") as f:
|
| 740 |
+
pdf_data = f.read()
|
| 741 |
+
b64_pdf = base64.b64encode(pdf_data).decode()
|
| 742 |
+
href_pdf = f'<a href="data:application/pdf;base64,{b64_pdf}" download="ckd_risk_report.pdf" class="download-btn">{t("download_report", lang)}</a>'
|
| 743 |
+
st.markdown(href_pdf, unsafe_allow_html=True)
|
| 744 |
+
|
|
|
|
| 745 |
except Exception as e:
|
| 746 |
st.error(t("file_process_error").format(error=str(e)))
|
| 747 |
st.stop()
|
| 748 |
else:
|
| 749 |
+
st.warning(t("upload_warning"))
|
| 750 |
|
| 751 |
# Chatbot Assistant Page
|
| 752 |
+
elif page == t("chatbot"):
|
| 753 |
st.markdown(f"<div class='title animate__fadeIn'>{t('chatbot_title')}</div>", unsafe_allow_html=True)
|
| 754 |
st.markdown(f"""
|
| 755 |
<div class='content animate__fadeIn'>
|
|
|
|
| 759 |
|
| 760 |
# Chat Interface
|
| 761 |
st.markdown(f"<div class='section-header'>{t('chat_with_assistant')}</div>", unsafe_allow_html=True)
|
| 762 |
+
chat_container = st.container()
|
| 763 |
with chat_container:
|
| 764 |
for message in st.session_state.chat_history:
|
| 765 |
if message['role'] == 'user':
|
|
|
|
| 768 |
st.markdown(f"<div class='chat-bubble bot'>{message['content']}</div>", unsafe_allow_html=True)
|
| 769 |
|
| 770 |
# User input
|
| 771 |
+
user_input = st.text_area(t("type_message"), key="chat_input")
|
| 772 |
if st.button(t("send"), key="button_send"):
|
| 773 |
+
if user_input:
|
| 774 |
+
try:
|
| 775 |
+
# Add user message to history
|
| 776 |
+
st.session_state.chat_history.append({'role': 'user', 'content': user_input})
|
| 777 |
+
|
| 778 |
+
# Check if user is asking about results
|
| 779 |
+
if st.session_state.results and any(
|
| 780 |
+
keyword in user_input.lower() for keyword in t('chat_risk_keywords').split(',')):
|
| 781 |
+
risk_score = st.session_state.results['umap_df'][t('risk_score')].iloc[0]
|
| 782 |
+
biomarkers = st.session_state.results['important_biomarkers'][t('feature')].head(5).tolist()
|
| 783 |
+
context = t('chat_risk_response').format(score=risk_score, biomarkers=', '.join(biomarkers))
|
| 784 |
+
response = send_to_rasa(f"{context} {user_input}", lang)
|
| 785 |
+
else:
|
| 786 |
+
# Generate recommendation based on intent
|
| 787 |
+
intent = 'None'
|
| 788 |
+
if any(keyword in user_input.lower() for keyword in t('chat_medication_keywords').split(',')):
|
| 789 |
+
intent = 'medication'
|
| 790 |
+
elif any(keyword in user_input.lower() for keyword in t('chat_diet_keywords').split(',')):
|
| 791 |
+
intent = 'diet'
|
| 792 |
+
elif any(keyword in user_input.lower() for keyword in t('chat_exercise_keywords').split(',')):
|
| 793 |
+
intent = 'exercise'
|
| 794 |
+
|
| 795 |
+
if intent != 'None' and st.session_state.results:
|
| 796 |
risk_score = st.session_state.results['umap_df'][t('risk_score')].iloc[0]
|
| 797 |
+
biomarkers = st.session_state.results['important_biomarkers'][t('feature')].head(5).tolist()
|
| 798 |
+
prompt = formulate_prompt(intent, risk_score, biomarkers)
|
| 799 |
+
response = generate_recommendation(prompt)
|
| 800 |
+
if validate_recommendation(response):
|
| 801 |
+
response = translate_recommendation(response, lang)
|
| 802 |
+
else:
|
| 803 |
+
response = t(f"{intent}_error", lang)
|
| 804 |
else:
|
| 805 |
+
response = send_to_rasa(user_input, lang)
|
| 806 |
+
|
| 807 |
+
# Add bot response to history
|
| 808 |
+
st.session_state.chat_history.append({'role': 'bot', 'content': response})
|
| 809 |
+
|
| 810 |
+
# Rerender chat
|
| 811 |
+
st.rerun()
|
| 812 |
+
|
| 813 |
+
except Exception as e:
|
| 814 |
+
st.error(t("chatbot_error").format(error=str(e)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 815 |
|
| 816 |
# Dashboard Page
|
| 817 |
+
elif page == t("dashboard"):
|
| 818 |
st.markdown(f"<div class='title animate__fadeIn'>{t('dashboard_title')}</div>", unsafe_allow_html=True)
|
|
|
|
| 819 |
st.markdown(f"""
|
| 820 |
<div class='content animate__fadeIn'>
|
| 821 |
<p>{t('dashboard_intro')}</p>
|
|
|
|
| 843 |
st.dataframe(filtered_df, use_container_width=True)
|
| 844 |
|
| 845 |
# Plot
|
| 846 |
+
fig = px.scatter(
|
| 847 |
filtered_df,
|
| 848 |
x=t('risk_score'),
|
| 849 |
y=t('patient_id'),
|
|
|
|
| 855 |
fig.update_layout(
|
| 856 |
paper_bgcolor='#0E1117',
|
| 857 |
plot_bgcolor='#1C2526',
|
| 858 |
+
font={'color': '#FFFFFF'}
|
| 859 |
)
|
| 860 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 861 |
else:
|
| 862 |
st.info(t("no_patients"))
|
| 863 |
session.close()
|
|
|
|
| 874 |
try:
|
| 875 |
rasa_process.terminate()
|
| 876 |
except Exception as e:
|
| 877 |
+
print(f"Error terminating Rasa server: {str(e)}")
|
| 878 |
|
| 879 |
st.markdown(f"""
|
| 880 |
+
<div class='footer'>
|
| 881 |
{t('footer')}
|
| 882 |
</div>
|
| 883 |
""", unsafe_allow_html=True)
|