-
Notifications
You must be signed in to change notification settings - Fork 1
unified document card
The Unified Document Card component provides a single, consistent design for displaying documents across all tabs in the BMLibrarian GUI. It maintains the same visual appearance while showing context-specific features based on where it's used.
- Collapsible Design: Title and score visible when collapsed, full details when expanded
- Consistent Appearance: Same look and feel across literature, scoring, citations, and counterfactual tabs
- Context-Specific Features: Scoring controls, citation highlights, etc. appear only when relevant
-
Smart Full-Text Access: Automatically shows the appropriate button based on PDF availability:
- "View Full Text" if PDF exists locally
- "Fetch Full Text" if URL is available
- "Upload Full Text" if no source is available
- Score Badges: Color-coded badges for AI scores (1-5 scale), human scores, and relevance scores (0-1 scale)
- Citation Highlighting: Automatically highlights cited passages in abstracts
UnifiedDocumentCard(page, pdf_manager=None, on_pdf_status_change=None)The main class that creates document cards with all features.
Parameters:
-
page: Flet page instance (required for dialogs) -
pdf_manager: Optional PDFManager instance for PDF operations -
on_pdf_status_change: Optional callback when PDF status changes(doc_id, status)
class DocumentCardContext:
LITERATURE = "literature" # Literature search results
SCORING = "scoring" # Document scoring view
CITATIONS = "citations" # Citations view
COUNTERFACTUAL = "counterfactual" # Counterfactual evidence
REPORT = "report" # Final report viewfrom bmlibrarian.gui.unified_document_card import UnifiedDocumentCard, DocumentCardContext
# Create card creator
card_creator = UnifiedDocumentCard(page)
# Create basic literature card
card = card_creator.create_card(
index=0,
doc=document_dict,
context=DocumentCardContext.LITERATURE
)def on_score_change(index: int, score: str, score_type: str):
"""Handle score override."""
print(f"Document {index} scored {score} ({score_type})")
def on_score_approve(index: int):
"""Handle AI score approval."""
print(f"AI score approved for document {index}")
# Create scoring card with human controls
card = card_creator.create_card(
index=0,
doc=document_dict,
context=DocumentCardContext.SCORING,
ai_score=4.5,
scoring_reasoning="This document directly answers the research question...",
show_scoring_controls=True,
on_score_change=on_score_change,
on_score_approve=on_score_approve
)citation_data = {
'summary': 'Brief summary of why this citation is relevant',
'passage': 'The exact passage extracted from the document...',
'relevance_score': 0.95
}
# Create citation card
card = card_creator.create_card(
index=0,
doc=document_dict,
context=DocumentCardContext.CITATIONS,
citation_data=citation_data,
relevance_score=0.95
)# Create card showing both AI and human scores
card = card_creator.create_card(
index=0,
doc=document_dict,
context=DocumentCardContext.SCORING,
ai_score=4.5,
human_score=5.0,
scoring_reasoning="Original AI assessment",
show_scoring_controls=False # Don't show controls, just display scores
)For simpler use cases, convenience functions are provided:
from bmlibrarian.gui.unified_document_card import (
create_literature_card,
create_scored_card,
create_citation_card
)
# Literature card
card = create_literature_card(page, index=0, doc=document_dict)
# Scored card
card = create_scored_card(
page, index=0, doc=document_dict,
ai_score=4.5, reasoning="...", show_controls=True
)
# Citation card
card = create_citation_card(
page, index=0, doc=document_dict, citation_data={...}
)The component expects documents in this format:
document = {
'id': 64396205, # Database ID
'title': 'Document Title',
'authors': ['Author 1', 'Author 2'], # List or comma-separated string
'publication': 'Journal Name',
'year': '2023',
'publication_date': '2023-01-01', # Optional, more precise than year
'abstract': 'Full abstract text...',
'pmid': '12345678', # Optional
'doi': '10.1234/example', # Optional
'pdf_path': '/path/to/file.pdf', # Optional, local PDF path
'pdf_url': 'https://...', # Optional, download URL
}The component automatically determines which button to show:
-
View Full Text (Blue): PDF exists at
pdf_path- Opens PDF in system viewer
- Uses
PDFViewerDialog.show_pdf()
-
Fetch Full Text (Orange):
pdf_urlis available but no local PDF- Downloads PDF from URL
- Saves to organized storage
- Opens after download
- Uses
PDFViewerDialog.download_and_show_pdf()
-
Upload Full Text (Green): No
pdf_pathorpdf_url- Shows file picker
- Copies selected PDF to organized storage
- Opens after import
- Uses
PDFViewerDialog.import_pdf()
The component intelligently displays scores in the collapsed title:
- Human score takes precedence if available (shown as "Human" badge)
- AI score shown if no human score (shown as "AI" badge)
- Relevance score for citations (shown as "Rel" badge)
- Color-coded based on value:
- 1-5 scale: Green (≥4.5), Blue (≥3.5), Orange (≥2.5), Red (<2.5)
- 0-1 scale: Green (≥0.8), Blue (≥0.6), Orange (≥0.4), Red (<0.4)
When citation_data is provided:
- Summary Section: Highlighted in green background
- Cited Passage: Yellow background with border and emoji marker
- Abstract Highlighting: Automatically finds and highlights the passage in the full abstract
- Relevance Badge: Shows 0-1 score in collapsed title
# OLD
from bmlibrarian.gui.document_card_utils import create_document_card
cards = [create_document_card(i, doc) for i, doc in enumerate(documents)]
# NEW
from bmlibrarian.gui.unified_document_card import create_literature_card
card_creator = UnifiedDocumentCard(page)
cards = [card_creator.create_card(i, doc, context=DocumentCardContext.LITERATURE)
for i, doc in enumerate(documents)]# OLD
from bmlibrarian.gui.document_card_utils import create_scored_document_cards_list
cards = create_scored_document_cards_list(scored_documents)
# NEW
from bmlibrarian.gui.unified_document_card import UnifiedDocumentCard, DocumentCardContext
card_creator = UnifiedDocumentCard(page)
cards = [
card_creator.create_card(
i, doc,
context=DocumentCardContext.SCORING,
ai_score=score,
scoring_reasoning=reasoning,
show_scoring_controls=True,
on_score_change=handle_score_change,
on_score_approve=handle_approve
)
for i, (doc, score, reasoning) in enumerate(scored_documents)
]# OLD
from bmlibrarian.gui.citation_card_utils import create_citation_card
cards = [create_citation_card(i, cit) for i, cit in enumerate(citations)]
# NEW
from bmlibrarian.gui.unified_document_card import create_citation_card
cards = [
create_citation_card(page, i, citation.document, citation_data={
'summary': citation.summary,
'passage': citation.passage,
'relevance_score': citation.relevance_score
})
for i, citation in enumerate(citations)
]To enable full PDF functionality, pass a PDFManager instance:
from bmlibrarian.utils.pdf_manager import PDFManager
pdf_manager = PDFManager()
card_creator = UnifiedDocumentCard(
page,
pdf_manager=pdf_manager,
on_pdf_status_change=lambda doc_id, status: print(f"PDF {status} for {doc_id}")
)The component matches the design shown in your screenshot:
-
Collapsed State:
- Title (truncated to 80 chars)
- Score badge (color-coded, rounded)
- Authors and publication info in subtitle
-
Expanded State:
- Full title (bold, blue)
- Authors (gray)
- Metadata section (gray background)
- Score section (if applicable)
- Citation sections (if applicable)
- Abstract (light gray background)
- Full-text button
-
Colors:
- Primary: Blue (#2196F3 family)
- Success: Green (#4CAF50 family)
- Warning: Orange (#FF9800 family)
- Error: Red (#F44336 family)
- Background: Gray (#ECEFF1 family)
Run the demo to see all card variations:
uv run python examples/unified_card_demo.pyThe demo shows:
- Basic literature search card
- Scoring card with human controls
- Citation card with highlighted passage
- Card with both AI and human scores
- Card with local PDF (View button)
- Card with URL (Fetch button)
- Card without PDF source (Upload button)
- Always use the same card creator instance within a tab to ensure consistent behavior
- Provide citation_data only in citation contexts to keep cards focused
- Show scoring controls only when needed (not for read-only views)
- Use appropriate context constants instead of string literals
- Handle callbacks appropriately - they may be called frequently
- Pass PDFManager instance if PDF functionality is needed
To migrate existing code to the unified card:
-
Replace imports:
# OLD from bmlibrarian.gui.document_card_utils import create_document_card from bmlibrarian.gui.citation_card_utils import create_citation_card # NEW from bmlibrarian.gui.unified_document_card import UnifiedDocumentCard, DocumentCardContext
-
Create card creator instance (once per tab):
card_creator = UnifiedDocumentCard(page)
-
Update card creation calls:
# Determine context context = DocumentCardContext.LITERATURE # or SCORING, CITATIONS, etc. # Create card with appropriate parameters card = card_creator.create_card( index=i, doc=doc, context=context, # Add context-specific parameters as needed )
-
Test thoroughly to ensure callbacks work as expected
Potential future improvements:
- Batch PDF operations (download multiple at once)
- PDF preview thumbnails
- Inline PDF viewer (embedded in card)
- Export citations in various formats
- Drag-and-drop PDF upload
- PDF metadata extraction and validation
- Automatic DOI/PMID lookup for missing PDFs
Getting Started
Applications
Features
- Workflow Guide
- Agents Guide
- Multi-Model Query Guide
- Query Agent Guide
- Citation Guide
- Reporting Guide
- Counterfactual Guide
Advanced
Architecture
Systems
- Workflow System
- Queue System Architecture
- Citation System
- Reporting System
- Counterfactual System
- Multi-Model Architecture
Contributing