mirror of
https://github.com/tcsenpai/pensieve.git
synced 2025-06-06 19:25:24 +00:00
feat: list libraries
This commit is contained in:
parent
5c850a56c1
commit
dd3b32821d
@ -1,3 +1,4 @@
|
|||||||
|
from typing import List
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from .schemas import Library, NewLibraryParam, Folder, NewEntityParam, Entity, Plugin, NewPluginParam
|
from .schemas import Library, NewLibraryParam, Folder, NewEntityParam, Entity, Plugin, NewPluginParam
|
||||||
from .models import LibraryModel, FolderModel, EntityModel, EntityModel, PluginModel, LibraryPluginModel
|
from .models import LibraryModel, FolderModel, EntityModel, EntityModel, PluginModel, LibraryPluginModel
|
||||||
@ -21,11 +22,15 @@ def create_library(library: NewLibraryParam, db: Session) -> Library:
|
|||||||
return Library(
|
return Library(
|
||||||
id=db_library.id,
|
id=db_library.id,
|
||||||
name=db_library.name,
|
name=db_library.name,
|
||||||
folders=[Folder(id=db_folder.id, name=db_folder.path) for db_folder in db_library.folders],
|
folders=[Folder(id=db_folder.id, path=db_folder.path) for db_folder in db_library.folders],
|
||||||
plugins=[]
|
plugins=[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_libraries(db: Session) -> List[Library]:
|
||||||
|
return db.query(LibraryModel).all()
|
||||||
|
|
||||||
|
|
||||||
def create_entity(library_id: int, entity: NewEntityParam, db: Session) -> Entity:
|
def create_entity(library_id: int, entity: NewEntityParam, db: Session) -> Entity:
|
||||||
db_entity = EntityModel(
|
db_entity = EntityModel(
|
||||||
**entity.model_dump(),
|
**entity.model_dump(),
|
||||||
|
@ -6,7 +6,7 @@ from sqlalchemy import (
|
|||||||
DateTime,
|
DateTime,
|
||||||
Enum,
|
Enum,
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
func
|
func,
|
||||||
)
|
)
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, mapped_column
|
from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, mapped_column
|
||||||
@ -17,23 +17,37 @@ from .schemas import MetadataSource, MetadataType
|
|||||||
|
|
||||||
class Base(DeclarativeBase):
|
class Base(DeclarativeBase):
|
||||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False)
|
created_at: Mapped[datetime] = mapped_column(
|
||||||
updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now(), nullable=False)
|
DateTime, server_default=func.now(), nullable=False
|
||||||
|
)
|
||||||
|
updated_at: Mapped[datetime] = mapped_column(
|
||||||
|
DateTime, server_default=func.now(), onupdate=func.now(), nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LibraryModel(Base):
|
class LibraryModel(Base):
|
||||||
__tablename__ = "libraries"
|
__tablename__ = "libraries"
|
||||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
folders: Mapped[List["FolderModel"]] = relationship("FolderModel", back_populates="library")
|
folders: Mapped[List["FolderModel"]] = relationship(
|
||||||
plugins: Mapped[List["PluginModel"]] = relationship("LibraryPluginModel", back_populates="library")
|
"FolderModel", back_populates="library", lazy="joined"
|
||||||
|
)
|
||||||
|
plugins: Mapped[List["PluginModel"]] = relationship(
|
||||||
|
"LibraryPluginModel", back_populates="library", lazy="joined"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FolderModel(Base):
|
class FolderModel(Base):
|
||||||
__tablename__ = "folders"
|
__tablename__ = "folders"
|
||||||
path: Mapped[str] = mapped_column(String, nullable=False)
|
path: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
library_id: Mapped[int] = mapped_column(Integer, ForeignKey('libraries.id'), nullable=False)
|
library_id: Mapped[int] = mapped_column(
|
||||||
library: Mapped["LibraryModel"] = relationship("LibraryModel", back_populates="folders")
|
Integer, ForeignKey("libraries.id"), nullable=False
|
||||||
entities: Mapped[List["EntityModel"]] = relationship("EntityModel", back_populates="folder")
|
)
|
||||||
|
library: Mapped["LibraryModel"] = relationship(
|
||||||
|
"LibraryModel", back_populates="folders"
|
||||||
|
)
|
||||||
|
entities: Mapped[List["EntityModel"]] = relationship(
|
||||||
|
"EntityModel", back_populates="folder"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EntityModel(Base):
|
class EntityModel(Base):
|
||||||
@ -45,10 +59,18 @@ class EntityModel(Base):
|
|||||||
file_last_modified_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
file_last_modified_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
||||||
file_type: Mapped[str] = mapped_column(String, nullable=False)
|
file_type: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
last_scan_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
last_scan_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||||
library_id: Mapped[int] = mapped_column(Integer, ForeignKey('libraries.id'), nullable=False)
|
library_id: Mapped[int] = mapped_column(
|
||||||
folder_id: Mapped[int] = mapped_column(Integer, ForeignKey('folders.id'), nullable=False)
|
Integer, ForeignKey("libraries.id"), nullable=False
|
||||||
folder: Mapped["FolderModel"] = relationship("FolderModel", back_populates="entities")
|
)
|
||||||
metadata_entries: Mapped[List["EntityMetadataModel"]] = relationship("EntityMetadataModel")
|
folder_id: Mapped[int] = mapped_column(
|
||||||
|
Integer, ForeignKey("folders.id"), nullable=False
|
||||||
|
)
|
||||||
|
folder: Mapped["FolderModel"] = relationship(
|
||||||
|
"FolderModel", back_populates="entities"
|
||||||
|
)
|
||||||
|
metadata_entries: Mapped[List["EntityMetadataModel"]] = relationship(
|
||||||
|
"EntityMetadataModel"
|
||||||
|
)
|
||||||
tags: Mapped[List["TagModel"]] = relationship("EntityTagModel")
|
tags: Mapped[List["TagModel"]] = relationship("EntityTagModel")
|
||||||
|
|
||||||
|
|
||||||
@ -62,17 +84,23 @@ class TagModel(Base):
|
|||||||
|
|
||||||
class EntityTagModel(Base):
|
class EntityTagModel(Base):
|
||||||
__tablename__ = "entity_tags"
|
__tablename__ = "entity_tags"
|
||||||
entity_id: Mapped[int] = mapped_column(Integer, ForeignKey('entities.id'), nullable=False)
|
entity_id: Mapped[int] = mapped_column(
|
||||||
tag_id: Mapped[int] = mapped_column(Integer, ForeignKey('tags.id'), nullable=False)
|
Integer, ForeignKey("entities.id"), nullable=False
|
||||||
|
)
|
||||||
|
tag_id: Mapped[int] = mapped_column(Integer, ForeignKey("tags.id"), nullable=False)
|
||||||
source: Mapped[MetadataSource] = mapped_column(Enum(MetadataSource), nullable=False)
|
source: Mapped[MetadataSource] = mapped_column(Enum(MetadataSource), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class EntityMetadataModel(Base):
|
class EntityMetadataModel(Base):
|
||||||
__tablename__ = "metadata_entries"
|
__tablename__ = "metadata_entries"
|
||||||
entity_id: Mapped[int] = mapped_column(Integer, ForeignKey('entities.id'), nullable=False)
|
entity_id: Mapped[int] = mapped_column(
|
||||||
|
Integer, ForeignKey("entities.id"), nullable=False
|
||||||
|
)
|
||||||
key: Mapped[str] = mapped_column(String, nullable=False)
|
key: Mapped[str] = mapped_column(String, nullable=False)
|
||||||
value: Mapped[str] = mapped_column(Text, nullable=False)
|
value: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
source_type: Mapped[MetadataSource] = mapped_column(Enum(MetadataSource), nullable=False)
|
source_type: Mapped[MetadataSource] = mapped_column(
|
||||||
|
Enum(MetadataSource), nullable=False
|
||||||
|
)
|
||||||
source: Mapped[str | None] = mapped_column(String, nullable=True)
|
source: Mapped[str | None] = mapped_column(String, nullable=True)
|
||||||
date_type: Mapped[MetadataType] = mapped_column(Enum(MetadataType), nullable=False)
|
date_type: Mapped[MetadataType] = mapped_column(Enum(MetadataType), nullable=False)
|
||||||
entity = relationship("EntityModel", back_populates="metadata_entries")
|
entity = relationship("EntityModel", back_populates="metadata_entries")
|
||||||
@ -88,10 +116,18 @@ class PluginModel(Base):
|
|||||||
|
|
||||||
class LibraryPluginModel(Base):
|
class LibraryPluginModel(Base):
|
||||||
__tablename__ = "library_plugins"
|
__tablename__ = "library_plugins"
|
||||||
library_id: Mapped[int] = mapped_column(Integer, ForeignKey('libraries.id'), nullable=False)
|
library_id: Mapped[int] = mapped_column(
|
||||||
plugin_id: Mapped[int] = mapped_column(Integer, ForeignKey('plugins.id'), nullable=False)
|
Integer, ForeignKey("libraries.id"), nullable=False
|
||||||
library: Mapped["LibraryModel"] = relationship("LibraryModel", back_populates="plugins")
|
)
|
||||||
plugin: Mapped["PluginModel"] = relationship("PluginModel", back_populates="libraries")
|
plugin_id: Mapped[int] = mapped_column(
|
||||||
|
Integer, ForeignKey("plugins.id"), nullable=False
|
||||||
|
)
|
||||||
|
library: Mapped["LibraryModel"] = relationship(
|
||||||
|
"LibraryModel", back_populates="plugins"
|
||||||
|
)
|
||||||
|
plugin: Mapped["PluginModel"] = relationship(
|
||||||
|
"PluginModel", back_populates="libraries"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Create the database engine with the path from config
|
# Create the database engine with the path from config
|
||||||
|
@ -66,7 +66,7 @@ class NewLibraryPluginParam(BaseModel):
|
|||||||
|
|
||||||
class Folder(BaseModel):
|
class Folder(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
name: str
|
path: str
|
||||||
|
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
@ -6,7 +6,14 @@ from sqlalchemy.orm import sessionmaker
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from .config import get_database_path
|
from .config import get_database_path
|
||||||
from .crud import get_library_by_id, create_library, create_entity, create_plugin, add_plugin_to_library
|
from .crud import (
|
||||||
|
get_library_by_id,
|
||||||
|
create_library,
|
||||||
|
create_entity,
|
||||||
|
create_plugin,
|
||||||
|
add_plugin_to_library,
|
||||||
|
get_libraries,
|
||||||
|
)
|
||||||
from .schemas import (
|
from .schemas import (
|
||||||
Library,
|
Library,
|
||||||
Folder,
|
Folder,
|
||||||
@ -44,15 +51,22 @@ def new_library(library_param: NewLibraryParam, db: Session = Depends(get_db)):
|
|||||||
return library
|
return library
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/libraries", response_model=List[Library])
|
||||||
|
def list_libraries(db: Session = Depends(get_db)):
|
||||||
|
libraries = get_libraries(db)
|
||||||
|
return libraries
|
||||||
|
|
||||||
|
|
||||||
@app.post("/libraries/{library_id}/folders", response_model=Folder)
|
@app.post("/libraries/{library_id}/folders", response_model=Folder)
|
||||||
def new_folder(
|
def new_folder(
|
||||||
library_id: int,
|
library_id: int,
|
||||||
folder: NewFolderParam, db: Session = Depends(get_db),
|
folder: NewFolderParam,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
library = get_library_by_id(library_id, db)
|
library = get_library_by_id(library_id, db)
|
||||||
if library is None:
|
if library is None:
|
||||||
raise HTTPException(status_code=404, detail="Library not found")
|
raise HTTPException(status_code=404, detail="Library not found")
|
||||||
|
|
||||||
db_folder = Folder(path=folder.path, library_id=library.id)
|
db_folder = Folder(path=folder.path, library_id=library.id)
|
||||||
db.add(db_folder)
|
db.add(db_folder)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
@ -1,11 +1,85 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
from sqlalchemy import create_engine
|
||||||
from .server import app
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy.pool import StaticPool
|
||||||
client = TestClient(app)
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def test_read_main():
|
from memos.server import app, get_db
|
||||||
|
from memos.schemas import Library, NewLibraryParam
|
||||||
|
from memos.models import Base
|
||||||
|
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
"sqlite:///:memory:",
|
||||||
|
connect_args={"check_same_thread": False},
|
||||||
|
poolclass=StaticPool,
|
||||||
|
)
|
||||||
|
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
|
||||||
|
def override_get_db():
|
||||||
|
try:
|
||||||
|
db = TestingSessionLocal()
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
app.dependency_overrides[get_db] = override_get_db
|
||||||
|
|
||||||
|
|
||||||
|
# Setup a fixture for the FastAPI test client
|
||||||
|
@pytest.fixture
|
||||||
|
def client():
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
with TestClient(app) as client:
|
||||||
|
yield client
|
||||||
|
Base.metadata.drop_all(bind=engine)
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_main(client):
|
||||||
response = client.get("/")
|
response = client.get("/")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json() == {"healthy": True}
|
assert response.json() == {"healthy": True}
|
||||||
|
|
||||||
|
|
||||||
|
# Test the new_library endpoint
|
||||||
|
def test_new_library(client):
|
||||||
|
library_param = NewLibraryParam(name="Test Library")
|
||||||
|
# Make a POST request to the /libraries endpoint
|
||||||
|
response = client.post("/libraries", json=library_param.model_dump())
|
||||||
|
# Check that the response is successful
|
||||||
|
assert response.status_code == 200
|
||||||
|
# Check the response data
|
||||||
|
assert response.json() == {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Test Library",
|
||||||
|
"folders": [],
|
||||||
|
"plugins": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_libraries(client):
|
||||||
|
# Setup data: Create a new library
|
||||||
|
new_library = NewLibraryParam(name="Sample Library", folders=["/tmp"])
|
||||||
|
client.post("/libraries", json=new_library.model_dump(mode="json"))
|
||||||
|
|
||||||
|
# Make a GET request to the /libraries endpoint
|
||||||
|
response = client.get("/libraries")
|
||||||
|
|
||||||
|
# Check that the response is successful
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Check the response data
|
||||||
|
expected_data = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Sample Library",
|
||||||
|
"folders": [{"id": 1, "path": "/tmp"}],
|
||||||
|
"plugins": [],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
assert response.json() == expected_data
|
||||||
|
Loading…
x
Reference in New Issue
Block a user