| from datetime import datetime, timezone |
| from typing import Optional, Dict, Any, List |
| from enum import Enum |
| from pydantic import BaseModel, Field, GetJsonSchemaHandler |
| from pydantic.json_schema import JsonSchemaValue |
| from pydantic_core import core_schema |
| from bson import ObjectId |
|
|
|
|
|
|
| class PaperSource(str, Enum): |
| UPLOAD = "upload" |
| ARXIV = "arxiv" |
| SEMANTIC_SCHOLAR = "semantic_scholar" |
| OPENALEX = "openalex" |
| OTHER = "other" |
|
|
|
|
| class PaperStatus(str, Enum): |
| UPLOADED = "uploaded" |
| PROCESSING = "processing" |
| INDEXED = "indexed" |
| FAILED = "failed" |
|
|
|
|
| class PyObjectId(ObjectId): |
| @classmethod |
| def __get_pydantic_core_schema__( |
| cls, source_type: Any, handler: Any |
| ) -> core_schema.CoreSchema: |
| def validate_from_str(input_value: str) -> ObjectId: |
| if isinstance(input_value, ObjectId): |
| return input_value |
| if isinstance(input_value, str): |
| if ObjectId.is_valid(input_value): |
| return ObjectId(input_value) |
| raise ValueError("Invalid ObjectId string") |
| raise ValueError("Invalid ObjectId type") |
|
|
| return core_schema.no_info_plain_validator_function(validate_from_str) |
|
|
| @classmethod |
| def __get_pydantic_json_schema__( |
| cls, _core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler |
| ) -> JsonSchemaValue: |
| return {"type": "string"} |
|
|
|
|
| class ResearchPaper(BaseModel): |
| """ |
| ResearchPaper model for MongoDB. |
| Represents metadata about an uploaded or fetched research paper. |
| """ |
| id: Optional[PyObjectId] = Field(default=None, alias="_id") |
| filename: str |
| stored_path: str |
| file_hash: str |
| source: PaperSource |
| status: PaperStatus |
| title: Optional[str] = None |
| authors: Optional[List[str]] = None |
| abstract: Optional[str] = None |
| publication_year: Optional[str] = None |
| doi: Optional[str] = None |
| arxiv_id: Optional[str] = None |
| semantic_scholar_id: Optional[str] = None |
| subject_areas: Optional[List[str]] = None |
| uploaded_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) |
| processed_at: Optional[datetime] = None |
| chunk_count: int = 0 |
| error_message: Optional[str] = None |
| metadata: Optional[Dict[str, Any]] = None |
| session_id: Optional[str] = None |
|
|
| class Config: |
| populate_by_name = True |
| arbitrary_types_allowed = True |
| json_encoders = {ObjectId: str} |
| json_schema_extra = { |
| "example": { |
| "filename": "attention_is_all_you_need.pdf", |
| "source": "upload", |
| "title": "Attention Is All You Need", |
| "authors": ["Vaswani et al."], |
| "publication_year": "2017" |
| } |
| } |
|
|
| def to_mongo(self) -> dict: |
| data = self.dict(by_alias=True, exclude={"id"}) |
| if self.id: |
| data["_id"] = self.id |
| return data |
|
|
| @classmethod |
| def from_mongo(cls, data: dict) -> "ResearchPaper": |
| if "_id" in data: |
| data = {**data, "_id": str(data["_id"])} |
| return cls(**data) |