fastapi-sdk 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,109 @@
1
+ """Controller module for crud operations."""
2
+
3
+ from datetime import UTC, datetime
4
+ from typing import List, Optional, Type
5
+
6
+ from odmantic import AIOEngine, Model
7
+ from pydantic import BaseModel
8
+
9
+ from fastapi_sdk.utils.schema import datetime_now_sec
10
+
11
+
12
+ class Controller:
13
+ """Base controller class."""
14
+
15
+ model: Type[Model]
16
+ schema_create: Type[BaseModel]
17
+ schema_update: Type[BaseModel]
18
+ n_per_page: int = 10
19
+
20
+ def __init__(self, db_engine: AIOEngine):
21
+ """Initialize the controller."""
22
+ self.db_engine = db_engine
23
+
24
+ async def _create(self, **kwargs) -> BaseModel:
25
+ """Create a new model."""
26
+ data = self.schema_create(**kwargs)
27
+ model = self.model(**data.model_dump())
28
+ return await self.db_engine.save(model)
29
+
30
+ async def _update(self, uuid: str, data: dict) -> BaseModel:
31
+ """Update a model."""
32
+ model = await self._get(uuid)
33
+ data = self.schema_update(**data)
34
+ if model:
35
+ # Update the fields submitted
36
+ for field in data.model_dump(exclude_unset=True):
37
+ setattr(model, field, data.model_dump()[field])
38
+ model.updated_at = datetime_now_sec()
39
+ return await self.db_engine.save(model)
40
+ return None
41
+
42
+ async def _get(self, uuid: str) -> BaseModel:
43
+ """Get a model."""
44
+ return await self.db_engine.find_one(
45
+ self.model, self.model.uuid == uuid, self.model.deleted == False
46
+ )
47
+
48
+ async def _delete(self, uuid: str) -> BaseModel:
49
+ """Delete a model."""
50
+ model = await self._get(uuid)
51
+ if model:
52
+ model.deleted = True
53
+ return await self.db_engine.save(model)
54
+ return None
55
+
56
+ async def _list(
57
+ self,
58
+ page: int = 0,
59
+ query: Optional[List[dict]] = None,
60
+ order_by: Optional[dict] = None,
61
+ ) -> List[BaseModel]:
62
+ """List models."""
63
+ # Get the collection
64
+ collection_name = self.model.model_config[
65
+ "collection"
66
+ ] or self.model.__name__.lower().replace("model", "")
67
+ _collection = self.db_engine.database[collection_name]
68
+
69
+ # Create a pipeline for aggregation
70
+ _pipeline = []
71
+
72
+ # Filter out deleted models by default
73
+ # Example query: [{"due_date": {"$gte": start_date}}]
74
+ _query = {"deleted": False}
75
+ if query:
76
+ for q in query:
77
+ _query.update(q)
78
+
79
+ # Sorting, default by created_at
80
+ # Order by example: {"name": -1}, 1 ascending, -1 descending
81
+ _sort = order_by if order_by else {"created_at": -1}
82
+
83
+ # Add the pipeline stages
84
+ _pipeline.append({"$match": _query})
85
+ _pipeline.append({"$sort": _sort})
86
+
87
+ # Add pagination data
88
+ _pipeline.append({"$skip": (page - 1) * self.n_per_page if page > 0 else 0})
89
+ _pipeline.append({"$limit": self.n_per_page})
90
+
91
+ # Execute the aggregation
92
+ items = await _collection.aggregate(_pipeline).to_list(length=self.n_per_page)
93
+
94
+ # Count the total number of items
95
+ total = await _collection.count_documents(_query)
96
+
97
+ pages = total // self.n_per_page
98
+ if total % self.n_per_page > 0:
99
+ pages += 1
100
+
101
+ data = {
102
+ "items": [self.model.model_validate_doc(item) for item in items],
103
+ "total": total,
104
+ "size": len(items),
105
+ "page": page,
106
+ "pages": pages,
107
+ }
108
+
109
+ return data
@@ -0,0 +1,28 @@
1
+ """Set of utilities to create and manage models."""
2
+
3
+ import re
4
+ from typing import Annotated
5
+
6
+ import shortuuid
7
+ from pydantic import StringConstraints
8
+
9
+
10
+ class ShortUUID:
11
+ """Custom type for short UUIDs with trigram prefixes"""
12
+
13
+ @classmethod
14
+ def generate(cls, prefix: str) -> str:
15
+ """Generate a valid short UUID with a given trigram prefix"""
16
+ if not re.match(r"^[a-z]{3}$", prefix):
17
+ raise ValueError("Prefix must be exactly 3 lowercase letters")
18
+ return f"{prefix}_{shortuuid.uuid()[:10]}"
19
+
20
+ @classmethod
21
+ def validate(cls, value: str) -> str:
22
+ """Validate the short UUID format"""
23
+ if not re.match(r"^[a-z]{3}_[a-zA-Z0-9]{10}$", value):
24
+ raise ValueError("Invalid short UUID format")
25
+ return value
26
+
27
+
28
+ ShortUUIDType = Annotated[str, StringConstraints(pattern=r"^[a-z]{3}_[a-zA-Z0-9]{10}$")]
@@ -0,0 +1,8 @@
1
+ """Schema utilities."""
2
+
3
+ from datetime import UTC, datetime
4
+
5
+
6
+ def datetime_now_sec():
7
+ """Return the current datetime with microseconds set to 0."""
8
+ return datetime.now(UTC).replace(microsecond=0)
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.2
2
+ Name: fastapi-sdk
3
+ Version: 0.1.0
4
+ Summary: Utilities for FastAPI projects.
5
+ Requires-Python: >=3.13
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: fastapi[standard]>=0.115.11
8
+ Requires-Dist: odmantic>=1.0.2
9
+ Requires-Dist: pydantic>=2.10.6
10
+ Requires-Dist: pydantic-settings>=2.8.1
11
+ Requires-Dist: pytest>=8.3.5
12
+ Requires-Dist: pytest-asyncio>=0.25.3
13
+ Requires-Dist: shortuuid>=1.0.13
14
+
15
+ # FastAPI SDK
16
+
17
+ ## Run tests
18
+
19
+ ```bash
20
+ uv sync
21
+ pytest
22
+ ```
23
+
24
+ ## Update requirements
25
+
26
+ You can add new requirements by using UV:
27
+
28
+ ```bash
29
+ uv add module_name
30
+ ```
31
+
32
+ Then update the requirements.txt:
33
+
34
+ ```bash
35
+ uv pip compile pyproject.toml -o requirements.txt
36
+ ```
@@ -0,0 +1,7 @@
1
+ fastapi_sdk/controller.py,sha256=xGbzP-KZX1hsiY0uytsSLMa_IMDIQ-zBZAXYFNwUAn8,3462
2
+ fastapi_sdk/utils/model.py,sha256=rWpVHELs6wvh_7Q9wcEMJtaDT33IqW7gnbBzANaUtPk,871
3
+ fastapi_sdk/utils/schema.py,sha256=7nd5gV1izWOqmomnM_Q5PNIk-1QbUtZrIZ02id2RWNM,204
4
+ fastapi_sdk-0.1.0.dist-info/METADATA,sha256=FWFu1GTgqgbgBafFJTCSpba0haPwtPglaSZG1WImYXs,660
5
+ fastapi_sdk-0.1.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
6
+ fastapi_sdk-0.1.0.dist-info/top_level.txt,sha256=FcXWgEILikx1WO7Rm0-0bxb4Ck3nMSkzVHfy15Gag2o,12
7
+ fastapi_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (76.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ fastapi_sdk