fastapi-mongo-base 0.8.30__tar.gz

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.
Files changed (30) hide show
  1. fastapi_mongo_base-0.8.30/LICENSE.txt +19 -0
  2. fastapi_mongo_base-0.8.30/PKG-INFO +69 -0
  3. fastapi_mongo_base-0.8.30/README.md +7 -0
  4. fastapi_mongo_base-0.8.30/pyproject.toml +48 -0
  5. fastapi_mongo_base-0.8.30/setup.cfg +4 -0
  6. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/__init__.py +0 -0
  7. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/cached.py +115 -0
  8. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/core/__init__.py +0 -0
  9. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/core/app_factory.py +184 -0
  10. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/core/config.py +84 -0
  11. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/core/db.py +45 -0
  12. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/core/enums.py +36 -0
  13. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/core/exceptions.py +86 -0
  14. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/core/middlewares.py +22 -0
  15. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/handlers.py +39 -0
  16. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/models.py +384 -0
  17. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/routes.py +293 -0
  18. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/schemas.py +107 -0
  19. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/tasks.py +291 -0
  20. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/utils/__init__.py +0 -0
  21. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/utils/aionetwork.py +90 -0
  22. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/utils/basic.py +75 -0
  23. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/utils/bsontools.py +25 -0
  24. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/utils/imagetools.py +261 -0
  25. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base/utils/texttools.py +207 -0
  26. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base.egg-info/PKG-INFO +69 -0
  27. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base.egg-info/SOURCES.txt +28 -0
  28. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base.egg-info/dependency_links.txt +1 -0
  29. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base.egg-info/requires.txt +21 -0
  30. fastapi_mongo_base-0.8.30/src/fastapi_mongo_base.egg-info/top_level.txt +1 -0
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2016 The Python Packaging Authority (PyPA)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.2
2
+ Name: fastapi-mongo-base
3
+ Version: 0.8.30
4
+ Summary: A simple boilerplate application, including models and schemas and abstract router, for FastAPI with MongoDB
5
+ Author-email: Mahdi Kiani <mahdikiany@gmail.com>
6
+ Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
7
+ License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
10
+ this software and associated documentation files (the "Software"), to deal in
11
+ the Software without restriction, including without limitation the rights to
12
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
13
+ of the Software, and to permit persons to whom the Software is furnished to do
14
+ so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Project-URL: Homepage, https://github.com/mahdikiani/fastapi-mongo-base-app
28
+ Project-URL: Bug Reports, https://github.com/mahdikiani/fastapi-mongo-base-app/issues
29
+ Project-URL: Funding, https://github.com/mahdikiani/fastapi-mongo-base-app
30
+ Project-URL: Say Thanks!, https://saythanks.io/to/mahdikiani
31
+ Project-URL: Source, https://github.com/mahdikiani/fastapi-mongo-base-app
32
+ Keywords: fastapi,mongodb,beanie
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: Topic :: Software Development :: Build Tools
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.10
39
+ Classifier: Programming Language :: Python :: 3.11
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Programming Language :: Python :: 3 :: Only
42
+ Requires-Python: >=3.9
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE.txt
45
+ Requires-Dist: pydantic>=1.8.2
46
+ Requires-Dist: requests>=2.26.0
47
+ Requires-Dist: pyjwt[crypto]
48
+ Requires-Dist: singleton_package
49
+ Requires-Dist: fastapi>=0.65.0
50
+ Requires-Dist: uvicorn[standard]>=0.13.0
51
+ Provides-Extra: image
52
+ Requires-Dist: Pillow>=9.0.0; extra == "image"
53
+ Provides-Extra: test
54
+ Requires-Dist: coverage; extra == "test"
55
+ Provides-Extra: image-similarity
56
+ Requires-Dist: numpy>=1.21.0; extra == "image-similarity"
57
+ Requires-Dist: imagededup>=0.3.0; extra == "image-similarity"
58
+ Provides-Extra: all
59
+ Requires-Dist: Pillow>=9.0.0; extra == "all"
60
+ Requires-Dist: numpy>=1.21.0; extra == "all"
61
+ Requires-Dist: imagededup>=0.3.0; extra == "all"
62
+
63
+ # Fastapi Mongo
64
+
65
+ ## Contributing
66
+ Contributions are welcome! See CONTRIBUTING.md for more details on how to get involved.
67
+
68
+ ## License
69
+ Distributed under the MIT License. See LICENSE for more information.
@@ -0,0 +1,7 @@
1
+ # Fastapi Mongo
2
+
3
+ ## Contributing
4
+ Contributions are welcome! See CONTRIBUTING.md for more details on how to get involved.
5
+
6
+ ## License
7
+ Distributed under the MIT License. See LICENSE for more information.
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fastapi-mongo-base"
7
+ version = "0.8.30"
8
+ description = "A simple boilerplate application, including models and schemas and abstract router, for FastAPI with MongoDB"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = {file = "LICENSE.txt"}
12
+ keywords = ["fastapi", "mongodb", "beanie"]
13
+ authors = [
14
+ {name = "Mahdi Kiani", email = "mahdikiany@gmail.com"}
15
+ ]
16
+ maintainers = [
17
+ {name = "Mahdi Kiani", email = "mahdikiany@gmail.com"}
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 3 - Alpha",
21
+ "Intended Audience :: Developers",
22
+ "Topic :: Software Development :: Build Tools",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3 :: Only",
29
+ ]
30
+ dependencies = [
31
+ "pydantic>=1.8.2",
32
+ "requests>=2.26.0",
33
+ "pyjwt[crypto]",
34
+ "singleton_package",
35
+ "fastapi>=0.65.0",
36
+ "uvicorn[standard]>=0.13.0"
37
+ ]
38
+ optional-dependencies = {"image" = ["Pillow>=9.0.0"],"test" = ["coverage"], "image-similarity" = ["numpy>=1.21.0", "imagededup>=0.3.0"], "all" = ["Pillow>=9.0.0", "numpy>=1.21.0", "imagededup>=0.3.0"]}
39
+
40
+ [project.urls]
41
+ "Homepage" = "https://github.com/mahdikiani/fastapi-mongo-base-app"
42
+ "Bug Reports" = "https://github.com/mahdikiani/fastapi-mongo-base-app/issues"
43
+ "Funding" = "https://github.com/mahdikiani/fastapi-mongo-base-app"
44
+ "Say Thanks!" = "https://saythanks.io/to/mahdikiani"
45
+ "Source" = "https://github.com/mahdikiani/fastapi-mongo-base-app"
46
+
47
+ [tool.setuptools]
48
+ package-data = {"usso" = ["*.dat"]}
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,115 @@
1
+ import json
2
+ import logging
3
+ import uuid
4
+
5
+ from pymongo import UpdateOne
6
+
7
+ from .models import BaseEntity
8
+ from .tasks import TaskStatusEnum
9
+ from .utils import bsontools
10
+
11
+ try:
12
+ from server.config import Settings
13
+ except ImportError:
14
+ from .core.config import Settings
15
+
16
+
17
+ try:
18
+ from server.db import redis
19
+ except ImportError:
20
+ from redis import Redis
21
+
22
+ redis = Redis()
23
+
24
+
25
+ class CachedMixin(BaseEntity):
26
+ def is_done(self):
27
+ return (
28
+ getattr(self, "task_status", "done") in TaskStatusEnum.Finishes()
29
+ or self.is_deleted
30
+ )
31
+
32
+ async def is_cached(self):
33
+ return await redis.hexists(
34
+ f"{Settings.project_name}:{self.__class__.__name__}_updates_hash",
35
+ str(self.uid),
36
+ )
37
+
38
+ async def save(self, *args, **kwargs):
39
+ if self.is_done():
40
+ result = await super().save(*args, **kwargs)
41
+ await redis.hdel(
42
+ f"{Settings.project_name}:{self.__class__.__name__}_updates_hash",
43
+ str(self.uid),
44
+ )
45
+ return result
46
+ else:
47
+ await redis.hset(
48
+ f"{Settings.project_name}:{self.__class__.__name__}_updates_hash",
49
+ str(self.uid),
50
+ self.model_dump_json(),
51
+ )
52
+
53
+ @classmethod
54
+ async def flush_queue_to_db(cls):
55
+ # Get all items from the Redis hash in a single operation
56
+ items_data: dict[bytes, bytes] = await redis.hgetall(
57
+ f"{Settings.project_name}:{cls.__name__}_updates_hash"
58
+ )
59
+
60
+ # Clear the Redis hash after a successful batch write
61
+ await redis.delete(f"{Settings.project_name}:{cls.__name__}_updates_hash")
62
+
63
+ if items_data:
64
+ # Create a list of MongoDB upsert operations for the bulk update/insert
65
+ bulk_operations = []
66
+ for uid_bytes, item_data in items_data.items():
67
+ item_dict = json.loads(item_data)
68
+ item = cls(**item_dict)
69
+ uid = uuid.UUID(uid_bytes.decode("utf-8"))
70
+ filter_query = {"uid": bsontools.get_bson_value(uid)}
71
+ # Assuming the unique identifier is stored in _id
72
+ logging.info(
73
+ f"Flushing item {uid} to DB {bsontools.get_bson_value(item.model_dump())}"
74
+ )
75
+ update_query = {"$set": bsontools.get_bson_value(item.model_dump())}
76
+ bulk_operations.append(
77
+ UpdateOne(filter_query, update_query, upsert=True)
78
+ )
79
+
80
+ # Perform the bulk upsert operation in a single call
81
+ if bulk_operations:
82
+ res = await cls.get_motor_collection().bulk_write(bulk_operations)
83
+ logging.info(f"Flushed {len(bulk_operations)} items to DB \n{res}")
84
+
85
+ @classmethod
86
+ async def get_item(
87
+ cls,
88
+ uid,
89
+ user_id: uuid.UUID = None,
90
+ business_name: str = None,
91
+ is_deleted: bool = False,
92
+ *args,
93
+ **kwargs,
94
+ ) -> BaseEntity:
95
+ if user_id == None and kwargs.get("ignore_user_id") != True:
96
+ raise ValueError("user_id is required")
97
+ item_data = await redis.hget(
98
+ f"{Settings.project_name}:{cls.__name__}_updates_hash", str(uid)
99
+ )
100
+ if item_data:
101
+ item_dict = json.loads(item_data)
102
+ item = cls(**item_dict)
103
+ if user_id and getattr(item, "user_id", None) != user_id:
104
+ return None
105
+ if getattr(item, "business_name", None) != business_name:
106
+ return None
107
+ return item
108
+ return await super().get_item(
109
+ uid,
110
+ business_name=business_name,
111
+ user_id=user_id,
112
+ is_deleted=is_deleted,
113
+ *args,
114
+ **kwargs,
115
+ )
@@ -0,0 +1,184 @@
1
+ import asyncio
2
+ import logging
3
+ from collections import deque
4
+ from contextlib import asynccontextmanager
5
+
6
+ import fastapi
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.staticfiles import StaticFiles
9
+
10
+ from fastapi_mongo_base.core import db, exceptions
11
+
12
+ try:
13
+ from server.config import Settings
14
+ except ImportError:
15
+ from .config import Settings
16
+
17
+
18
+ async def health(request: fastapi.Request):
19
+ return {
20
+ "status": "up",
21
+ "host": request.url.hostname,
22
+ # "host2": request.base_url.hostname,
23
+ # "original_host":request.headers.get("x-original-host", "!not found!"),
24
+ # "forwarded_host": request.headers.get("X-Forwarded-Host", "forwarded_host"),
25
+ # "forwarded_proto": request.headers.get("X-Forwarded-Proto", "forwarded_proto"),
26
+ # "forwarded_for": request.headers.get("X-Forwarded-For", "forwarded_for"),
27
+ }
28
+
29
+
30
+ @asynccontextmanager
31
+ async def lifespan(app: fastapi.FastAPI, worker=None, init_functions=[], settings: Settings = None): # type: ignore
32
+ """Initialize application services."""
33
+ await db.init_mongo_db()
34
+
35
+ if worker:
36
+ app.state.worker = asyncio.create_task(worker())
37
+
38
+ for function in init_functions:
39
+ if asyncio.iscoroutinefunction(function):
40
+ await function()
41
+ else:
42
+ function()
43
+
44
+ logging.info("Startup complete")
45
+ yield
46
+ app.state.worker.cancel()
47
+ logging.info("Shutdown complete")
48
+
49
+
50
+ def setup_exception_handlers(
51
+ app: fastapi.FastAPI,
52
+ usso_handler: bool = True,
53
+ ufaas_handler: bool = True,
54
+ **kwargs,
55
+ ):
56
+ exception_handlers = exceptions.EXCEPTION_HANDLERS
57
+ if usso_handler:
58
+ from usso.fastapi.integration import (
59
+ EXCEPTION_HANDLERS as USSO_EXCEPTION_HANDLERS,
60
+ )
61
+
62
+ exception_handlers.update(USSO_EXCEPTION_HANDLERS)
63
+ if ufaas_handler:
64
+ from ufaas.fastapi.integration import (
65
+ EXCEPTION_HANDLERS as UFAAS_EXCEPTION_HANDLERS,
66
+ )
67
+
68
+ exception_handlers.update(UFAAS_EXCEPTION_HANDLERS)
69
+
70
+ for exc_class, handler in exception_handlers.items():
71
+ app.exception_handler(exc_class)(handler)
72
+
73
+
74
+ def setup_middlewares(
75
+ app: fastapi.FastAPI,
76
+ origins: list = None,
77
+ original_host_middleware: bool = False,
78
+ request_log_middleware: bool = False,
79
+ **kwargs,
80
+ ):
81
+ if origins:
82
+ app.add_middleware(
83
+ CORSMiddleware,
84
+ allow_origins=origins,
85
+ allow_credentials=True,
86
+ allow_methods=["*"],
87
+ allow_headers=["*"],
88
+ )
89
+
90
+ if original_host_middleware:
91
+ from ufaas_fastapi_business.core.middlewares import OriginalHostMiddleware
92
+
93
+ app.add_middleware(OriginalHostMiddleware)
94
+ if request_log_middleware:
95
+ from .middlewares import RequestLoggingMiddleware
96
+
97
+ app.add_middleware(RequestLoggingMiddleware)
98
+
99
+
100
+ def create_app(
101
+ *,
102
+ settings: Settings = None,
103
+ title=None,
104
+ description=None,
105
+ version="0.1.0",
106
+ serve_coverage: bool = False,
107
+ origins: list = None,
108
+ lifespan_func=None,
109
+ worker=None,
110
+ init_functions: list = [],
111
+ contact={
112
+ "name": "Mahdi Kiani",
113
+ "url": "https://github.com/mahdikiani/FastAPILaunchpad",
114
+ "email": "mahdikiany@gmail.com",
115
+ },
116
+ license_info={
117
+ "name": "MIT License",
118
+ "url": "https://github.com/mahdikiani/FastAPILaunchpad/blob/main/LICENSE",
119
+ },
120
+ usso_handler: bool = True,
121
+ ufaas_handler: bool = True,
122
+ original_host_middleware: bool = False,
123
+ request_log_middleware: bool = False,
124
+ **kwargs,
125
+ ) -> fastapi.FastAPI:
126
+ settings.config_logger()
127
+
128
+ """Create a FastAPI app with shared configurations."""
129
+ if settings is None:
130
+ settings = Settings()
131
+ if title is None:
132
+ title = settings.project_name.replace("-", " ").title()
133
+ if description is None:
134
+ description = getattr(settings, "project_description", None)
135
+ if version is None:
136
+ version = getattr(settings, "project_version", "0.1.0")
137
+ base_path: str = settings.base_path
138
+
139
+ if origins is None:
140
+ origins = ["http://localhost:8000"]
141
+
142
+ if lifespan_func is None:
143
+ lifespan_func = lambda app: lifespan(app, worker, init_functions, settings)
144
+
145
+ docs_url = f"{base_path}/docs"
146
+ openapi_url = f"{base_path}/openapi.json"
147
+
148
+ app = fastapi.FastAPI(
149
+ title=title,
150
+ version=version,
151
+ description=description,
152
+ lifespan=lifespan_func,
153
+ contact=contact,
154
+ license_info=license_info,
155
+ docs_url=docs_url,
156
+ openapi_url=openapi_url,
157
+ )
158
+
159
+ setup_exception_handlers(app, usso_handler, ufaas_handler, **kwargs)
160
+ setup_middlewares(
161
+ app,
162
+ origins,
163
+ original_host_middleware,
164
+ request_log_middleware,
165
+ **kwargs,
166
+ )
167
+
168
+ async def logs():
169
+ with open(settings.get_log_config()["info_log_path"], "rb") as f:
170
+ last_100_lines = deque(f, maxlen=100)
171
+
172
+ return [line.decode("utf-8") for line in last_100_lines]
173
+
174
+ app.get(f"{base_path}/health")(health)
175
+ app.get(f"{base_path}/logs", include_in_schema=False)(logs)
176
+
177
+ if serve_coverage:
178
+ app.mount(
179
+ f"{settings.base_path}/coverage",
180
+ StaticFiles(directory=settings.get_coverage_dir()),
181
+ name="coverage",
182
+ )
183
+
184
+ return app
@@ -0,0 +1,84 @@
1
+ """FastAPI server configuration."""
2
+
3
+ import dataclasses
4
+ import logging
5
+ import logging.config
6
+ import os
7
+
8
+ import dotenv
9
+ from singleton import Singleton
10
+
11
+ dotenv.load_dotenv()
12
+
13
+
14
+ @dataclasses.dataclass
15
+ class Settings(metaclass=Singleton):
16
+ """Server config settings."""
17
+
18
+ # base_dir: Path = Path(__file__).resolve().parent.parent
19
+ root_url: str = os.getenv("DOMAIN", default="http://localhost:8000")
20
+ project_name: str = os.getenv("PROJECT_NAME", default="PROJECT")
21
+ base_path: str = "/api/v1"
22
+ worker_update_time: int = int(os.getenv("WORKER_UPDATE_TIME", default=180))
23
+ testing: bool = os.getenv("DEBUG", default=False)
24
+
25
+ page_max_limit: int = 100
26
+
27
+ mongo_uri: str = os.getenv("MONGO_URI", default="mongodb://localhost:27017/")
28
+ redis_uri: str = os.getenv("REDIS_URI", default="redis://localhost:6379/0")
29
+
30
+ app_id: str = os.getenv("APP_ID")
31
+ app_secret: str = os.getenv("APP_SECRET")
32
+
33
+ JWT_CONFIG: str = os.getenv(
34
+ "USSO_JWT_CONFIG",
35
+ default='{"jwk_url": "https://sso.usso.io/website/jwks.json","type": "RS256","header": {"type": "Cookie", "name": "usso_access_token"} }',
36
+ )
37
+
38
+ @classmethod
39
+ def get_coverage_dir(cls):
40
+ return cls.base_dir / "htmlcov"
41
+
42
+ @classmethod
43
+ def get_log_config(cls, console_level: str = "INFO", file_level: str = "INFO", **kwargs):
44
+ log_config = {
45
+ "formatters": {
46
+ "standard": {
47
+ "format": "[{levelname} : {filename}:{lineno} : {asctime} -> {funcName:10}] {message}",
48
+ "style": "{",
49
+ }
50
+ },
51
+ "handlers": {
52
+ "console": {
53
+ "class": "logging.StreamHandler",
54
+ "level": console_level,
55
+ "formatter": "standard",
56
+ },
57
+ "file": {
58
+ "class": "logging.FileHandler",
59
+ "level": file_level,
60
+ "filename": cls.base_dir / "logs" / "info.log",
61
+ "formatter": "standard",
62
+ },
63
+ },
64
+ "loggers": {
65
+ "": {
66
+ "handlers": ["console", "file"],
67
+ "level": "INFO",
68
+ "propagate": True,
69
+ },
70
+ "httpx": {
71
+ "handlers": ["console", "file"],
72
+ "level": "WARNING",
73
+ "propagate": False,
74
+ },
75
+ },
76
+ "version": 1,
77
+ }
78
+ return log_config
79
+
80
+ @classmethod
81
+ def config_logger(cls):
82
+ (cls.base_dir / "logs").mkdir(parents=True, exist_ok=True)
83
+
84
+ logging.config.dictConfig(cls.get_log_config())
@@ -0,0 +1,45 @@
1
+ import logging
2
+
3
+ from beanie import init_beanie
4
+ from motor.motor_asyncio import AsyncIOMotorClient
5
+
6
+ from fastapi_mongo_base.models import BaseEntity
7
+ from fastapi_mongo_base.utils import basic
8
+
9
+ try:
10
+ from server.config import Settings
11
+ except ImportError:
12
+ from .config import Settings
13
+
14
+
15
+ async def init_mongo_db():
16
+ client = AsyncIOMotorClient(Settings.mongo_uri)
17
+ db = client.get_database(Settings.project_name)
18
+ await init_beanie(
19
+ database=db,
20
+ document_models=[
21
+ cls
22
+ for cls in basic.get_all_subclasses(BaseEntity)
23
+ if not (
24
+ hasattr(cls, "Settings")
25
+ and getattr(cls.Settings, "__abstract__", False)
26
+ )
27
+ ],
28
+ )
29
+ return db
30
+
31
+
32
+ def init_redis():
33
+ try:
34
+ from redis import Redis as RedisSync
35
+ from redis.asyncio.client import Redis
36
+
37
+ if getattr(Settings, "redis_uri"):
38
+ redis_sync: RedisSync = RedisSync.from_url(Settings.redis_uri)
39
+ redis: Redis = Redis.from_url(Settings.redis_uri)
40
+ except ImportError or AttributeError or Exception as e:
41
+ logging.error(f"Error initializing Redis: {e}")
42
+ redis_sync = None
43
+ redis = None
44
+
45
+ return redis_sync, redis
@@ -0,0 +1,36 @@
1
+ from enum import Enum
2
+
3
+
4
+ class Language(str, Enum):
5
+ English = "English"
6
+ Persian = "Persian"
7
+
8
+ @classmethod
9
+ def has_value(cls, value):
10
+ return value in cls._value2member_map_
11
+
12
+ @property
13
+ def fa(self):
14
+ return {
15
+ Language.English: "انگلیسی",
16
+ Language.Persian: "فارسی",
17
+ }[self]
18
+
19
+ @property
20
+ def abbreviation(self):
21
+ return {
22
+ Language.English: "en",
23
+ Language.Persian: "fa",
24
+ }[self]
25
+
26
+ def get_dict(self):
27
+ return {
28
+ "en": self.name,
29
+ "fa": self.fa,
30
+ "value": self.value,
31
+ "abbreviation": self.abbreviation,
32
+ }
33
+
34
+ @classmethod
35
+ def get_choices(cls):
36
+ return [item.get_dict() for item in cls]