Update pages/3_Reports.py
Browse files- pages/3_Reports.py +80 -119
pages/3_Reports.py
CHANGED
@@ -5,19 +5,17 @@ from typing import List, Dict, Any
|
|
5 |
from sqlmodel import select
|
6 |
|
7 |
from config.settings import settings
|
8 |
-
from models import ChatMessage, ChatSession #
|
9 |
from models.db import get_session_context
|
10 |
-
from services.pdf_report import generate_pdf_report
|
11 |
from services.logger import app_logger
|
12 |
from services.metrics import log_report_generated
|
13 |
|
14 |
-
# --- Authentication Check
|
15 |
if not st.session_state.get("authenticated_user_id"):
|
16 |
st.warning("Please log in to access reports.")
|
17 |
-
try:
|
18 |
-
|
19 |
-
except st.errors.StreamlitAPIException:
|
20 |
-
st.info("Please navigate to the main login page manually.")
|
21 |
st.stop()
|
22 |
|
23 |
authenticated_user_id = st.session_state.get("authenticated_user_id")
|
@@ -26,27 +24,25 @@ app_logger.info(f"User '{authenticated_username}' (ID: {authenticated_user_id})
|
|
26 |
|
27 |
st.title("Consultation Reports")
|
28 |
st.markdown("View and download your past consultation sessions.")
|
|
|
|
|
29 |
|
30 |
-
# --- Helper Function to Load User's Chat Session Data (Primitives) ---
|
31 |
-
# @st.cache_data(ttl=300)
|
32 |
def get_user_chat_session_display_data(user_id: int) -> List[Dict[str, Any]]:
|
33 |
-
app_logger.debug(f"Fetching
|
34 |
session_data_list: List[Dict[str, Any]] = []
|
35 |
try:
|
36 |
-
with get_session_context() as
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
results = db_session.exec(statement).all()
|
41 |
for row in results:
|
42 |
session_data_list.append({
|
43 |
-
"id": row.id,
|
44 |
-
"
|
45 |
-
"start_time": row.start_time
|
46 |
})
|
47 |
-
app_logger.debug(f"Found {len(session_data_list)} session display entries for
|
48 |
except Exception as e:
|
49 |
-
app_logger.error(f"Error fetching
|
50 |
st.error("Could not load your chat sessions.")
|
51 |
return session_data_list
|
52 |
|
@@ -59,128 +55,93 @@ if not chat_session_display_items:
|
|
59 |
session_options = []
|
60 |
for s_data in chat_session_display_items:
|
61 |
start_time_obj = s_data["start_time"]
|
62 |
-
start_time_display = start_time_obj.strftime('%Y-%m-%d %H:%M') if start_time_obj else "
|
63 |
-
title_display = s_data["title"] if s_data["title"] else f"
|
64 |
display_string = f"ID: {s_data['id']} | Started: {start_time_display} | Title: {title_display}"
|
65 |
session_options.append((display_string, s_data['id']))
|
66 |
|
67 |
selected_option_tuple = st.selectbox(
|
68 |
-
"Select a Consultation Session:",
|
69 |
-
options=session_options,
|
70 |
-
format_func=lambda x: x[0],
|
71 |
-
index=0
|
72 |
)
|
73 |
|
74 |
-
# --- Store Fetched Message Data in Session State to Persist Across Reruns ---
|
75 |
-
# This key will be specific to the selected session to avoid conflicts if the user selects another session.
|
76 |
if selected_option_tuple:
|
77 |
selected_session_id = selected_option_tuple[1]
|
78 |
-
|
79 |
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
messages_data_for_display: List[Dict[str, Any]] = []
|
84 |
try:
|
85 |
-
with get_session_context() as
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
results = db_session.exec(statement).all() # List of Row objects
|
95 |
-
|
96 |
-
for msg_row in results:
|
97 |
-
messages_data_for_display.append({
|
98 |
-
"role": msg_row.role,
|
99 |
-
"content": msg_row.content,
|
100 |
-
"timestamp": msg_row.timestamp, # Keep as datetime for formatting
|
101 |
-
"tool_name": msg_row.tool_name if hasattr(msg_row, 'tool_name') else None
|
102 |
})
|
103 |
-
st.session_state[
|
104 |
-
app_logger.info(f"Fetched and stored {len(messages_data_for_display)} messages for session {selected_session_id} in session_state.")
|
105 |
except Exception as e:
|
106 |
-
app_logger.error(f"Error fetching messages for session {selected_session_id}: {e}", exc_info=True)
|
107 |
-
st.error("Could not load messages for this
|
108 |
-
st.session_state[
|
109 |
else:
|
110 |
-
app_logger.debug(f"Using cached messages from session_state for session ID: {selected_session_id}")
|
111 |
-
|
112 |
-
|
113 |
|
114 |
-
|
115 |
-
selected_session_data = next((s_data for s_data in chat_session_display_items if s_data['id'] == selected_session_id), None)
|
116 |
|
117 |
if selected_session_data:
|
118 |
st.markdown("---")
|
119 |
-
st.subheader(f"
|
120 |
-
|
121 |
-
|
122 |
-
st.write(f"**Started:** {start_time_detail_display}")
|
123 |
st.write(f"**Title:** {selected_session_data['title'] or 'Untitled Session'}")
|
124 |
-
|
125 |
-
|
|
|
|
|
|
|
126 |
with st.expander("View Chat Transcript", expanded=False):
|
127 |
-
for
|
128 |
-
#
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
if
|
135 |
-
timestamp_display = timestamp_obj.strftime('%Y-%m-%d %H:%M:%S') if timestamp_obj else "Time N/A"
|
136 |
-
|
137 |
-
st.markdown(f"**{icon} {role.capitalize()}** ({timestamp_display})")
|
138 |
-
st.markdown(f"> ```\n{content}\n```")
|
139 |
-
if msg_idx < len(messages_data_for_display) - 1:
|
140 |
-
st.markdown("---")
|
141 |
|
142 |
st.markdown("---")
|
143 |
try:
|
144 |
-
#
|
145 |
-
#
|
146 |
-
#
|
147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
149 |
-
|
150 |
-
|
151 |
-
for msg_d in messages_data_for_display:
|
152 |
-
# This is a mock ChatMessage, not an ORM instance.
|
153 |
-
# Ensure your generate_pdf_report can handle this structure.
|
154 |
-
pdf_report_messages.append(
|
155 |
-
ChatMessage( # Re-create a temporary (non-ORM) ChatMessage-like object
|
156 |
-
role=msg_d["role"],
|
157 |
-
content=msg_d["content"],
|
158 |
-
timestamp=msg_d["timestamp"], # datetime object
|
159 |
-
tool_name=msg_d.get("tool_name") # Get tool_name if exists
|
160 |
-
# session_id and id are not strictly needed if generate_pdf_report doesn't use them.
|
161 |
-
)
|
162 |
-
)
|
163 |
-
|
164 |
-
pdf_bytes = generate_pdf_report(pdf_report_messages, patient_name=authenticated_username)
|
165 |
-
current_time_str = datetime.now().strftime('%Y%m%d_%H%M%S')
|
166 |
-
pdf_file_name = f"Consultation_Report_Session{selected_session_id}_{current_time_str}.pdf"
|
167 |
st.download_button(
|
168 |
-
label="Download Report as PDF",
|
169 |
-
|
170 |
-
|
171 |
-
mime="application/pdf",
|
172 |
-
key=f"download_pdf_btn_{selected_session_id}",
|
173 |
-
on_click=log_report_generated,
|
174 |
-
args=(authenticated_user_id, selected_session_id),
|
175 |
-
help="Click to download the consultation transcript as a PDF file."
|
176 |
)
|
177 |
except Exception as e:
|
178 |
-
app_logger.error(f"Error generating PDF for session {selected_session_id}
|
179 |
-
st.error(f"Could not generate PDF report
|
180 |
-
else:
|
181 |
-
|
182 |
-
|
183 |
-
app_logger.error(f"Selected session ID {selected_session_id} not found in fetched display data for user '{authenticated_username}'.")
|
184 |
-
st.error("Selected session could not be found. Please try again.")
|
185 |
-
else:
|
186 |
-
st.info("Please select a session from the dropdown menu to view details.")
|
|
|
5 |
from sqlmodel import select
|
6 |
|
7 |
from config.settings import settings
|
8 |
+
from models import ChatMessage, ChatSession # Model definitions
|
9 |
from models.db import get_session_context
|
10 |
+
from services.pdf_report import generate_pdf_report # Needs to accept more data
|
11 |
from services.logger import app_logger
|
12 |
from services.metrics import log_report_generated
|
13 |
|
14 |
+
# --- Authentication Check ---
|
15 |
if not st.session_state.get("authenticated_user_id"):
|
16 |
st.warning("Please log in to access reports.")
|
17 |
+
try: st.switch_page("app.py")
|
18 |
+
except st.errors.StreamlitAPIException: st.info("Please navigate to the main login page manually.")
|
|
|
|
|
19 |
st.stop()
|
20 |
|
21 |
authenticated_user_id = st.session_state.get("authenticated_user_id")
|
|
|
24 |
|
25 |
st.title("Consultation Reports")
|
26 |
st.markdown("View and download your past consultation sessions.")
|
27 |
+
st.info(settings.MAIN_DISCLAIMER_SHORT)
|
28 |
+
|
29 |
|
|
|
|
|
30 |
def get_user_chat_session_display_data(user_id: int) -> List[Dict[str, Any]]:
|
31 |
+
app_logger.debug(f"Fetching session display data for user_id: {user_id}")
|
32 |
session_data_list: List[Dict[str, Any]] = []
|
33 |
try:
|
34 |
+
with get_session_context() as db:
|
35 |
+
stmt = select(ChatSession.id, ChatSession.title, ChatSession.start_time, ChatSession.patient_context_summary)\
|
36 |
+
.where(ChatSession.user_id == user_id).order_by(ChatSession.start_time.desc())
|
37 |
+
results = db.exec(stmt).all()
|
|
|
38 |
for row in results:
|
39 |
session_data_list.append({
|
40 |
+
"id": row.id, "title": row.title, "start_time": row.start_time,
|
41 |
+
"patient_context_summary": row.patient_context_summary
|
|
|
42 |
})
|
43 |
+
app_logger.debug(f"Found {len(session_data_list)} session display entries for user {user_id}")
|
44 |
except Exception as e:
|
45 |
+
app_logger.error(f"Error fetching session display data for user {user_id}: {e}", exc_info=True)
|
46 |
st.error("Could not load your chat sessions.")
|
47 |
return session_data_list
|
48 |
|
|
|
55 |
session_options = []
|
56 |
for s_data in chat_session_display_items:
|
57 |
start_time_obj = s_data["start_time"]
|
58 |
+
start_time_display = start_time_obj.strftime('%Y-%m-%d %H:%M') if start_time_obj else "N/A"
|
59 |
+
title_display = s_data["title"] if s_data["title"] else f"Consult on {start_time_display}"
|
60 |
display_string = f"ID: {s_data['id']} | Started: {start_time_display} | Title: {title_display}"
|
61 |
session_options.append((display_string, s_data['id']))
|
62 |
|
63 |
selected_option_tuple = st.selectbox(
|
64 |
+
"Select a Consultation Session:", options=session_options, format_func=lambda x: x[0], index=0
|
|
|
|
|
|
|
65 |
)
|
66 |
|
|
|
|
|
67 |
if selected_option_tuple:
|
68 |
selected_session_id = selected_option_tuple[1]
|
69 |
+
MESSAGES_KEY = f"report_messages_for_session_{selected_session_id}"
|
70 |
|
71 |
+
if MESSAGES_KEY not in st.session_state:
|
72 |
+
app_logger.info(f"Fetching messages for report, session ID: {selected_session_id}")
|
73 |
+
msg_data_list: List[Dict[str, Any]] = []
|
|
|
74 |
try:
|
75 |
+
with get_session_context() as db:
|
76 |
+
stmt = select(ChatMessage.role, ChatMessage.content, ChatMessage.timestamp, ChatMessage.tool_name)\
|
77 |
+
.where(ChatMessage.session_id == selected_session_id).order_by(ChatMessage.timestamp)
|
78 |
+
results = db.exec(stmt).all()
|
79 |
+
for row in results:
|
80 |
+
msg_data_list.append({
|
81 |
+
"role": row.role, "content": row.content, "timestamp": row.timestamp,
|
82 |
+
"tool_name": getattr(row, 'tool_name', None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
})
|
84 |
+
st.session_state[MESSAGES_KEY] = msg_data_list
|
|
|
85 |
except Exception as e:
|
86 |
+
app_logger.error(f"Error fetching messages for report (session {selected_session_id}): {e}", exc_info=True)
|
87 |
+
st.error("Could not load messages for this report.")
|
88 |
+
st.session_state[MESSAGES_KEY] = []
|
89 |
else:
|
90 |
+
app_logger.debug(f"Using cached messages from session_state for report, session ID: {selected_session_id}")
|
91 |
+
msg_data_list = st.session_state[MESSAGES_KEY]
|
|
|
92 |
|
93 |
+
selected_session_data = next((s for s in chat_session_display_items if s['id'] == selected_session_id), None)
|
|
|
94 |
|
95 |
if selected_session_data:
|
96 |
st.markdown("---")
|
97 |
+
st.subheader(f"Report Preview for Session ID: {selected_session_data['id']}")
|
98 |
+
start_t = selected_session_data["start_time"]
|
99 |
+
st.write(f"**Started:** {start_t.strftime('%Y-%m-%d %H:%M:%S UTC') if start_t else 'N/A'}")
|
|
|
100 |
st.write(f"**Title:** {selected_session_data['title'] or 'Untitled Session'}")
|
101 |
+
if selected_session_data.get("patient_context_summary"):
|
102 |
+
with st.expander("View Patient Context Provided", expanded=False):
|
103 |
+
st.markdown(selected_session_data["patient_context_summary"])
|
104 |
+
|
105 |
+
if msg_data_list:
|
106 |
with st.expander("View Chat Transcript", expanded=False):
|
107 |
+
for idx, msg_d in enumerate(msg_data_list):
|
108 |
+
if msg_d["role"] == "system": continue # Don't show system context messages here
|
109 |
+
icon = "🧑⚕️" if msg_d["role"] == "assistant" else "👤"
|
110 |
+
if msg_d["role"] == "tool": icon = "🛠️"
|
111 |
+
ts_display = msg_d["timestamp"].strftime('%Y-%m-%d %H:%M:%S') if msg_d["timestamp"] else "N/A"
|
112 |
+
st.markdown(f"**{icon} {msg_d['role'].capitalize()}** ({ts_display})")
|
113 |
+
st.markdown(f"> ```\n{msg_d['content']}\n```")
|
114 |
+
if idx < len(msg_data_list) - 1: st.markdown("---")
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
|
116 |
st.markdown("---")
|
117 |
try:
|
118 |
+
# Prepare data for PDF report
|
119 |
+
# This creates temporary ChatMessage-like objects.
|
120 |
+
# Ensure generate_pdf_report can handle these non-ORM objects.
|
121 |
+
pdf_report_messages = [
|
122 |
+
ChatMessage(**msg_d) for msg_d in msg_data_list # Spread dict into ChatMessage constructor
|
123 |
+
]
|
124 |
+
report_data_for_pdf = {
|
125 |
+
"patient_name": authenticated_username,
|
126 |
+
"session_id": selected_session_data['id'],
|
127 |
+
"session_title": selected_session_data['title'] or 'Untitled Session',
|
128 |
+
"session_start_time": selected_session_data['start_time'],
|
129 |
+
"patient_context_summary": selected_session_data.get('patient_context_summary', "Not provided."),
|
130 |
+
"messages": pdf_report_messages
|
131 |
+
}
|
132 |
+
|
133 |
+
pdf_bytes = generate_pdf_report(report_data_for_pdf) # Pass the whole dict
|
134 |
|
135 |
+
file_ts = datetime.now().strftime('%Y%m%d_%H%M%S')
|
136 |
+
pdf_fn = f"Consult_Report_S{selected_session_id}_{file_ts}.pdf"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
st.download_button(
|
138 |
+
label="Download Report as PDF", data=pdf_bytes, file_name=pdf_fn, mime="application/pdf",
|
139 |
+
key=f"dl_pdf_s{selected_session_id}", on_click=log_report_generated,
|
140 |
+
args=(authenticated_user_id, selected_session_id), help="Download consultation summary."
|
|
|
|
|
|
|
|
|
|
|
141 |
)
|
142 |
except Exception as e:
|
143 |
+
app_logger.error(f"Error generating PDF for session {selected_session_id}: {e}", exc_info=True)
|
144 |
+
st.error(f"Could not generate PDF report. Error: {type(e).__name__}")
|
145 |
+
else: st.info("This session has no messages for the report.")
|
146 |
+
else: st.error("Selected session data not found.")
|
147 |
+
else: st.info("Select a session to view details.")
|
|
|
|
|
|
|
|