|
|
import logging |
|
|
from pathlib import Path |
|
|
from functools import lru_cache |
|
|
from typing import Optional |
|
|
from pybtex.database import parse_file, BibliographyData |
|
|
from pybtex.plugin import find_plugin |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
BIB_DIR = Path("public/bib") |
|
|
STYLE_NAME = "apa7" |
|
|
OUTPUT_FORMAT = "html" |
|
|
|
|
|
@lru_cache(maxsize=1) |
|
|
def load_bibliography() -> BibliographyData: |
|
|
|
|
|
bib_files = list(BIB_DIR.glob("*.bib")) |
|
|
|
|
|
if not bib_files: |
|
|
logger.warning(f"No .bib files found in {BIB_DIR}") |
|
|
return BibliographyData() |
|
|
|
|
|
merged_bib = parse_file(str(bib_files[0])) |
|
|
logger.info(f"Loaded bibliography from {bib_files[0].name}") |
|
|
|
|
|
for bib_file in bib_files[1:]: |
|
|
try: |
|
|
bib_data = parse_file(str(bib_file)) |
|
|
merged_bib.entries.update(bib_data.entries) |
|
|
logger.info(f"Merged bibliography from {bib_file.name}") |
|
|
except Exception as e: |
|
|
logger.error(f"Error parsing {bib_file}: {e}") |
|
|
|
|
|
logger.info(f"Total entries loaded: {len(merged_bib.entries)}") |
|
|
return merged_bib |
|
|
|
|
|
|
|
|
def format_citation( |
|
|
cite_key: str, |
|
|
style: str = STYLE_NAME, |
|
|
output_format: str = OUTPUT_FORMAT |
|
|
) -> str: |
|
|
|
|
|
try: |
|
|
bibliography = load_bibliography() |
|
|
|
|
|
if cite_key not in bibliography.entries: |
|
|
logger.warning(f"Citation key '{cite_key}' not found in bibliography") |
|
|
return f"<p class='citation-error'>[Citation not found: {cite_key}]</span>" |
|
|
|
|
|
filtered_bib = BibliographyData({cite_key: bibliography.entries[cite_key]}) |
|
|
|
|
|
style_plugin = find_plugin('pybtex.style.formatting', style)() |
|
|
backend = find_plugin('pybtex.backends', output_format)() |
|
|
|
|
|
formatted = style_plugin.format_bibliography(filtered_bib) |
|
|
|
|
|
for entry in formatted: |
|
|
return entry.text.render(backend) |
|
|
|
|
|
return f"<p class='citation-error'>[Error formatting: {cite_key}]</span>" |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error formatting citation {cite_key}: {e}") |
|
|
return f"<p class='citation-error'>[Error: {cite_key}]</span>" |
|
|
|
|
|
|
|
|
def format_bibliography( |
|
|
cite_keys: Optional[list[str]] = None, |
|
|
style: str = STYLE_NAME, |
|
|
output_format: str = OUTPUT_FORMAT |
|
|
) -> str: |
|
|
|
|
|
try: |
|
|
bibliography = load_bibliography() |
|
|
|
|
|
|
|
|
if cite_keys: |
|
|
filtered_entries = { |
|
|
key: bibliography.entries[key] |
|
|
for key in cite_keys |
|
|
if key in bibliography.entries |
|
|
} |
|
|
|
|
|
if not filtered_entries: |
|
|
return "<p class='bibliography-error'>No valid citation keys provided.</p>" |
|
|
|
|
|
filtered_bib = BibliographyData(filtered_entries) |
|
|
else: |
|
|
filtered_bib = bibliography |
|
|
|
|
|
style_plugin = find_plugin('pybtex.style.formatting', style)() |
|
|
backend = find_plugin('pybtex.backends', output_format)() |
|
|
|
|
|
formatted = style_plugin.format_bibliography(filtered_bib) |
|
|
|
|
|
entries_html = [] |
|
|
for entry in formatted: |
|
|
entries_html.append(entry.text.render(backend)) |
|
|
|
|
|
return "\n".join(entries_html) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error formatting bibliography: {e}") |
|
|
return f"<p class='bibliography-error'>[Error formatting bibliography]</p>" |
|
|
|
|
|
|
|
|
def get_bibtex_entry(cite_key: str) -> Optional[str]: |
|
|
|
|
|
try: |
|
|
bibliography = load_bibliography() |
|
|
|
|
|
if cite_key not in bibliography.entries: |
|
|
logger.warning(f"Citation key '{cite_key}' not found") |
|
|
return None |
|
|
|
|
|
single_entry = BibliographyData({cite_key: bibliography.entries[cite_key]}) |
|
|
|
|
|
return single_entry.to_string('bibtex') |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error getting BibTeX for {cite_key}: {e}") |
|
|
return None |