Spaces:
Runtime error
Runtime error
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +75 -87
src/streamlit_app.py
CHANGED
|
@@ -775,24 +775,29 @@ def upload_page():
|
|
| 775 |
uploaded_file = st.file_uploader(
|
| 776 |
"Choose medical image",
|
| 777 |
type=['png', 'jpg', 'jpeg', 'dcm'],
|
| 778 |
-
help="Upload X-ray, CT, MRI,
|
|
|
|
| 779 |
)
|
| 780 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 781 |
|
| 782 |
-
# Display uploaded image with modern styling
|
| 783 |
if uploaded_file is not None:
|
| 784 |
try:
|
| 785 |
-
# Validate file size
|
| 786 |
file_size = uploaded_file.size
|
| 787 |
if file_size > 10 * 1024 * 1024: # 10MB
|
| 788 |
st.error("β οΈ File size too large. Please upload an image smaller than 10MB.")
|
| 789 |
else:
|
| 790 |
-
# Display file info
|
| 791 |
-
st.
|
|
|
|
| 792 |
|
| 793 |
-
# Display image
|
| 794 |
-
|
| 795 |
-
|
|
|
|
|
|
|
|
|
|
| 796 |
|
| 797 |
# Reset file pointer for later use
|
| 798 |
uploaded_file.seek(0)
|
|
@@ -800,6 +805,8 @@ def upload_page():
|
|
| 800 |
except Exception as e:
|
| 801 |
st.error(f"β Error processing image: {str(e)}")
|
| 802 |
st.info("π‘ Please ensure the file is a valid image format (PNG, JPG, JPEG, DCM)")
|
|
|
|
|
|
|
| 803 |
|
| 804 |
submit_button = st.form_submit_button("π Generate Report", use_container_width=True)
|
| 805 |
|
|
@@ -823,7 +830,7 @@ def generate_report(name, date_of_birth, gender, medical_record_number,
|
|
| 823 |
"""Generate report by sending data to backend and displaying the result"""
|
| 824 |
try:
|
| 825 |
if uploaded_file is not None:
|
| 826 |
-
with st.spinner("
|
| 827 |
# Validate file before sending
|
| 828 |
if uploaded_file.size > 10 * 1024 * 1024: # 10MB limit
|
| 829 |
st.error("β οΈ File size too large. Please upload an image smaller than 10MB.")
|
|
@@ -832,102 +839,83 @@ def generate_report(name, date_of_birth, gender, medical_record_number,
|
|
| 832 |
# Reset file pointer to beginning
|
| 833 |
uploaded_file.seek(0)
|
| 834 |
|
| 835 |
-
#
|
| 836 |
-
files = {"file": (uploaded_file.name, uploaded_file, uploaded_file.type)}
|
| 837 |
|
| 838 |
try:
|
|
|
|
| 839 |
analyze_response = requests.post(
|
| 840 |
-
f"{FASTAPI_BASE_URL}/analyze
|
| 841 |
files=files,
|
| 842 |
timeout=60 # 60 second timeout
|
| 843 |
)
|
| 844 |
|
| 845 |
-
if analyze_response.status_code
|
| 846 |
-
|
| 847 |
-
|
| 848 |
-
|
| 849 |
-
|
| 850 |
-
|
| 851 |
-
|
| 852 |
-
|
| 853 |
-
|
| 854 |
-
|
| 855 |
-
|
| 856 |
-
|
| 857 |
-
|
| 858 |
-
|
| 859 |
-
|
| 860 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 861 |
return
|
| 862 |
|
| 863 |
-
if not findings or findings.strip() == "":
|
| 864 |
-
st.warning("β οΈ No findings were generated. Please try again with a different image.")
|
| 865 |
-
return
|
| 866 |
|
| 867 |
except requests.exceptions.Timeout:
|
| 868 |
-
st.error("
|
| 869 |
return
|
| 870 |
except requests.exceptions.ConnectionError:
|
| 871 |
-
st.error("
|
|
|
|
|
|
|
| 872 |
return
|
| 873 |
except requests.exceptions.RequestException as e:
|
| 874 |
st.error(f"β Request failed: {str(e)}")
|
| 875 |
return
|
| 876 |
|
| 877 |
-
# Patient info for the report
|
| 878 |
-
patient_info = {
|
| 879 |
-
"name": name,
|
| 880 |
-
"medical_record_number": medical_record_number,
|
| 881 |
-
"referring_physician": referring_physician,
|
| 882 |
-
"date_of_study": date_of_study
|
| 883 |
-
}
|
| 884 |
-
|
| 885 |
-
# Show findings and PDF link
|
| 886 |
-
st.success("β
Analysis completed successfully!")
|
| 887 |
-
|
| 888 |
-
# Separate thinking process from findings
|
| 889 |
-
thinking_text = ""
|
| 890 |
-
report_text = findings
|
| 891 |
-
|
| 892 |
-
# Debug: Show raw findings
|
| 893 |
-
# st.write("DEBUG - Raw findings:", findings[:200] + "..." if len(findings) > 200 else findings)
|
| 894 |
-
|
| 895 |
-
# Check if findings contains "thinking:" section
|
| 896 |
-
if "thinking:" in findings.lower():
|
| 897 |
-
parts = findings.split("answer:", 1)
|
| 898 |
-
if len(parts) == 2:
|
| 899 |
-
# Extract thinking part (remove "thinking:" prefix)
|
| 900 |
-
thinking_part = parts[0].strip()
|
| 901 |
-
if thinking_part.lower().startswith("thinking:"):
|
| 902 |
-
thinking_text = thinking_part[9:].strip() # Remove "thinking:" prefix
|
| 903 |
-
else:
|
| 904 |
-
thinking_text = thinking_part
|
| 905 |
-
|
| 906 |
-
# Extract answer part
|
| 907 |
-
report_text = parts[1].strip()
|
| 908 |
-
|
| 909 |
-
# Debug: Show extracted parts
|
| 910 |
-
# st.write("DEBUG - Thinking text:", thinking_text[:100] + "..." if len(thinking_text) > 100 else thinking_text)
|
| 911 |
-
# st.write("DEBUG - Report text:", report_text[:100] + "..." if len(report_text) > 100 else report_text)
|
| 912 |
-
else:
|
| 913 |
-
# If no "answer:" found, use the whole thinking section
|
| 914 |
-
thinking_text = findings
|
| 915 |
-
report_text = "Analysis completed."
|
| 916 |
-
|
| 917 |
-
stream_response(thinking_text, report_text, patient_info)
|
| 918 |
-
|
| 919 |
# Generate PDF report
|
| 920 |
-
with st.spinner("Generating PDF report..."):
|
| 921 |
try:
|
| 922 |
pdf_response = requests.post(
|
| 923 |
f"{FASTAPI_BASE_URL}/generate-report/",
|
| 924 |
-
|
| 925 |
"patient_name": name,
|
| 926 |
-
"medical_record_number": medical_record_number,
|
| 927 |
"date_of_birth": date_of_birth,
|
| 928 |
"gender": gender,
|
| 929 |
"referring_physician": referring_physician,
|
| 930 |
-
"
|
| 931 |
"findings": findings
|
| 932 |
},
|
| 933 |
timeout=30
|
|
@@ -936,7 +924,7 @@ def generate_report(name, date_of_birth, gender, medical_record_number,
|
|
| 936 |
if pdf_response.status_code == 200:
|
| 937 |
pdf_data = pdf_response.json()
|
| 938 |
if pdf_data.get("success"):
|
| 939 |
-
pdf_url = f"{FASTAPI_BASE_URL}/reports/{name}/pdf"
|
| 940 |
st.markdown(f'''
|
| 941 |
<div style="margin-top: 1rem; padding: 1rem; background: linear-gradient(135deg, #4caf50, #66bb6a); border-radius: 10px;">
|
| 942 |
<p style="color: white; margin: 0; font-weight: 600;">β
PDF Report Generated Successfully!</p>
|
|
@@ -946,21 +934,21 @@ def generate_report(name, date_of_birth, gender, medical_record_number,
|
|
| 946 |
</div>
|
| 947 |
''', unsafe_allow_html=True)
|
| 948 |
else:
|
| 949 |
-
st.error(f"β Failed to generate PDF: {pdf_data.get('
|
| 950 |
else:
|
| 951 |
st.error(f"β PDF generation failed with status {pdf_response.status_code}")
|
| 952 |
|
| 953 |
except requests.exceptions.Timeout:
|
| 954 |
-
st.error("
|
| 955 |
except Exception as e:
|
| 956 |
st.error(f"β Error generating PDF: {str(e)}")
|
| 957 |
|
| 958 |
except Exception as e:
|
| 959 |
-
st.error(f"β Unexpected error
|
| 960 |
-
st.info("π‘
|
| 961 |
-
st.info(" β’
|
| 962 |
-
st.info(" β’
|
| 963 |
-
st.info(" β’
|
| 964 |
|
| 965 |
def view_reports_page():
|
| 966 |
st.markdown('<div class="section-header">π View Patient Reports - Search by MRN</div>', unsafe_allow_html=True)
|
|
|
|
| 775 |
uploaded_file = st.file_uploader(
|
| 776 |
"Choose medical image",
|
| 777 |
type=['png', 'jpg', 'jpeg', 'dcm'],
|
| 778 |
+
help="Upload X-ray, CT, MRI, (Max 10MB)",
|
| 779 |
+
key="medical_image_upload"
|
| 780 |
)
|
| 781 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 782 |
|
| 783 |
+
# Display uploaded image with modern styling (NO API CALLS)
|
| 784 |
if uploaded_file is not None:
|
| 785 |
try:
|
| 786 |
+
# Validate file size only (no API calls)
|
| 787 |
file_size = uploaded_file.size
|
| 788 |
if file_size > 10 * 1024 * 1024: # 10MB
|
| 789 |
st.error("β οΈ File size too large. Please upload an image smaller than 10MB.")
|
| 790 |
else:
|
| 791 |
+
# Display success message and file info
|
| 792 |
+
st.success(f"β
File uploaded successfully: {uploaded_file.name}")
|
| 793 |
+
st.info(f"π File size: {file_size/1024:.1f} KB")
|
| 794 |
|
| 795 |
+
# Display image preview (local only, no backend calls)
|
| 796 |
+
if uploaded_file.type.startswith('image/'):
|
| 797 |
+
image = Image.open(uploaded_file)
|
| 798 |
+
st.image(image, caption="Uploaded Medical Image", use_column_width=True)
|
| 799 |
+
else:
|
| 800 |
+
st.info("π DICOM file uploaded successfully")
|
| 801 |
|
| 802 |
# Reset file pointer for later use
|
| 803 |
uploaded_file.seek(0)
|
|
|
|
| 805 |
except Exception as e:
|
| 806 |
st.error(f"β Error processing image: {str(e)}")
|
| 807 |
st.info("π‘ Please ensure the file is a valid image format (PNG, JPG, JPEG, DCM)")
|
| 808 |
+
else:
|
| 809 |
+
st.info("π Please upload a medical image to begin analysis")
|
| 810 |
|
| 811 |
submit_button = st.form_submit_button("π Generate Report", use_container_width=True)
|
| 812 |
|
|
|
|
| 830 |
"""Generate report by sending data to backend and displaying the result"""
|
| 831 |
try:
|
| 832 |
if uploaded_file is not None:
|
| 833 |
+
with st.spinner("π Analyzing medical image..."):
|
| 834 |
# Validate file before sending
|
| 835 |
if uploaded_file.size > 10 * 1024 * 1024: # 10MB limit
|
| 836 |
st.error("β οΈ File size too large. Please upload an image smaller than 10MB.")
|
|
|
|
| 839 |
# Reset file pointer to beginning
|
| 840 |
uploaded_file.seek(0)
|
| 841 |
|
| 842 |
+
# Prepare file for upload
|
| 843 |
+
files = {"file": (uploaded_file.name, uploaded_file.getvalue(), uploaded_file.type)}
|
| 844 |
|
| 845 |
try:
|
| 846 |
+
# Call analyze endpoint
|
| 847 |
analyze_response = requests.post(
|
| 848 |
+
f"{FASTAPI_BASE_URL}/analyze",
|
| 849 |
files=files,
|
| 850 |
timeout=60 # 60 second timeout
|
| 851 |
)
|
| 852 |
|
| 853 |
+
if analyze_response.status_code == 200:
|
| 854 |
+
response_data = analyze_response.json()
|
| 855 |
+
|
| 856 |
+
# Handle successful response
|
| 857 |
+
if response_data.get("success"):
|
| 858 |
+
findings = response_data.get("analysis", "Analysis completed")
|
| 859 |
+
|
| 860 |
+
# Parse thinking and answer sections
|
| 861 |
+
thinking_text = ""
|
| 862 |
+
report_text = findings
|
| 863 |
+
|
| 864 |
+
if "thinking:" in findings and "answer:" in findings:
|
| 865 |
+
parts = findings.split("answer:", 1)
|
| 866 |
+
thinking_text = parts[0].replace("thinking:", "").strip()
|
| 867 |
+
report_text = parts[1].strip()
|
| 868 |
+
|
| 869 |
+
st.success("β
Analysis completed successfully!")
|
| 870 |
+
stream_response(thinking_text, report_text, {
|
| 871 |
+
"name": name,
|
| 872 |
+
"medical_record_number": medical_record_number,
|
| 873 |
+
"referring_physician": referring_physician,
|
| 874 |
+
"date_of_study": date_of_study
|
| 875 |
+
})
|
| 876 |
+
|
| 877 |
+
else:
|
| 878 |
+
# Handle failed analysis with fallback
|
| 879 |
+
st.warning("β οΈ AI analysis encountered issues, using fallback analysis")
|
| 880 |
+
fallback_findings = response_data.get("analysis", "Medical image processed. Professional review recommended.")
|
| 881 |
+
stream_response("Fallback analysis used", fallback_findings, {
|
| 882 |
+
"name": name,
|
| 883 |
+
"medical_record_number": medical_record_number,
|
| 884 |
+
"referring_physician": referring_physician,
|
| 885 |
+
"date_of_study": date_of_study
|
| 886 |
+
})
|
| 887 |
+
findings = fallback_findings
|
| 888 |
+
|
| 889 |
+
else:
|
| 890 |
+
st.error(f"β Backend Error (Status {analyze_response.status_code})")
|
| 891 |
+
st.error(f"Response: {analyze_response.text}")
|
| 892 |
return
|
| 893 |
|
|
|
|
|
|
|
|
|
|
| 894 |
|
| 895 |
except requests.exceptions.Timeout:
|
| 896 |
+
st.error("β±οΈ Request timed out. The analysis is taking too long. Please try again.")
|
| 897 |
return
|
| 898 |
except requests.exceptions.ConnectionError:
|
| 899 |
+
st.error("π Cannot connect to backend service.")
|
| 900 |
+
st.error("Please ensure the backend is running at: http://localhost:8001")
|
| 901 |
+
st.info("π‘ To start the backend, run: `cd backend && python service.py`")
|
| 902 |
return
|
| 903 |
except requests.exceptions.RequestException as e:
|
| 904 |
st.error(f"β Request failed: {str(e)}")
|
| 905 |
return
|
| 906 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 907 |
# Generate PDF report
|
| 908 |
+
with st.spinner("π Generating PDF report..."):
|
| 909 |
try:
|
| 910 |
pdf_response = requests.post(
|
| 911 |
f"{FASTAPI_BASE_URL}/generate-report/",
|
| 912 |
+
data={
|
| 913 |
"patient_name": name,
|
| 914 |
+
"medical_record_number": medical_record_number or "AUTO-GENERATED",
|
| 915 |
"date_of_birth": date_of_birth,
|
| 916 |
"gender": gender,
|
| 917 |
"referring_physician": referring_physician,
|
| 918 |
+
"study_date": date_of_study,
|
| 919 |
"findings": findings
|
| 920 |
},
|
| 921 |
timeout=30
|
|
|
|
| 924 |
if pdf_response.status_code == 200:
|
| 925 |
pdf_data = pdf_response.json()
|
| 926 |
if pdf_data.get("success"):
|
| 927 |
+
pdf_url = f"{FASTAPI_BASE_URL}/reports/{name.replace(' ', '_')}/pdf"
|
| 928 |
st.markdown(f'''
|
| 929 |
<div style="margin-top: 1rem; padding: 1rem; background: linear-gradient(135deg, #4caf50, #66bb6a); border-radius: 10px;">
|
| 930 |
<p style="color: white; margin: 0; font-weight: 600;">β
PDF Report Generated Successfully!</p>
|
|
|
|
| 934 |
</div>
|
| 935 |
''', unsafe_allow_html=True)
|
| 936 |
else:
|
| 937 |
+
st.error(f"β Failed to generate PDF: {pdf_data.get('message', 'Unknown error')}")
|
| 938 |
else:
|
| 939 |
st.error(f"β PDF generation failed with status {pdf_response.status_code}")
|
| 940 |
|
| 941 |
except requests.exceptions.Timeout:
|
| 942 |
+
st.error("β±οΈ PDF generation timed out. Please try again.")
|
| 943 |
except Exception as e:
|
| 944 |
st.error(f"β Error generating PDF: {str(e)}")
|
| 945 |
|
| 946 |
except Exception as e:
|
| 947 |
+
st.error(f"β Unexpected error: {str(e)}")
|
| 948 |
+
st.info("π‘ Troubleshooting tips:")
|
| 949 |
+
st.info(" β’ Ensure backend service is running on port 8001")
|
| 950 |
+
st.info(" β’ Check that the uploaded file is a valid image")
|
| 951 |
+
st.info(" β’ Try refreshing the page and uploading again")
|
| 952 |
|
| 953 |
def view_reports_page():
|
| 954 |
st.markdown('<div class="section-header">π View Patient Reports - Search by MRN</div>', unsafe_allow_html=True)
|