File size: 10,112 Bytes
310dade 6145bc0 79d193c 19b059b e9c724f 864b488 310dade 19b059b 310dade 19b059b 0b77233 19b059b 864b488 8d6f871 0b77233 e9c724f 0b77233 864b488 8d6f871 19b059b 0b77233 19b059b 0b77233 0a2437f 19b059b 79d193c 0b77233 19b059b 0b77233 19b059b 0a2437f 19b059b 310dade 0b77233 19b059b 0b77233 19b059b 0b77233 64f8a92 0b77233 79d193c 19b059b 310dade 19b059b 04ae885 19b059b 310dade 04ae885 19b059b 79d193c 19b059b 04ae885 79d193c 310dade 19b059b 8d6f871 04ae885 19b059b 8d6f871 19b059b 8d6f871 19b059b 8d6f871 19b059b 04ae885 19b059b 8d6f871 19b059b 04ae885 19b059b 79d193c 8d6f871 0b77233 e9c724f 64f8a92 0b77233 19b059b 0b77233 19b059b 0b77233 19b059b 0b77233 19b059b 310dade 0b77233 310dade 8d6f871 0b77233 19b059b 0b77233 19b059b 0b77233 19b059b 310dade 19b059b 8d6f871 19b059b 310dade 19b059b 8d6f871 19b059b 79d193c 8d6f871 19b059b 0b77233 8d6f871 19b059b 8d6f871 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# /home/user/app/app.py
import streamlit as st
from pathlib import Path
from datetime import datetime
from sqlmodel import select
from config.settings import settings
from models import (
create_db_and_tables,
get_session_context,
User,
ChatMessage,
ChatSession
)
from models.user import UserCreate
from services.auth import create_user_in_db, authenticate_user
from services.logger import app_logger
from assets.logo import get_logo_path
# --- Page Configuration ---
st.set_page_config(
page_title=settings.APP_TITLE,
page_icon="⚕️",
layout="wide",
initial_sidebar_state="expanded"
)
# --- Database Initialization ---
@st.cache_resource
def init_db():
app_logger.info("Initializing database and tables...")
create_db_and_tables()
app_logger.info("Database initialized.")
init_db()
# --- Session State Initialization ---
if 'authenticated_user_id' not in st.session_state:
st.session_state.authenticated_user_id = None
if 'authenticated_username' not in st.session_state:
st.session_state.authenticated_username = None
if 'current_chat_session_id' not in st.session_state:
st.session_state.current_chat_session_id = None
if 'chat_messages' not in st.session_state:
st.session_state.chat_messages = []
if 'user_understands_disclaimer' not in st.session_state:
st.session_state.user_understands_disclaimer = False
# --- Disclaimer Function ---
def display_disclaimer():
st.warning(f"**Important Disclaimer:** {settings.MAIN_DISCLAIMER_LONG}")
st.info(settings.SIMULATION_DISCLAIMER)
if st.button("I Understand and Agree to Proceed", key="disclaimer_agree_btn"):
st.session_state.user_understands_disclaimer = True
st.rerun()
# --- Authentication Logic ---
def display_login_form():
# (This function remains the same as the last full app.py provided, which had the fix for
# `Instance '<ChatSession ...>' is not persistent within this Session`.
# Ensure that version is used here for robustness.)
with st.form("login_form"):
st.subheader("Login")
username_input = st.text_input("Username", key="login_username_input_main_app")
password_input = st.text_input("Password", type="password", key="login_password_input_main_app")
submit_button = st.form_submit_button("Login")
if submit_button:
user_object_from_auth = authenticate_user(username_input, password_input)
if user_object_from_auth:
st.success(f"Welcome back, {username_input}!")
app_logger.info(f"User '{username_input}' authenticated successfully.")
try:
with get_session_context() as db_session:
statement = select(User).where(User.username == username_input)
live_user = db_session.exec(statement).first()
if not live_user:
st.error("Auth inconsistency. User not found post-login.")
app_logger.error(f"CRITICAL: User '{username_input}' auth OK but NOT FOUND in DB.")
st.session_state.authenticated_user_id = None
st.session_state.authenticated_username = None
st.rerun()
return
st.session_state.authenticated_user_id = live_user.id
st.session_state.authenticated_username = live_user.username
app_logger.info(f"Stored user ID {live_user.id} and username '{live_user.username}' in session state.")
new_chat_session = ChatSession(
user_id=live_user.id,
title=f"Session for {live_user.username} - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
)
db_session.add(new_chat_session)
app_logger.debug("New ChatSession added to SQLModel session.")
db_session.flush()
app_logger.debug(f"Session flushed. new_chat_session ID: {new_chat_session.id}")
if new_chat_session.id is None:
app_logger.error("CRITICAL: new_chat_session.id is None after flush.")
raise Exception("Failed to obtain ID for new chat session after flush.")
db_session.refresh(new_chat_session)
app_logger.debug("new_chat_session refreshed.")
st.session_state.current_chat_session_id = new_chat_session.id
st.session_state.chat_messages = []
app_logger.info(f"New chat session (ID: {new_chat_session.id}) created for user '{live_user.username}'.")
app_logger.info(f"Post-login setup complete for '{username_input}'. Rerunning app.")
st.rerun()
except Exception as e:
app_logger.error(f"Error during post-login session setup for '{username_input}': {e}", exc_info=True)
st.error(f"Could not complete login process: {e}")
st.session_state.authenticated_user_id = None
st.session_state.authenticated_username = None
st.session_state.current_chat_session_id = None
else:
st.error("Invalid username or password.")
app_logger.warning(f"Failed login attempt for username: {username_input}")
def display_signup_form():
# (This function remains largely the same as the last full app.py provided.
# Ensure it correctly calls your `create_user_in_db` which handles hashing and DB commit.)
with st.form("signup_form"):
st.subheader("Sign Up")
new_username = st.text_input("Choose a Username", key="signup_username_input_main_app")
new_email = st.text_input("Email (Optional)", key="signup_email_input_main_app")
new_password = st.text_input("Choose a Password", type="password", key="signup_password_input_main_app")
confirm_password = st.text_input("Confirm Password", type="password", key="signup_confirm_password_input_main_app")
submit_button = st.form_submit_button("Sign Up")
if submit_button:
if not new_username or not new_password: st.error("Username and password are required.")
elif len(new_password) < 6: st.error("Password must be at least 6 characters long.")
elif new_password != confirm_password: st.error("Passwords do not match.")
else:
user_data = UserCreate(username=new_username, password=new_password, email=new_email if new_email else None)
user = create_user_in_db(user_data)
if user:
st.success(f"Account created for {new_username}. Please log in.")
app_logger.info(f"Account created for '{new_username}'.")
else:
st.error("Username/Email might be taken, or another error occurred. Check logs or try different credentials.")
app_logger.warning(f"Signup failed for '{new_username}'. create_user_in_db returned None.")
# --- Main App Logic ---
if not st.session_state.get("authenticated_user_id"): # Not logged in
if not st.session_state.user_understands_disclaimer:
st.title(f"Welcome to {settings.APP_TITLE}")
st.markdown("Your AI-powered partner for advanced healthcare insights.")
st.markdown("---")
display_disclaimer()
st.stop() # Stop further execution until disclaimer is agreed to
else:
# Disclaimer agreed, show login/signup
st.title(f"Welcome to {settings.APP_TITLE}")
st.caption(settings.MAIN_DISCLAIMER_SHORT) # Short disclaimer always visible on login page
login_tab, signup_tab = st.tabs(["Login", "Sign Up"])
with login_tab:
display_login_form()
with signup_tab:
display_signup_form()
else:
# --- User is Authenticated ---
with st.sidebar:
logo_path_str_sidebar = get_logo_path()
if logo_path_str_sidebar:
logo_file_sidebar = Path(logo_path_str_sidebar)
if logo_file_sidebar.exists():
try: st.image(str(logo_file_sidebar), width=100)
except Exception as e: app_logger.warning(f"Could not display sidebar logo: {e}")
elif settings.APP_TITLE: st.sidebar.markdown(f"#### {settings.APP_TITLE}")
username_for_display = st.session_state.get("authenticated_username", "User")
st.sidebar.markdown(f"### Welcome, {username_for_display}!")
st.sidebar.info(settings.MAIN_DISCLAIMER_SHORT) # Constant reminder
st.sidebar.markdown("---")
if st.sidebar.button("Logout", key="sidebar_logout_button_main_app"):
logged_out_username = st.session_state.get("authenticated_username", "UnknownUser")
app_logger.info(f"User '{logged_out_username}' logging out.")
# Clear all session state, or selectively
for key in list(st.session_state.keys()): # Clear most session state keys on logout
if key not in ['query_params']: # Keep essential Streamlit keys if needed
del st.session_state[key]
st.session_state.user_understands_disclaimer = False # Require disclaimer agreement again
st.success("You have been logged out.")
st.rerun()
# Main content area: Streamlit MPA handles this by showing the selected page
# from the `pages/` directory.
# If on app.py itself, give a pointer.
current_page = st.session_state.get('page_script_hash', None)
if not current_page or current_page == st.elements.utils.calc_page_script_hash('app.py'):
st.sidebar.success("Select a page from the navigation.")
st.info("Please select a page from the sidebar (e.g., Home, Consult, Reports) to begin.")
app_logger.info(f"Streamlit app '{settings.APP_TITLE}' (app.py) processed.") |