Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 171 additions & 30 deletions report_analyst/core/cache_manager.py

Large diffs are not rendered by default.

24 changes: 15 additions & 9 deletions report_analyst/core/dataframe_manager.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
# ruff: noqa: BLE001, E501, E722, S307
import logging
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Tuple

import pandas as pd

Expand All @@ -26,7 +26,7 @@ def format_list_field(field: Any) -> str:
chunk = item.get("chunk", "Unknown")
formatted_items.append(f"• {text} [Chunk {chunk}]")
else:
formatted_items.append(f"• {str(item)}")
formatted_items.append(f"• {item!s}")
return "\n".join(formatted_items)
return str(field)

Expand All @@ -40,7 +40,7 @@ def extract_evidence_text(evidence: Any) -> str:
return str(evidence)


def create_analysis_dataframes(cached_results: Dict, file_key: str = None) -> Tuple[pd.DataFrame, pd.DataFrame]:
def create_analysis_dataframes(cached_results: Dict, file_key: str | None = None) -> Tuple[pd.DataFrame, pd.DataFrame]:
"""Create analysis and chunks dataframes from database results."""
try:
analysis_rows = []
Expand All @@ -57,19 +57,25 @@ def create_analysis_dataframes(cached_results: Dict, file_key: str = None) -> Tu
logger.info(f"Processing question {question_id} with keys: {list(result.keys())}")

# Create analysis row
score = result.get("SCORE", 0)
try:
score = float(score) if score is not None else 0
except (ValueError, TypeError):
score = 0

analysis_row = {
"Question ID": question_id,
"Analysis": result.get("ANSWER", ""),
"Score": float(result.get("SCORE", 0)),
"Score": score,
"Key Evidence": format_list_field(result.get("EVIDENCE", [])),
"Gaps": format_list_field(result.get("GAPS", [])),
"Sources": format_list_field(result.get("SOURCES", [])),
}
analysis_rows.append(analysis_row)
logger.info(f"Added analysis row for question {question_id}")

# Process chunks - use exactly what's in the database
chunks = data.get("chunks", [])
# Process chunks - check both result and data for chunks
chunks = result.get("chunks", data.get("chunks", []))
logger.info(f"Processing {len(chunks)} chunks for question {question_id}")

for chunk in chunks:
Expand All @@ -90,7 +96,7 @@ def create_analysis_dataframes(cached_results: Dict, file_key: str = None) -> Tu
)

except Exception as e:
logger.error(f"Error processing result for question {question_id}: {str(e)}")
logger.error(f"Error processing result for question {question_id}: {e!s}")
logger.error(f"Result data: {data}")
continue

Expand All @@ -111,7 +117,7 @@ def create_analysis_dataframes(cached_results: Dict, file_key: str = None) -> Tu
return analysis_df, chunks_df

except Exception as e:
logger.error(f"Error creating dataframes: {str(e)}")
logger.error(f"Error creating dataframes: {e!s}")
return pd.DataFrame(), pd.DataFrame()


Expand Down
119 changes: 114 additions & 5 deletions report_analyst/streamlit_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def is_api_key_missing_message(message: str) -> bool:
)
from report_analyst.core.prompt_manager import PromptManager
from report_analyst.core.question_loader import get_question_loader
from report_analyst.ui.view_report_page import render_view_report_page

# Load environment variables
load_dotenv()
Expand Down Expand Up @@ -808,13 +809,29 @@ def get_uploaded_files_history(backend_config=None) -> List[Dict]:
return result


def display_analysis_results(analysis_df: pd.DataFrame, chunks_df: pd.DataFrame, file_key: str | None = None) -> None:
def display_analysis_results(
analysis_df: pd.DataFrame,
chunks_df: pd.DataFrame,
file_key: str | None = None,
file_path: str | None = None,
question_set: str | None = None,
) -> None:
"""Display analysis results in a consistent format for both individual and consolidated views"""
try:
if analysis_df.empty:
st.warning("No analysis results to display")
return


# Try to import and use PDF viewer component if available
pdf_viewer_available = False
try:
from report_analyst_enterprise.components.streamlit_component.backend import pdf_viewer

pdf_viewer_available = True
except ImportError:
pass

# Analysis Results Table
st.subheader("Analysis Results")
st.dataframe(
Expand Down Expand Up @@ -849,6 +866,82 @@ def display_analysis_results(analysis_df: pd.DataFrame, chunks_df: pd.DataFrame,
},
)

if pdf_viewer_available and file_path and not chunks_df.empty:
try:
if not question_set:
question_set = st.session_state.get("question_set", "tcfd")

question_set_obj = question_loader.get_question_set(question_set)
questions_data = {}
if question_set_obj:
for q_id, q_data in question_set_obj.questions.items():
questions_data[q_id] = q_data.get("text", q_id)

chunks_by_question: dict = {}
try:
from report_analyst.core.analyzer import DocumentAnalyzer

doc_analyzer = DocumentAnalyzer()
config = {
"chunk_size": st.session_state.get("chunk_size", 500),
"chunk_overlap": st.session_state.get("chunk_overlap", 0),
"top_k": st.session_state.get("top_k", 10),
"model": st.session_state.get("llm_model", "gpt-4o-mini"),
"question_set": question_set,
}
cached_results = doc_analyzer.cache_manager.get_analysis(
file_path=file_path,
config=config,
)
if cached_results:
for q_id, data in cached_results.items():
if q_id not in chunks_by_question:
chunks_by_question[q_id] = []
chunks = data.get("chunks", [])
for chunk in chunks:
metadata = chunk.get("metadata") or {}
if "page_number" not in metadata and "source" in metadata:
try:
metadata["page_number"] = int(metadata["source"])
except (ValueError, TypeError):
metadata["page_number"] = 1
elif "page_number" in metadata:
try:
metadata["page_number"] = int(metadata["page_number"])
except (ValueError, TypeError):
metadata["page_number"] = 1
else:
metadata["page_number"] = 1
chunk["metadata"] = metadata
chunks_by_question[q_id].extend(chunks)
except Exception as cache_error:
logger.debug(f"Could not get chunks from cache: {cache_error}")
for _, row in chunks_df.iterrows():
q_id = row.get("Question ID", "")
if q_id not in chunks_by_question:
chunks_by_question[q_id] = []
chunks_by_question[q_id].append(
{
"text": row.get("Chunk Text", ""),
"metadata": {},
"is_evidence": row.get("Is Evidence", False),
"similarity_score": row.get("Vector Similarity", 0.0),
"llm_score": row.get("LLM Score"),
"chunk_order": row.get("Position", 0),
}
)

with st.expander("PDF Viewer with Chunks", expanded=False):
pdf_viewer(
pdf_path=file_path,
chunks_data=chunks_by_question,
questions_data=questions_data,
height=800,
key=f"pdf_viewer_{file_key}" if file_key else "pdf_viewer",
)
except Exception as e:
logger.warning(f"Could not display PDF viewer: {e}", exc_info=True)

# Document Chunks Table
if not chunks_df.empty:
st.subheader("Document Chunks")
Expand Down Expand Up @@ -1249,7 +1342,13 @@ def display_consolidated_results(analyzer, question_set, file_path=None, selecte

# Display results using the existing display function
file_key = f"{Path(file_path).stem}_cs{selected_config['config']['chunk_size']}"
display_analysis_results(analysis_df, chunks_df, file_key)
display_analysis_results(
analysis_df,
chunks_df,
file_key,
file_path=file_path,
question_set=question_set,
)
else:
st.warning("No results found in stored for this configuration")
else:
Expand Down Expand Up @@ -2856,10 +2955,11 @@ def main():
options=[
"Upload Report",
"Report Analyst",
"View Report",
"All Results",
"Settings",
],
icons=["house", "file-text", "bar-chart", "gear"],
icons=["house", "file-text", "file-pdf", "bar-chart", "gear"],
menu_icon=None,
default_index=0,
orientation="vertical",
Expand Down Expand Up @@ -2889,7 +2989,7 @@ def main():
)
except ImportError:
# Fallback to regular radio if package not installed
nav_options = ["Upload Report", "Report Analyst", "All Results", "Settings"]
nav_options = ["Upload Report", "Report Analyst", "View Report", "All Results", "Settings"]
nav_page = st.sidebar.radio("", nav_options, key="nav_page", label_visibility="collapsed")

# Show page-specific content based on navigation
Expand Down Expand Up @@ -3978,7 +4078,13 @@ def main():
if all_results:
analysis_df, chunks_df = create_analysis_dataframes(all_results)
file_key = Path(file_path).stem
display_analysis_results(analysis_df, chunks_df, file_key)
display_analysis_results(
analysis_df,
chunks_df,
file_key,
file_path=str(file_path),
question_set=config.get("question_set", st.session_state.get("question_set", "tcfd")),
)
progress_text.success(f"✓ Analysis complete for {len(selected_questions)} questions")
else:
progress_text.error("No results found after analysis")
Expand Down Expand Up @@ -4285,6 +4391,9 @@ def main():
st.session_state.file_processed = True
st.rerun()

elif nav_page == "View Report":
render_view_report_page(analyzer, question_sets, get_uploaded_files_history)

# All Results page
elif nav_page == "All Results":
st.header("View All Results")
Expand Down
Empty file added report_analyst/ui/__init__.py
Empty file.
Loading
Loading