fastapi-mongo-base 0.8.30__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,261 @@
1
+ import base64
2
+ import itertools
3
+ from fractions import Fraction
4
+ from io import BytesIO
5
+ from typing import Literal
6
+
7
+ import httpx
8
+ from aiocache import cached
9
+ from PIL import Image
10
+
11
+
12
+ def rgb_to_hex(rgb):
13
+ """Convert RGB color to HEX."""
14
+ r = min(255, max(0, rgb[0]))
15
+ g = min(255, max(0, rgb[1]))
16
+ b = min(255, max(0, rgb[2]))
17
+ return "#{:02x}{:02x}{:02x}".format(int(r), int(g), int(b))
18
+
19
+
20
+ def rgb_to_xyz(rgb):
21
+ r, g, b = rgb
22
+
23
+ # Normalize the RGB values to the range [0, 1]
24
+ r = r / 255.0
25
+ g = g / 255.0
26
+ b = b / 255.0
27
+
28
+ # Apply the gamma correction (inverse of sRGB companding)
29
+ if r > 0.04045:
30
+ r = ((r + 0.055) / 1.055) ** 2.4
31
+ else:
32
+ r = r / 12.92
33
+
34
+ if g > 0.04045:
35
+ g = ((g + 0.055) / 1.055) ** 2.4
36
+ else:
37
+ g = g / 12.92
38
+
39
+ if b > 0.04045:
40
+ b = ((b + 0.055) / 1.055) ** 2.4
41
+ else:
42
+ b = b / 12.92
43
+
44
+ # Convert to XYZ using the RGB to XYZ matrix transformation
45
+ x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375
46
+ y = r * 0.2126729 + g * 0.7151522 + b * 0.0721750
47
+ z = r * 0.0193339 + g * 0.1191920 + b * 0.9503041
48
+
49
+ return (x, y, z)
50
+
51
+
52
+ def xyz_to_lab(xyz):
53
+ x, y, z = xyz
54
+
55
+ # Define the reference white point
56
+ x_ref = 0.95047
57
+ y_ref = 1.00000
58
+ z_ref = 1.08883
59
+
60
+ # Normalize the XYZ values by the reference white point
61
+ x = x / x_ref
62
+ y = y / y_ref
63
+ z = z / z_ref
64
+
65
+ # Apply the LAB transformation
66
+ def f(t):
67
+ if t > 0.008856:
68
+ return t ** (1 / 3)
69
+ else:
70
+ return (7.787 * t) + (16 / 116)
71
+
72
+ l = 116 * f(y) - 16
73
+ a = 500 * (f(x) - f(y))
74
+ b = 200 * (f(y) - f(z))
75
+
76
+ return (l, a, b)
77
+
78
+
79
+ def rgb_to_lab(rgb):
80
+ xyz = rgb_to_xyz(rgb)
81
+ lab = xyz_to_lab(xyz)
82
+ return lab
83
+
84
+
85
+ def add_watermark_to_image(
86
+ background_image_path: str | bytes | Image.Image,
87
+ watermark_image_path: str,
88
+ position: tuple[int, int] = (0, 0),
89
+ resize: tuple[int, int] = None,
90
+ ) -> Image:
91
+ """
92
+ Add an watermark image to a background image at a specified position using PIL.
93
+
94
+ :param background_image_path: Path to the background image.
95
+ :param watermark_image_path: Path to the watermark image.
96
+ :param position: A tuple (x, y) representing the position to place the watermark.
97
+ :param resize: A tuple (w, h) representing the target size of placed watermark.
98
+ :return: An Image object with the watermark added.
99
+ """
100
+
101
+ background = Image.open(background_image_path)
102
+ w, h = background.size
103
+ watermark = Image.open(watermark_image_path)
104
+
105
+ position = (w + position[0]) % w, (h + position[1]) % h
106
+
107
+ if resize:
108
+ watermark = watermark.resize((50, 50))
109
+
110
+ background.paste(watermark, position[0], position[1], watermark)
111
+
112
+ return background
113
+
114
+
115
+ def get_aspect_ratio_str(width: int, height: int) -> str:
116
+ fr = Fraction(height, width)
117
+ return f"{fr.denominator}:{fr.numerator}"
118
+
119
+
120
+ def resize_image(
121
+ image: Image.Image | BytesIO, new_width=384, new_height=None
122
+ ) -> Image.Image:
123
+ if isinstance(image, BytesIO):
124
+ image = Image.open(image)
125
+
126
+ if new_width is None and new_height is None:
127
+ return image
128
+
129
+ original_width, original_height = image.size
130
+ aspect_ratio = original_height / original_width
131
+
132
+ if new_height is None:
133
+ new_height = int(aspect_ratio * new_width)
134
+ elif new_width is None:
135
+ new_width = int(new_height / aspect_ratio)
136
+
137
+ resized_image = image.resize((new_width, new_height))
138
+ return resized_image
139
+
140
+
141
+ def split_image(image: Image.Image, sections=(2, 2), **kwargs) -> list[Image.Image]:
142
+ parts = []
143
+ for i, j in itertools.product(range(sections[0]), range(sections[1])):
144
+ x = j * image.width // sections[0]
145
+ y = i * image.height // sections[1]
146
+ region = image.crop(
147
+ (x, y, x + image.width // sections[0], y + image.height // sections[1])
148
+ )
149
+ parts.append(region)
150
+ return parts
151
+
152
+
153
+ def convert_image(
154
+ image: Image.Image, format: Literal["JPEG", "PNG", "WEBP", "BMP", "GIF"] = "JPEG"
155
+ ) -> Image.Image:
156
+ color_mode = "RGB" if format not in ("PNG", "WEBP") else "RGBA"
157
+ return image.convert(color_mode)
158
+
159
+
160
+ def convert_image_bytes(
161
+ image: Image.Image,
162
+ format: Literal["JPEG", "PNG", "WEBP", "BMP", "GIF"] = "JPEG",
163
+ quality=None,
164
+ ) -> BytesIO:
165
+ image_bytes = BytesIO()
166
+ convert_image(image, format).save(
167
+ image_bytes,
168
+ format=format,
169
+ **{"quality": quality} if quality else {},
170
+ )
171
+ image_bytes.seek(0)
172
+ return image_bytes
173
+
174
+
175
+ def strip_metadata(
176
+ image: Image.Image,
177
+ format: Literal["JPEG", "PNG", "WEBP", "BMP", "GIF"] = "JPEG",
178
+ ) -> Image.Image:
179
+ """Strip metadata from the image by re-creating it in memory."""
180
+ return convert_image(image, format)
181
+
182
+
183
+ def image_to_base64(
184
+ image: Image.Image,
185
+ format: Literal["JPEG", "PNG", "WEBP", "BMP", "GIF"] = "JPEG",
186
+ quality: int = 90,
187
+ *,
188
+ include_base64_header: bool = True,
189
+ **kwargs,
190
+ ) -> str:
191
+ buffered = convert_image_bytes(image, format, quality)
192
+ base64_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
193
+ if include_base64_header:
194
+ return f"data:image/{format};base64,{base64_str}"
195
+ return base64_str
196
+
197
+
198
+ def load_from_base64(encoded: str) -> Image.Image:
199
+ """Load an image from a base64 encoded string."""
200
+ encoded = encoded.split(",")[1]
201
+ encoded += "=" * (4 - len(encoded) % 4)
202
+ buffered = BytesIO(base64.b64decode(encoded))
203
+ return Image.open(buffered)
204
+
205
+
206
+ async def load_from_url(url: str) -> Image.Image:
207
+ """Load an image from a URL."""
208
+ async with httpx.AsyncClient() as client:
209
+ r = await client.get(url)
210
+ r.raise_for_status()
211
+ buffered = BytesIO(r.content)
212
+ return Image.open(buffered)
213
+
214
+
215
+ def compress_image(image: Image.Image, max_size_kb: int) -> Image.Image:
216
+ """Compress image to fit within max_size_kb."""
217
+ while True:
218
+ buffered = BytesIO()
219
+ image.save(buffered, format="JPEG", optimize=True, quality=85)
220
+ encoded = base64.b64encode(buffered.getvalue()).decode()
221
+ if len(encoded) <= max_size_kb * 1024:
222
+ break
223
+ new_width = int(image.width * 4 / 5)
224
+ new_height = int(image.height * 4 / 5)
225
+ image = resize_image(image, new_width, new_height)
226
+ return image
227
+
228
+
229
+ @cached(ttl=60 * 60 * 24)
230
+ async def download_image(
231
+ url: str, max_width: int | None = None, max_size_kb: int | None = None, **kwargs
232
+ ) -> Image.Image:
233
+ """Fetch, resize, remove metadata, and compress an image to fit the specified constraints."""
234
+ # Load image from either base64 or URL
235
+ image = (
236
+ load_from_base64(url)
237
+ if url.startswith("data:image")
238
+ else await load_from_url(url)
239
+ )
240
+
241
+ # Prepare image (convert to RGB and strip metadata)
242
+ image = strip_metadata(image)
243
+
244
+ if max_size_kb is None and max_width is None:
245
+ return image
246
+
247
+ # Resize if needed
248
+ image = resize_image(image, max_width)
249
+
250
+ # Compress if needed
251
+ if max_size_kb is not None:
252
+ image = compress_image(image, max_size_kb)
253
+
254
+ return image
255
+
256
+
257
+ async def download_image_base64(
258
+ url: str, max_width: int | None = None, max_size_kb: int | None = None, **kwargs
259
+ ) -> str:
260
+ image = await download_image(url, max_width, max_size_kb, **kwargs)
261
+ return image_to_base64(image, **kwargs)
@@ -0,0 +1,207 @@
1
+ import random
2
+ import re
3
+ import string
4
+ import unicodedata
5
+ import uuid
6
+ from urllib.parse import urlparse
7
+
8
+
9
+ def backtick_formatter(text: str):
10
+ text = text.strip().strip("```json").strip("```").strip()
11
+ return text
12
+
13
+
14
+ def format_string_keys(text: str) -> set[str]:
15
+ return {t[1] for t in string.Formatter().parse(text) if t[1]}
16
+
17
+
18
+ def format_string_fixer(**kwargs):
19
+ list_length = len(next(iter(kwargs.values())))
20
+
21
+ # Initialize the target list
22
+ target = []
23
+
24
+ # Iterate over each index of the lists
25
+ for i in range(list_length):
26
+ # Create a new dictionary for each index
27
+ entry = {key: kwargs[key][i] for key in kwargs}
28
+ # Append the new dictionary to the target list
29
+ target.append(entry)
30
+
31
+ return target
32
+
33
+
34
+ def escape_markdown(text: str):
35
+ replacements = [
36
+ ("_", r"\_"),
37
+ ("*", r"\*"),
38
+ ("[", r"\["),
39
+ ("]", r"\]"),
40
+ ("(", r"\("),
41
+ (")", r"\)"),
42
+ ("~", r"\~"),
43
+ # ("`", r"\`"),
44
+ (">", r"\>"),
45
+ ("#", r"\#"),
46
+ ("+", r"\+"),
47
+ ("-", r"\-"),
48
+ ("=", r"\="),
49
+ ("|", r"\|"),
50
+ ("{", r"\{"),
51
+ ("}", r"\}"),
52
+ (".", r"\."),
53
+ ("!", r"\!"),
54
+ ("=", r"\="),
55
+ ]
56
+
57
+ for old, new in replacements:
58
+ text = text.replace(old, new)
59
+
60
+ return text
61
+
62
+
63
+ def split_text(text: str, max_chunk_size=4096):
64
+ # Split text into paragraphs
65
+ paragraphs = text.split("\n")
66
+ chunks = []
67
+ current_chunk = ""
68
+
69
+ for paragraph in paragraphs:
70
+ if len(current_chunk) + len(paragraph) + 1 > max_chunk_size:
71
+ if current_chunk:
72
+ if current_chunk.count("```") % 2 == 1:
73
+ chunks.append(current_chunk[: current_chunk.rfind("```")].strip())
74
+ current_chunk = current_chunk[current_chunk.rfind("```") :]
75
+ continue
76
+
77
+ chunks.append(current_chunk.strip())
78
+ current_chunk = ""
79
+ continue
80
+
81
+ if len(paragraph) > max_chunk_size:
82
+ # Split paragraph into sentences
83
+ sentences = re.split(r"(?<=[.!?]) +", paragraph)
84
+ for sentence in sentences:
85
+ if len(current_chunk) + len(sentence) + 1 > max_chunk_size:
86
+ if current_chunk:
87
+ chunks.append(current_chunk.strip())
88
+ current_chunk = ""
89
+ if len(sentence) > max_chunk_size:
90
+ # Split sentence into words
91
+ words = sentence.split(" ")
92
+ for word in words:
93
+ if len(current_chunk) + len(word) + 1 > max_chunk_size:
94
+ if current_chunk:
95
+ chunks.append(current_chunk.strip())
96
+ current_chunk = ""
97
+ current_chunk += word + " "
98
+ else:
99
+ current_chunk += sentence + " "
100
+ else:
101
+ current_chunk += paragraph + "\n"
102
+ if current_chunk:
103
+ chunks.append(current_chunk.strip())
104
+
105
+ return chunks
106
+
107
+
108
+ def replace_unicode_digits(match: re.Match) -> str:
109
+ char = match.group()
110
+ return unicodedata.digit(char)
111
+
112
+
113
+ def convert_to_english_digits(input_str: str):
114
+ non_ascii_digit_pattern = re.compile(r"\d", re.UNICODE)
115
+ return non_ascii_digit_pattern.sub(replace_unicode_digits, input_str)
116
+
117
+
118
+ regex = re.compile(
119
+ r"^(https?|ftp):\/\/" # http:// or https:// or ftp://
120
+ r"(?"
121
+ r":(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|" # domain...
122
+ # r"localhost|" # or localhost...
123
+ # r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|" # or IPv4...
124
+ # r"\[?[A-F0-9]*:[A-F0-9:]+\]?" # or IPv6...
125
+ r")"
126
+ r"(?::\d+)?" # optional port
127
+ r"(?:\/[-A-Z0-9+&@#\/%=~_|$]*)*$",
128
+ re.IGNORECASE,
129
+ )
130
+ phone_regex = re.compile(r"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$")
131
+ email_regex = re.compile(r"^[a-zA-Z\._]+@[a-zA-Z0-9\.-_]+\.[a-zA-Z]{2,}$")
132
+ username_regex = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]{2,16}$")
133
+
134
+
135
+ def is_valid_uuid(val):
136
+ try:
137
+ uuid.UUID(str(val))
138
+ return True
139
+ except ValueError:
140
+ return False
141
+
142
+
143
+ def is_valid_url(url):
144
+ # Check if the URL matches the regex
145
+ if re.match(regex, url) is None:
146
+ return False
147
+
148
+ # Additional check using urllib.parse to ensure proper scheme and netloc
149
+ parsed_url = urlparse(url)
150
+ return all([parsed_url.scheme, parsed_url.netloc])
151
+
152
+
153
+ def is_username(username):
154
+ return username_regex.search(username)
155
+
156
+
157
+ def is_email(email):
158
+ return email_regex.search(email)
159
+
160
+
161
+ def is_phone(phone):
162
+ return phone_regex.search(phone)
163
+
164
+
165
+ def generate_random_chars(length=6, characters=string.ascii_letters + string.digits):
166
+ # Generate the random characters
167
+ return "".join(random.choices(characters, k=length))
168
+
169
+
170
+ def replace_whitespace(match: re.Match) -> str:
171
+ if "\n" in match.group():
172
+ return "\n"
173
+ else:
174
+ return " "
175
+
176
+
177
+ def remove_whitespace(text: str) -> str:
178
+ return re.sub(r"\s+", replace_whitespace, text)
179
+
180
+
181
+ def sanitize_filename(
182
+ draft_name: str, max_length: int = 0, space_remover: bool = True
183
+ ) -> str:
184
+ # get filename from URL
185
+ if draft_name.startswith("http"):
186
+ url_parts = urlparse(draft_name)
187
+ draft_name = url_parts.path
188
+
189
+ # Remove path and extension
190
+ draft_name = draft_name.split("/")[-1].strip()
191
+ dotted_name = draft_name.split(".")
192
+ pure_name = ".".join(dotted_name[:-1] if len(dotted_name) > 1 else dotted_name)
193
+
194
+ # Remove invalid characters and replace spaces with underscores
195
+ # Valid characters: alphanumeric, underscores, and periods and spaces
196
+ sanitized = re.sub(r"[^a-zA-Z0-9_. ]", "", pure_name)
197
+
198
+ if max_length > 0:
199
+ position = pure_name.find(" ", max_length * 4 // 5)
200
+ if position > max_length * 6 // 5 or position == -1:
201
+ position = max_length
202
+ sanitized[:position] # Limit to 100 characters
203
+
204
+ if space_remover:
205
+ sanitized = sanitized.replace(" ", "_") # Replace spaces with underscores
206
+
207
+ return sanitized
@@ -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,25 @@
1
+ fastapi_mongo_base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ fastapi_mongo_base/cached.py,sha256=VcmunUkKxpJyeIq_fcGJGcJ_kzTcqZH8SP9FOa9b52A,3799
3
+ fastapi_mongo_base/handlers.py,sha256=-dckS-qetIs990VFItOMY0yWxMzMuP5PqHuMgQFeA3w,1006
4
+ fastapi_mongo_base/models.py,sha256=pLem_-cKOAu208zycigTDGBUG4nnq__jwx_n_hd6nVQ,11330
5
+ fastapi_mongo_base/routes.py,sha256=BbWglY01tP0kpXOW6FEA5ydtp7kcwUaMFJRakxIkpR4,9275
6
+ fastapi_mongo_base/schemas.py,sha256=MUYxkA2WCCl5r1vz3nxGlKIbIMpfWkhmsBkKC-lf7gg,2741
7
+ fastapi_mongo_base/tasks.py,sha256=yPMTc83I4hTZyAybzGIt-yYmUjt3ZarNQE5r73J1Mm4,9109
8
+ fastapi_mongo_base/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ fastapi_mongo_base/core/app_factory.py,sha256=GZVUlGCRrtR1YhlXvWzdlyiTsYeXGSg1GA5MQ3jMUqc,5306
10
+ fastapi_mongo_base/core/config.py,sha256=oCd_WN9h9NVIlFikdUdgOA1UtNtA4LYPMswPaeGxDr8,2703
11
+ fastapi_mongo_base/core/db.py,sha256=9Uvqi6goBuNSdTDTRmp2BCrklqFSHJx2zjc_GD9jzWQ,1235
12
+ fastapi_mongo_base/core/enums.py,sha256=f9fzuC9TlO00NnvntMOrSjGuYj_qXWnIxheAYfanpFk,782
13
+ fastapi_mongo_base/core/exceptions.py,sha256=gv6kaC62AVfTjIYsQ3tldSx_xcuKH0iKL6NH_jX6Pu8,2449
14
+ fastapi_mongo_base/core/middlewares.py,sha256=3sJH3eR1GsSsmrAp-DpbreIGWdt3J83I4ErkK4v7sqw,797
15
+ fastapi_mongo_base/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ fastapi_mongo_base/utils/aionetwork.py,sha256=92OJcp0CC-N7ugxveHueIdkQzJVjpjYhd2SnfOAA40U,2705
17
+ fastapi_mongo_base/utils/basic.py,sha256=R5gMPNFzWjFEaYQ_0T3LFFVhEEgd2q6pSv0Q1Tk303E,2543
18
+ fastapi_mongo_base/utils/bsontools.py,sha256=GTFXxw_6QtNVyXH5G52Q2hifOrJ-0oC75SaSQ292PZY,657
19
+ fastapi_mongo_base/utils/imagetools.py,sha256=fUP0JXkYDiPxR92sKglq8nOX-MG-lQynZqgUyG-gmEk,7296
20
+ fastapi_mongo_base/utils/texttools.py,sha256=6Jr1tdxaoTpTuRIIImlsUDGpUAE7we--DRYN7yVuuP8,6068
21
+ fastapi_mongo_base-0.8.30.dist-info/LICENSE.txt,sha256=ceC9ZJOV9H6CtQDcYmHOS46NA3dHJ_WD4J9blH513pc,1081
22
+ fastapi_mongo_base-0.8.30.dist-info/METADATA,sha256=MxMe9SjuI9fb1q9tneSb-tz3oOLM2xJbonmZJWqEuGA,3317
23
+ fastapi_mongo_base-0.8.30.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
24
+ fastapi_mongo_base-0.8.30.dist-info/top_level.txt,sha256=EyddBr2RKKxMM_0PqvIjU0aLiQL88J-QNTqkOUGRVMo,19
25
+ fastapi_mongo_base-0.8.30.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.8.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ fastapi_mongo_base