| | """Verification API routes.""" |
| | from fastapi import APIRouter, HTTPException, status |
| | from fastapi.responses import JSONResponse |
| |
|
| | from src.api.schemas import ( |
| | BibTeXVerifyRequest, |
| | BibTeXVerifyResponse, |
| | EntryComparisonResponse, |
| | DuplicateGroupResponse, |
| | ErrorResponse, |
| | ) |
| | from src.api.dependencies import VerificationServiceDep |
| | from src.core.logging import get_logger |
| | from src.core.exceptions import ParserException, FetcherException |
| |
|
| | logger = get_logger(__name__) |
| | router = APIRouter(prefix="/api/v1", tags=["verification"]) |
| |
|
| |
|
| | def _get_entry_status(comparison) -> str: |
| | """Determine entry status from comparison result.""" |
| | if comparison and comparison.is_match: |
| | return "verified" |
| | elif comparison and comparison.has_issues: |
| | return "warning" |
| | return "error" |
| |
|
| |
|
| | @router.post( |
| | "/verify", |
| | response_model=BibTeXVerifyResponse, |
| | status_code=status.HTTP_200_OK, |
| | summary="Verify BibTeX entries", |
| | description="Verify BibTeX entries against multiple academic databases", |
| | responses={ |
| | 200: {"description": "Verification completed successfully"}, |
| | 400: {"model": ErrorResponse, "description": "Invalid BibTeX content"}, |
| | 500: {"model": ErrorResponse, "description": "Internal server error"}, |
| | }, |
| | ) |
| | async def verify_bibtex( |
| | request: BibTeXVerifyRequest, |
| | service: VerificationServiceDep, |
| | ) -> BibTeXVerifyResponse: |
| | """Verify BibTeX entries. |
| | |
| | Args: |
| | request: BibTeX verification request |
| | service: Verification service instance |
| | |
| | Returns: |
| | Verification results |
| | |
| | Raises: |
| | HTTPException: If verification fails |
| | """ |
| | try: |
| | logger.info("Received BibTeX verification request") |
| |
|
| | |
| | result = service.verify_bibtex_string(request.bibtex_content) |
| |
|
| | |
| | entries = [] |
| | for entry_report in result.entry_reports: |
| | entry = entry_report.entry |
| | comparison = entry_report.comparison |
| |
|
| | |
| | bibtex_str = f"@{entry.entry_type}{{{entry.key},\n" |
| | for field, value in (entry.raw_entry or {}).items(): |
| | if field in ("ID", "ENTRYTYPE"): |
| | continue |
| | if value is not None and str(value).strip(): |
| | bibtex_str += f" {field}={{{value}}},\n" |
| | bibtex_str = bibtex_str.rstrip(",\n") + "\n}" |
| |
|
| | entry_response = EntryComparisonResponse( |
| | key=entry.key, |
| | status=_get_entry_status(comparison), |
| | is_match=comparison.is_match if comparison else False, |
| | has_issues=comparison.has_issues if comparison else False, |
| | source=getattr(comparison, "source", None) if comparison else None, |
| | confidence=getattr(comparison, "confidence", 0.0) if comparison else 0.0, |
| | title_match=comparison.title_match if comparison else False, |
| | author_match=comparison.author_match if comparison else False, |
| | year_match=comparison.year_match if comparison else False, |
| | venue_match=getattr(comparison, "venue_match", None) if comparison else None, |
| | fetched_title=getattr(comparison, "fetched_title", None) if comparison else None, |
| | fetched_authors=getattr(comparison, "fetched_authors", None) if comparison else None, |
| | fetched_year=getattr(comparison, "fetched_year", None) if comparison else None, |
| | fetched_doi=getattr(comparison, "fetched_doi", None) if comparison else None, |
| | fetched_url=getattr(comparison, "fetched_url", None) if comparison else None, |
| | original_bibtex=bibtex_str, |
| | ) |
| | entries.append(entry_response) |
| |
|
| | |
| | duplicate_groups = [ |
| | DuplicateGroupResponse( |
| | entry_keys=group.entry_keys, |
| | reason=group.reason, |
| | ) |
| | for group in result.duplicate_groups |
| | ] |
| |
|
| | response = BibTeXVerifyResponse( |
| | success=True, |
| | message="Verification completed successfully", |
| | total_count=result.total_count, |
| | verified_count=result.verified_count, |
| | warning_count=result.warning_count, |
| | error_count=result.error_count, |
| | success_rate=result.success_rate, |
| | entries=entries, |
| | duplicate_groups=duplicate_groups, |
| | ) |
| |
|
| | logger.info( |
| | f"Verification completed: {result.verified_count}/{result.total_count} verified" |
| | ) |
| | return response |
| |
|
| | except ParserException as e: |
| | logger.error(f"Parser error: {e}") |
| | raise HTTPException( |
| | status_code=status.HTTP_400_BAD_REQUEST, |
| | detail={"error": "ParserError", "message": str(e)}, |
| | ) |
| |
|
| | except FetcherException as e: |
| | logger.error(f"Fetcher error: {e}") |
| | raise HTTPException( |
| | status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| | detail={"error": "FetcherError", "message": str(e)}, |
| | ) |
| |
|
| | except Exception as e: |
| | logger.exception(f"Unexpected error during verification: {e}") |
| | raise HTTPException( |
| | status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| | detail={"error": "InternalError", "message": "An unexpected error occurred"}, |
| | ) |
| |
|