diff --git a/spoolman/api/v1/filament.py b/spoolman/api/v1/filament.py index 0c80a8e..6d8251e 100644 --- a/spoolman/api/v1/filament.py +++ b/spoolman/api/v1/filament.py @@ -29,29 +29,60 @@ class FilamentParameters(BaseModel): "Filament name, to distinguish this filament type among others from the same vendor." "Should contain its color for example." ), + example="PolyTerra™ Charcoal Black", ) vendor_id: Optional[int] = Field(description="The ID of the vendor of this filament type.") - material: Optional[str] = Field(max_length=64, description="The material of this filament, e.g. PLA.") - price: Optional[float] = Field(ge=0, description="The price of this filament in the system configured currency.") - density: float = Field(gt=0, description="The density of this filament in g/cm3.") - diameter: float = Field(gt=0, description="The diameter of this filament in mm.") - weight: Optional[float] = Field(gt=0, description="The weight of the filament in a full spool.") - spool_weight: Optional[float] = Field(gt=0, description="The empty spool weight.") - article_number: Optional[str] = Field(max_length=64, description="Vendor article number, e.g. EAN, QR code, etc.") - comment: Optional[str] = Field(max_length=1024, description="Free text comment about this filament type.") + material: Optional[str] = Field( + max_length=64, + description="The material of this filament, e.g. PLA.", + example="PLA", + ) + price: Optional[float] = Field( + ge=0, + description="The price of this filament in the system configured currency.", + example=20.0, + ) + density: float = Field(gt=0, description="The density of this filament in g/cm3.", example=1.24) + diameter: float = Field(gt=0, description="The diameter of this filament in mm.", example=1.75) + weight: Optional[float] = Field( + gt=0, + description="The weight of the filament in a full spool, in grams. (net weight)", + example=1000, + ) + spool_weight: Optional[float] = Field(gt=0, description="The empty spool weight, in grams.", example=140) + article_number: Optional[str] = Field( + max_length=64, + description="Vendor article number, e.g. EAN, QR code, etc.", + example="PM70820", + ) + comment: Optional[str] = Field( + max_length=1024, + description="Free text comment about this filament type.", + example="", + ) class FilamentUpdateParameters(FilamentParameters): - density: Optional[float] = Field(gt=0, description="The density of this filament in g/cm3.") - diameter: Optional[float] = Field(gt=0, description="The diameter of this filament in mm.") + density: Optional[float] = Field(gt=0, description="The density of this filament in g/cm3.", example=1.24) + diameter: Optional[float] = Field(gt=0, description="The diameter of this filament in mm.", example=1.75) -@router.get("/") +@router.get( + "/", + name="Find filaments", + description="Get a list of filaments that matches the search query.", + response_model_exclude_none=True, +) async def find(_vendor: Union[int, None] = None) -> list[Filament]: return [] -@router.get("/{filament_id}") +@router.get( + "/{filament_id}", + name="Get filament", + description="Get a specific filament.", + response_model_exclude_none=True, +) async def get( db: Annotated[AsyncSession, Depends(get_db_session)], filament_id: int, @@ -60,7 +91,12 @@ async def get( return Filament.from_db(db_item) -@router.post("/") +@router.post( + "/", + name="Add filament", + description="Add a new filament to the database.", + response_model_exclude_none=True, +) async def create( db: Annotated[AsyncSession, Depends(get_db_session)], body: FilamentParameters, @@ -82,7 +118,12 @@ async def create( return Filament.from_db(db_item) -@router.patch("/{filament_id}") +@router.patch( + "/{filament_id}", + name="Update filament", + description="Update any attribute of a filament. Only fields specified in the request will be affected.", + response_model_exclude_none=True, +) async def update( db: Annotated[AsyncSession, Depends(get_db_session)], filament_id: int, @@ -106,6 +147,8 @@ async def update( @router.delete( "/{filament_id}", + name="Delete filament", + description="Delete a filament.", response_model=Message, responses={ 403: {"model": Message}, diff --git a/spoolman/api/v1/models.py b/spoolman/api/v1/models.py index 424779b..331119c 100644 --- a/spoolman/api/v1/models.py +++ b/spoolman/api/v1/models.py @@ -14,8 +14,8 @@ class Message(BaseModel): class Vendor(BaseModel): id: int = Field(description="Unique internal ID of this vendor.") - name: str = Field(max_length=64, description="Vendor name.") - comment: Optional[str] = Field(max_length=1024, description="Free text comment about this vendor.") + name: str = Field(max_length=64, description="Vendor name.", example="Polymaker") + comment: Optional[str] = Field(max_length=1024, description="Free text comment about this vendor.", example="") @staticmethod def from_db(item: models.Vendor) -> "Vendor": @@ -35,16 +35,37 @@ class Filament(BaseModel): "Filament name, to distinguish this filament type among others from the same vendor." "Should contain its color for example." ), + example="PolyTerra™ Charcoal Black", ) vendor: Optional[Vendor] = Field(description="The vendor of this filament type.") - material: Optional[str] = Field(max_length=64, description="The material of this filament, e.g. PLA.") - price: Optional[float] = Field(ge=0, description="The price of this filament in the system configured currency.") - density: float = Field(gt=0, description="The density of this filament in g/cm3.") - diameter: float = Field(gt=0, description="The diameter of this filament in mm.") - weight: Optional[float] = Field(gt=0, description="The weight of the filament in a full spool.") - spool_weight: Optional[float] = Field(gt=0, description="The empty spool weight.") - article_number: Optional[str] = Field(max_length=64, description="Vendor article number, e.g. EAN, QR code, etc.") - comment: Optional[str] = Field(max_length=1024, description="Free text comment about this filament type.") + material: Optional[str] = Field( + max_length=64, + description="The material of this filament, e.g. PLA.", + example="PLA", + ) + price: Optional[float] = Field( + ge=0, + description="The price of this filament in the system configured currency.", + example=20.0, + ) + density: float = Field(gt=0, description="The density of this filament in g/cm3.", example=1.24) + diameter: float = Field(gt=0, description="The diameter of this filament in mm.", example=1.75) + weight: Optional[float] = Field( + gt=0, + description="The weight of the filament in a full spool, in grams.", + example=1000, + ) + spool_weight: Optional[float] = Field(gt=0, description="The empty spool weight, in grams.", example=140) + article_number: Optional[str] = Field( + max_length=64, + description="Vendor article number, e.g. EAN, QR code, etc.", + example="PM70820", + ) + comment: Optional[str] = Field( + max_length=1024, + description="Free text comment about this filament type.", + example="", + ) @staticmethod def from_db(item: models.Filament) -> "Filament": @@ -70,10 +91,18 @@ class Spool(BaseModel): first_used: Optional[datetime] = Field(description="First logged occurence of spool usage.") last_used: Optional[datetime] = Field(description="Last logged occurence of spool usage.") filament: Filament = Field(description="The filament type of this spool.") - weight: float = Field(ge=0, description="Remaining weight of filament on the spool.") - location: Optional[str] = Field(max_length=64, description="Where this spool can be found.") - lot_nr: Optional[str] = Field(max_length=64, description="Vendor manufacturing lot/batch number of the spool.") - comment: Optional[str] = Field(max_length=1024, description="Free text comment about this specific spool.") + weight: float = Field(ge=0, description="Remaining weight of filament on the spool.", example=500) + location: Optional[str] = Field(max_length=64, description="Where this spool can be found.", example="Shelf A") + lot_nr: Optional[str] = Field( + max_length=64, + description="Vendor manufacturing lot/batch number of the spool.", + example="52342", + ) + comment: Optional[str] = Field( + max_length=1024, + description="Free text comment about this specific spool.", + example="", + ) @staticmethod def from_db(item: models.Spool) -> "Spool": diff --git a/spoolman/api/v1/spool.py b/spoolman/api/v1/spool.py index 754bcb8..72b7a55 100644 --- a/spoolman/api/v1/spool.py +++ b/spoolman/api/v1/spool.py @@ -33,10 +33,19 @@ class SpoolParameters(BaseModel): "Remaining weight of filament on the spool. " "Leave empty to assume a full spool based on the weight parameters of the filament type." ), + example=500, + ) + location: Optional[str] = Field(max_length=64, description="Where this spool can be found.", example="Shelf A") + lot_nr: Optional[str] = Field( + max_length=64, + description="Vendor manufacturing lot/batch number of the spool.", + example="52342", + ) + comment: Optional[str] = Field( + max_length=1024, + description="Free text comment about this specific spool.", + example="", ) - location: Optional[str] = Field(max_length=64, description="Where this spool can be found.") - lot_nr: Optional[str] = Field(max_length=64, description="Vendor manufacturing lot/batch number of the spool.") - comment: Optional[str] = Field(max_length=1024, description="Free text comment about this specific spool.") class SpoolUpdateParameters(SpoolParameters): @@ -44,16 +53,26 @@ class SpoolUpdateParameters(SpoolParameters): class SpoolUseParameters(BaseModel): - use_length: Optional[float] = Field(description="Length of filament to reduce by, in mm.") - use_weight: Optional[float] = Field(description="Filament weight to reduce by, in g.") + use_length: Optional[float] = Field(description="Length of filament to reduce by, in mm.", example=2.2) + use_weight: Optional[float] = Field(description="Filament weight to reduce by, in g.", example=5.3) -@router.get("/") +@router.get( + "/", + name="Find spool", + description="Get a list of spools that matches the search query.", + response_model_exclude_none=True, +) async def find(_filament: Union[int, None] = None) -> list[Spool]: return [] -@router.get("/{spool_id}") +@router.get( + "/{spool_id}", + name="Get spool", + description="Get a specific spool.", + response_model_exclude_none=True, +) async def get( db: Annotated[AsyncSession, Depends(get_db_session)], spool_id: int, @@ -64,6 +83,9 @@ async def get( @router.post( "/", + name="Add spool", + description="Add a new spool to the database.", + response_model_exclude_none=True, response_model=Spool, responses={ 400: {"model": Message}, @@ -92,7 +114,12 @@ async def create( # noqa: ANN201 ) -@router.patch("/{spool_id}") +@router.patch( + "/{spool_id}", + name="Update spool", + description="Update any attribute of a spool. Only fields specified in the request will be affected.", + response_model_exclude_none=True, +) async def update( db: Annotated[AsyncSession, Depends(get_db_session)], spool_id: int, @@ -114,7 +141,11 @@ async def update( return Spool.from_db(db_item) -@router.delete("/{spool_id}") +@router.delete( + "/{spool_id}", + name="Delete spool", + description="Delete a spool.", +) async def delete( db: Annotated[AsyncSession, Depends(get_db_session)], spool_id: int, @@ -125,6 +156,13 @@ async def delete( @router.put( "/{spool_id}/use", + name="Use spool filament", + description=( + "Use some length or weight of filament from the spool." + " Specify either a length or a weight, not both." + " Will do nothing if the spool is empty (you have to keep track of that by yourself)." + ), + response_model_exclude_none=True, response_model=Spool, responses={ 400: {"model": Message}, diff --git a/spoolman/api/v1/vendor.py b/spoolman/api/v1/vendor.py index a2f609f..1a76055 100644 --- a/spoolman/api/v1/vendor.py +++ b/spoolman/api/v1/vendor.py @@ -21,21 +21,39 @@ router = APIRouter( class VendorParameters(BaseModel): - name: str = Field(max_length=64, description="Vendor name.") - comment: Optional[str] = Field(max_length=1024, description="Free text comment about this vendor.") + name: str = Field(max_length=64, description="Vendor name.", example="Polymaker") + comment: Optional[str] = Field( + max_length=1024, + description="Free text comment about this vendor.", + example="", + ) class VendorUpdateParameters(VendorParameters): - name: Optional[str] = Field(max_length=64, description="Vendor name.") - comment: Optional[str] = Field(max_length=1024, description="Free text comment about this vendor.") + name: Optional[str] = Field(max_length=64, description="Vendor name.", example="Polymaker") + comment: Optional[str] = Field( + max_length=1024, + description="Free text comment about this vendor.", + example="", + ) -@router.get("/") +@router.get( + "/", + name="Find vendor", + description="Get a list of vendors that matches the search query.", + response_model_exclude_none=True, +) async def find(_name: Union[int, None] = None) -> list[Vendor]: return [] -@router.get("/{vendor_id}") +@router.get( + "/{vendor_id}", + name="Get vendor", + description="Get a specific vendor.", + response_model_exclude_none=True, +) async def get( db: Annotated[AsyncSession, Depends(get_db_session)], vendor_id: int, @@ -44,7 +62,12 @@ async def get( return Vendor.from_db(db_item) -@router.post("/") +@router.post( + "/", + name="Add vendor", + description="Add a new vendor to the database.", + response_model_exclude_none=True, +) async def create( db: Annotated[AsyncSession, Depends(get_db_session)], body: VendorParameters, @@ -58,7 +81,12 @@ async def create( return Vendor.from_db(db_item) -@router.patch("/{vendor_id}") +@router.patch( + "/{vendor_id}", + name="Update vendor", + description="Update any attribute of a vendor. Only fields specified in the request will be affected.", + response_model_exclude_none=True, +) async def update( db: Annotated[AsyncSession, Depends(get_db_session)], vendor_id: int, @@ -78,7 +106,13 @@ async def update( return Vendor.from_db(db_item) -@router.delete("/{vendor_id}") +@router.delete( + "/{vendor_id}", + name="Delete vendor", + description=( + "Delete a vendor. The vendor attribute of any filaments who refer to the deleted vendor will be cleared." + ), +) async def delete( db: Annotated[AsyncSession, Depends(get_db_session)], vendor_id: int, diff --git a/spoolman/database/filament.py b/spoolman/database/filament.py index cc698ac..38732f2 100644 --- a/spoolman/database/filament.py +++ b/spoolman/database/filament.py @@ -2,8 +2,8 @@ from typing import Optional -from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.exc import IntegrityError +from sqlalchemy.ext.asyncio import AsyncSession from spoolman.database import models, vendor from spoolman.exceptions import ItemDeleteError, ItemNotFoundError