aiteamutils 0.2.135__py3-none-any.whl → 0.2.137__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.
- aiteamutils/base_repository.py +2 -2
- aiteamutils/base_service.py +50 -14
- aiteamutils/database.py +2 -2
- aiteamutils/files.py +255 -0
- aiteamutils/version.py +1 -1
- {aiteamutils-0.2.135.dist-info → aiteamutils-0.2.137.dist-info}/METADATA +1 -1
- {aiteamutils-0.2.135.dist-info → aiteamutils-0.2.137.dist-info}/RECORD +8 -7
- {aiteamutils-0.2.135.dist-info → aiteamutils-0.2.137.dist-info}/WHEEL +0 -0
aiteamutils/base_repository.py
CHANGED
@@ -32,7 +32,7 @@ class BaseRepository(Generic[ModelType]):
|
|
32
32
|
raise CustomException(
|
33
33
|
ErrorCode.DB_CONNECTION_ERROR,
|
34
34
|
detail="Session cannot be None",
|
35
|
-
source_function=f"{self.__class__.__name__}.session"
|
35
|
+
source_function=f"base_repository.{self.__class__.__name__}.session"
|
36
36
|
)
|
37
37
|
self._session = value
|
38
38
|
|
@@ -57,7 +57,7 @@ class BaseRepository(Generic[ModelType]):
|
|
57
57
|
raise CustomException(
|
58
58
|
ErrorCode.INTERNAL_ERROR,
|
59
59
|
detail=str(e),
|
60
|
-
source_function=f"{self.__class__.__name__}.create",
|
60
|
+
source_function=f"base_repository.{self.__class__.__name__}.create",
|
61
61
|
original_error=e
|
62
62
|
)
|
63
63
|
|
aiteamutils/base_service.py
CHANGED
@@ -45,7 +45,8 @@ class BaseService(Generic[ModelType]):
|
|
45
45
|
fk_check: List[Dict[str, Any]] | None = None,
|
46
46
|
org_ulid_position: str = "organization_ulid",
|
47
47
|
role_permission: str | None = None,
|
48
|
-
token_settings: Dict[str, Any] | None = None
|
48
|
+
token_settings: Dict[str, Any] | None = None,
|
49
|
+
storage_dir: str | None = None
|
49
50
|
) -> ModelType:
|
50
51
|
|
51
52
|
if role_permission:
|
@@ -60,7 +61,7 @@ class BaseService(Generic[ModelType]):
|
|
60
61
|
raise CustomException(
|
61
62
|
ErrorCode.FORBIDDEN,
|
62
63
|
detail=f"{role_permission}",
|
63
|
-
source_function=f"{self.__class__.__name__}.create"
|
64
|
+
source_function=f"base_service.{self.__class__.__name__}.create.permission_result"
|
64
65
|
)
|
65
66
|
|
66
67
|
try:
|
@@ -72,25 +73,60 @@ class BaseService(Generic[ModelType]):
|
|
72
73
|
if fk_check:
|
73
74
|
await validate_unique_fields(self.db_session, fk_check, find_value=False)
|
74
75
|
|
76
|
+
# _files로 끝나는 키 처리
|
77
|
+
file_keys = [key for key in entity_data.keys() if key.endswith('_files')]
|
78
|
+
file_infos = {}
|
79
|
+
|
80
|
+
if file_keys and not storage_dir:
|
81
|
+
raise CustomException(
|
82
|
+
ErrorCode.INVALID_INPUT,
|
83
|
+
detail="storage_dir is required for file upload",
|
84
|
+
source_function=f"base_service.{self.__class__.__name__}.create.file_keys"
|
85
|
+
)
|
86
|
+
|
87
|
+
# 먼저 엔티티를 생성하여 ULID를 얻음
|
75
88
|
result = await self.repository.create(
|
76
89
|
entity_data=entity_data,
|
77
90
|
exclude_entities=exclude_entities
|
78
91
|
)
|
92
|
+
|
93
|
+
# 파일 처리
|
94
|
+
for file_key in file_keys:
|
95
|
+
files_data = entity_data.pop(file_key) # 파일 데이터 추출 및 제거
|
96
|
+
if files_data:
|
97
|
+
from .files import FileHandler
|
98
|
+
file_infos[file_key] = await FileHandler.save_files(
|
99
|
+
files=files_data,
|
100
|
+
storage_dir=storage_dir,
|
101
|
+
entity_name=self.model.__tablename__,
|
102
|
+
entity_ulid=result.ulid,
|
103
|
+
db_session=self.db_session
|
104
|
+
)
|
79
105
|
|
80
106
|
# 결과 반환
|
81
107
|
if response_model:
|
82
|
-
|
108
|
+
processed_result = process_response(result, response_model)
|
109
|
+
# 파일 정보 추가
|
110
|
+
for key, value in file_infos.items():
|
111
|
+
processed_result[key] = value
|
112
|
+
return processed_result
|
83
113
|
else:
|
84
114
|
return result
|
85
115
|
|
86
116
|
except CustomException as e:
|
117
|
+
# 파일 저장 실패 시 저장된 파일들 삭제
|
118
|
+
if 'file_infos' in locals():
|
119
|
+
for file_list in file_infos.values():
|
120
|
+
for file_info in file_list:
|
121
|
+
from .files import FileHandler
|
122
|
+
await FileHandler.delete_files(file_info["storage_path"])
|
87
123
|
raise e
|
88
124
|
except Exception as e:
|
89
125
|
# 다른 예외 처리
|
90
126
|
raise CustomException(
|
91
127
|
ErrorCode.INTERNAL_ERROR,
|
92
128
|
detail=str(e),
|
93
|
-
source_function=f"{self.__class__.__name__}.create",
|
129
|
+
source_function=f"base_service.{self.__class__.__name__}.create",
|
94
130
|
original_error=e
|
95
131
|
)
|
96
132
|
|
@@ -117,7 +153,7 @@ class BaseService(Generic[ModelType]):
|
|
117
153
|
raise CustomException(
|
118
154
|
ErrorCode.INVALID_INPUT,
|
119
155
|
detail="Either 'ulid' or 'conditions' must be provided.",
|
120
|
-
source_function="
|
156
|
+
source_function=f"base_service.{self.__class__.__name__}.update.ulid_or_conditions"
|
121
157
|
)
|
122
158
|
|
123
159
|
# ulid로 조건 생성
|
@@ -126,7 +162,7 @@ class BaseService(Generic[ModelType]):
|
|
126
162
|
raise CustomException(
|
127
163
|
ErrorCode.VALIDATION_ERROR,
|
128
164
|
detail=ulid,
|
129
|
-
source_function=f"{self.__class__.__name__}.update"
|
165
|
+
source_function=f"base_service.{self.__class__.__name__}.update.ulid_or_conditions"
|
130
166
|
)
|
131
167
|
|
132
168
|
conditions = {"ulid": ulid}
|
@@ -147,7 +183,7 @@ class BaseService(Generic[ModelType]):
|
|
147
183
|
raise CustomException(
|
148
184
|
ErrorCode.INTERNAL_ERROR,
|
149
185
|
detail=str(e),
|
150
|
-
source_function=f"{self.__class__.__name__}.update",
|
186
|
+
source_function=f"base_service.{self.__class__.__name__}.update",
|
151
187
|
original_error=e
|
152
188
|
)
|
153
189
|
|
@@ -165,14 +201,14 @@ class BaseService(Generic[ModelType]):
|
|
165
201
|
raise CustomException(
|
166
202
|
ErrorCode.VALIDATION_ERROR,
|
167
203
|
detail=ulid,
|
168
|
-
source_function=f"{self.__class__.__name__}.delete"
|
204
|
+
source_function=f"base_service.{self.__class__.__name__}.delete.ulid_validation"
|
169
205
|
)
|
170
206
|
|
171
207
|
if not ulid and not conditions:
|
172
208
|
raise CustomException(
|
173
209
|
ErrorCode.INVALID_INPUT,
|
174
210
|
detail="Either 'ulid' or 'conditions' must be provided.",
|
175
|
-
source_function="
|
211
|
+
source_function=f"base_service.{self.__class__.__name__}.delete.ulid_or_conditions"
|
176
212
|
)
|
177
213
|
|
178
214
|
# ulid로 조건 생성
|
@@ -190,7 +226,7 @@ class BaseService(Generic[ModelType]):
|
|
190
226
|
raise CustomException(
|
191
227
|
ErrorCode.INTERNAL_ERROR,
|
192
228
|
detail=str(e),
|
193
|
-
source_function=f"{self.__class__.__name__}.delete",
|
229
|
+
source_function=f"base_service.{self.__class__.__name__}.delete",
|
194
230
|
original_error=e
|
195
231
|
)
|
196
232
|
|
@@ -246,7 +282,7 @@ class BaseService(Generic[ModelType]):
|
|
246
282
|
except Exception as e:
|
247
283
|
raise CustomException(
|
248
284
|
ErrorCode.INTERNAL_ERROR,
|
249
|
-
source_function=f"{self.__class__.__name__}.list",
|
285
|
+
source_function=f"base_service.{self.__class__.__name__}.list",
|
250
286
|
original_error=e
|
251
287
|
)
|
252
288
|
|
@@ -268,7 +304,7 @@ class BaseService(Generic[ModelType]):
|
|
268
304
|
raise CustomException(
|
269
305
|
ErrorCode.INVALID_INPUT,
|
270
306
|
detail="Either 'ulid' or 'conditions' must be provided.",
|
271
|
-
source_function="
|
307
|
+
source_function=f"base_service.{self.__class__.__name__}.get.ulid_or_conditions"
|
272
308
|
)
|
273
309
|
|
274
310
|
# ulid로 조건 생성
|
@@ -277,7 +313,7 @@ class BaseService(Generic[ModelType]):
|
|
277
313
|
raise CustomException(
|
278
314
|
ErrorCode.VALIDATION_ERROR,
|
279
315
|
detail=ulid,
|
280
|
-
source_function=f"{self.__class__.__name__}.
|
316
|
+
source_function=f"base_service.{self.__class__.__name__}.get.ulid_validation"
|
281
317
|
)
|
282
318
|
|
283
319
|
conditions = {"ulid": ulid}
|
@@ -295,7 +331,7 @@ class BaseService(Generic[ModelType]):
|
|
295
331
|
raise CustomException(
|
296
332
|
ErrorCode.INTERNAL_ERROR,
|
297
333
|
detail=str(e),
|
298
|
-
source_function=f"{self.__class__.__name__}.get",
|
334
|
+
source_function=f"base_service.{self.__class__.__name__}.get",
|
299
335
|
original_error=e
|
300
336
|
)
|
301
337
|
|
aiteamutils/database.py
CHANGED
@@ -341,7 +341,7 @@ async def update_entity(
|
|
341
341
|
raise CustomException(
|
342
342
|
ErrorCode.NOT_FOUND,
|
343
343
|
detail=f"{model.__name__}|{conditions}.",
|
344
|
-
source_function="database.update_entity"
|
344
|
+
source_function="database.update_entity.not_entity"
|
345
345
|
)
|
346
346
|
|
347
347
|
# 기존 데이터를 딕셔너리로 변환
|
@@ -394,7 +394,7 @@ async def delete_entity(
|
|
394
394
|
raise CustomException(
|
395
395
|
ErrorCode.NOT_FOUND,
|
396
396
|
detail=f"{model.__name__}|{conditions}.",
|
397
|
-
source_function="database.delete_entity"
|
397
|
+
source_function="database.delete_entity.not_entity"
|
398
398
|
)
|
399
399
|
|
400
400
|
entity.is_deleted = True
|
aiteamutils/files.py
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
import os
|
2
|
+
import hashlib
|
3
|
+
import aiofiles
|
4
|
+
from datetime import datetime, timezone
|
5
|
+
from typing import BinaryIO, Dict, Any, List, Tuple, Union
|
6
|
+
from pathlib import Path
|
7
|
+
from mimetypes import guess_type
|
8
|
+
from sqlalchemy import text
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
10
|
+
from ulid import ULID
|
11
|
+
|
12
|
+
from .exceptions import ErrorCode, CustomException
|
13
|
+
|
14
|
+
class FileHandler:
|
15
|
+
"""파일 처리를 위한 핵심 기능 제공 클래스"""
|
16
|
+
|
17
|
+
@staticmethod
|
18
|
+
def _create_directory(directory: str) -> None:
|
19
|
+
"""디렉토리가 없는 경우 생성
|
20
|
+
|
21
|
+
Args:
|
22
|
+
directory (str): 생성할 디렉토리 경로
|
23
|
+
"""
|
24
|
+
try:
|
25
|
+
os.makedirs(directory, exist_ok=True)
|
26
|
+
except Exception as e:
|
27
|
+
raise CustomException(
|
28
|
+
ErrorCode.FILE_SYSTEM_ERROR,
|
29
|
+
detail=str(directory),
|
30
|
+
source_function="FileHandler._create_directory",
|
31
|
+
original_error=e
|
32
|
+
)
|
33
|
+
|
34
|
+
@staticmethod
|
35
|
+
async def _calculate_checksum(file: BinaryIO) -> str:
|
36
|
+
"""파일의 SHA-256 체크섬 계산
|
37
|
+
|
38
|
+
Args:
|
39
|
+
file (BinaryIO): 체크섬을 계산할 파일 객체
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
str: 계산된 체크섬 값
|
43
|
+
"""
|
44
|
+
try:
|
45
|
+
sha256_hash = hashlib.sha256()
|
46
|
+
current_position = file.tell()
|
47
|
+
file.seek(0)
|
48
|
+
|
49
|
+
for chunk in iter(lambda: file.read(4096), b""):
|
50
|
+
sha256_hash.update(chunk)
|
51
|
+
|
52
|
+
file.seek(current_position)
|
53
|
+
return sha256_hash.hexdigest()
|
54
|
+
except Exception as e:
|
55
|
+
raise CustomException(
|
56
|
+
ErrorCode.FILE_SYSTEM_ERROR,
|
57
|
+
detail="checksum calculation failed",
|
58
|
+
source_function="FileHandler._calculate_checksum",
|
59
|
+
original_error=e
|
60
|
+
)
|
61
|
+
|
62
|
+
@staticmethod
|
63
|
+
def _get_mime_type(filename: str) -> str:
|
64
|
+
"""파일의 MIME 타입 추측
|
65
|
+
|
66
|
+
Args:
|
67
|
+
filename (str): MIME 타입을 추측할 파일명
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
str: 추측된 MIME 타입
|
71
|
+
"""
|
72
|
+
mime_type, _ = guess_type(filename)
|
73
|
+
return mime_type or "application/octet-stream"
|
74
|
+
|
75
|
+
@staticmethod
|
76
|
+
async def _save_file(
|
77
|
+
file: BinaryIO,
|
78
|
+
original_name: str,
|
79
|
+
storage_dir: str,
|
80
|
+
entity_name: str,
|
81
|
+
entity_ulid: str
|
82
|
+
) -> Dict[str, Any]:
|
83
|
+
"""파일을 저장하고 메타데이터 반환 (내부 사용)
|
84
|
+
|
85
|
+
Args:
|
86
|
+
file (BinaryIO): 저장할 파일 객체
|
87
|
+
original_name (str): 원본 파일명
|
88
|
+
storage_dir (str): 저장 디렉토리 경로
|
89
|
+
entity_name (str): 엔티티 이름
|
90
|
+
entity_ulid (str): 엔티티 ULID
|
91
|
+
|
92
|
+
Returns:
|
93
|
+
Dict[str, Any]: 저장된 파일의 메타데이터
|
94
|
+
"""
|
95
|
+
try:
|
96
|
+
# 저장 디렉토리 생성
|
97
|
+
FileHandler._create_directory(storage_dir)
|
98
|
+
|
99
|
+
# 파일 메타데이터 준비
|
100
|
+
file_ext = os.path.splitext(original_name)[1]
|
101
|
+
storage_filename = f"{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}_{entity_ulid}{file_ext}"
|
102
|
+
storage_path = os.path.join(storage_dir, storage_filename)
|
103
|
+
|
104
|
+
# 파일 저장
|
105
|
+
current_position = file.tell()
|
106
|
+
file.seek(0)
|
107
|
+
|
108
|
+
async with aiofiles.open(storage_path, 'wb') as f:
|
109
|
+
while chunk := file.read(8192):
|
110
|
+
await f.write(chunk)
|
111
|
+
|
112
|
+
# 체크섬 계산
|
113
|
+
file.seek(0)
|
114
|
+
checksum = await FileHandler._calculate_checksum(file)
|
115
|
+
|
116
|
+
# 파일 포인터 복구
|
117
|
+
file.seek(current_position)
|
118
|
+
|
119
|
+
return {
|
120
|
+
"original_name": original_name,
|
121
|
+
"storage_path": storage_path,
|
122
|
+
"mime_type": FileHandler._get_mime_type(original_name),
|
123
|
+
"size": os.path.getsize(storage_path),
|
124
|
+
"checksum": checksum,
|
125
|
+
"entity_name": entity_name,
|
126
|
+
"entity_ulid": entity_ulid
|
127
|
+
}
|
128
|
+
except CustomException as e:
|
129
|
+
raise e
|
130
|
+
except Exception as e:
|
131
|
+
raise CustomException(
|
132
|
+
ErrorCode.FILE_SYSTEM_ERROR,
|
133
|
+
detail=str(storage_path),
|
134
|
+
source_function="FileHandler._save_file",
|
135
|
+
original_error=e
|
136
|
+
)
|
137
|
+
|
138
|
+
@staticmethod
|
139
|
+
async def save_files(
|
140
|
+
files: Union[Tuple[BinaryIO, str], List[Tuple[BinaryIO, str]]],
|
141
|
+
storage_dir: str,
|
142
|
+
entity_name: str,
|
143
|
+
entity_ulid: str,
|
144
|
+
db_session: AsyncSession
|
145
|
+
) -> List[Dict[str, Any]]:
|
146
|
+
"""파일(들)을 저장하고 메타데이터 반환
|
147
|
+
|
148
|
+
Args:
|
149
|
+
files: 단일 파일 튜플 (file, original_name) 또는 파일 튜플 리스트
|
150
|
+
storage_dir (str): 저장 디렉토리 경로
|
151
|
+
entity_name (str): 엔티티 이름
|
152
|
+
entity_ulid (str): 엔티티 ULID
|
153
|
+
db_session (AsyncSession): DB 세션
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
List[Dict[str, Any]]: 저장된 파일들의 메타데이터 리스트
|
157
|
+
"""
|
158
|
+
file_infos = []
|
159
|
+
# 단일 파일인 경우 리스트로 변환
|
160
|
+
files_list = [files] if isinstance(files, tuple) else files
|
161
|
+
|
162
|
+
for file, original_name in files_list:
|
163
|
+
# 파일 저장 및 메타데이터 생성
|
164
|
+
file_info = await FileHandler._save_file(
|
165
|
+
file=file,
|
166
|
+
original_name=original_name,
|
167
|
+
storage_dir=storage_dir,
|
168
|
+
entity_name=entity_name,
|
169
|
+
entity_ulid=entity_ulid
|
170
|
+
)
|
171
|
+
|
172
|
+
# DB에 파일 정보 저장 (트랜잭션은 BaseService에서 관리)
|
173
|
+
result = await db_session.execute(
|
174
|
+
text("""
|
175
|
+
INSERT INTO files (
|
176
|
+
entity_name, entity_ulid, original_name, storage_path,
|
177
|
+
mime_type, size, checksum
|
178
|
+
) VALUES (
|
179
|
+
:entity_name, :entity_ulid, :original_name, :storage_path,
|
180
|
+
:mime_type, :size, :checksum
|
181
|
+
) RETURNING *
|
182
|
+
"""),
|
183
|
+
{
|
184
|
+
"entity_name": entity_name,
|
185
|
+
"entity_ulid": entity_ulid,
|
186
|
+
"original_name": file_info["original_name"],
|
187
|
+
"storage_path": file_info["storage_path"],
|
188
|
+
"mime_type": file_info["mime_type"],
|
189
|
+
"size": file_info["size"],
|
190
|
+
"checksum": file_info["checksum"]
|
191
|
+
}
|
192
|
+
)
|
193
|
+
|
194
|
+
file_info = dict(result.fetchone())
|
195
|
+
file_infos.append(file_info)
|
196
|
+
|
197
|
+
return file_infos
|
198
|
+
|
199
|
+
@staticmethod
|
200
|
+
async def delete_files(storage_paths: Union[str, List[str]]) -> None:
|
201
|
+
"""파일(들) 삭제
|
202
|
+
|
203
|
+
Args:
|
204
|
+
storage_paths: 단일 파일 경로 또는 파일 경로 리스트
|
205
|
+
"""
|
206
|
+
paths_list = [storage_paths] if isinstance(storage_paths, str) else storage_paths
|
207
|
+
for storage_path in paths_list:
|
208
|
+
await FileHandler._delete_file(storage_path)
|
209
|
+
|
210
|
+
@staticmethod
|
211
|
+
async def _delete_file(storage_path: str) -> None:
|
212
|
+
"""파일 삭제 (내부 사용)
|
213
|
+
|
214
|
+
Args:
|
215
|
+
storage_path (str): 삭제할 파일 경로
|
216
|
+
"""
|
217
|
+
try:
|
218
|
+
if os.path.exists(storage_path):
|
219
|
+
os.remove(storage_path)
|
220
|
+
except Exception as e:
|
221
|
+
raise CustomException(
|
222
|
+
ErrorCode.FILE_SYSTEM_ERROR,
|
223
|
+
detail=str(storage_path),
|
224
|
+
source_function="FileHandler._delete_file",
|
225
|
+
original_error=e
|
226
|
+
)
|
227
|
+
|
228
|
+
@staticmethod
|
229
|
+
async def read_file(storage_path: str) -> BinaryIO:
|
230
|
+
"""파일 읽기
|
231
|
+
|
232
|
+
Args:
|
233
|
+
storage_path (str): 읽을 파일 경로
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
BinaryIO: 파일 객체
|
237
|
+
"""
|
238
|
+
try:
|
239
|
+
if not os.path.exists(storage_path):
|
240
|
+
raise CustomException(
|
241
|
+
ErrorCode.FILE_NOT_FOUND,
|
242
|
+
detail=str(storage_path),
|
243
|
+
source_function="FileHandler.read_file"
|
244
|
+
)
|
245
|
+
|
246
|
+
return open(storage_path, 'rb')
|
247
|
+
except CustomException as e:
|
248
|
+
raise e
|
249
|
+
except Exception as e:
|
250
|
+
raise CustomException(
|
251
|
+
ErrorCode.FILE_SYSTEM_ERROR,
|
252
|
+
detail=str(storage_path),
|
253
|
+
source_function="FileHandler.read_file",
|
254
|
+
original_error=e
|
255
|
+
)
|
aiteamutils/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
"""버전 정보"""
|
2
|
-
__version__ = "0.2.
|
2
|
+
__version__ = "0.2.137"
|
@@ -1,15 +1,16 @@
|
|
1
1
|
aiteamutils/__init__.py,sha256=kRBpRjark0M8ZwFfmKiMFol6CbIILN3WE4f6_P6iIq0,1089
|
2
2
|
aiteamutils/base_model.py,sha256=0rs4cjnF2ea3Q2vBTj6F64BGk7ZglJsChsS7ne_R_tg,4056
|
3
|
-
aiteamutils/base_repository.py,sha256=
|
4
|
-
aiteamutils/base_service.py,sha256=
|
3
|
+
aiteamutils/base_repository.py,sha256=Oy2zE1i5qx60Xf1tnsaKLyFWapiPqt5JH8NejwNrPWg,4647
|
4
|
+
aiteamutils/base_service.py,sha256=LrmntqvNx8SxgNrumkkPMv0vaKMMPVJrHzoXmDdx83M,12839
|
5
5
|
aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
|
6
6
|
aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
|
7
|
-
aiteamutils/database.py,sha256=
|
7
|
+
aiteamutils/database.py,sha256=msvBKtxWeQVOo0v2Q9i2azuTNtnUItuNNar52gdRZTo,20418
|
8
8
|
aiteamutils/enums.py,sha256=7WLqlcJqQWtETAga2WAxNp3dJTQIAd2TW-4WzkoHHa8,2498
|
9
9
|
aiteamutils/exceptions.py,sha256=pgf3ersezObyl17wAO3I2fb8m9t2OzWDX1mSjwAWm2Y,16035
|
10
|
+
aiteamutils/files.py,sha256=tdvivl3XLNv7Al7H1gGFczmrHM8XlQpiZsEc2xQ_UTU,8829
|
10
11
|
aiteamutils/security.py,sha256=McUl3t5Z5SyUDVUHymHdDkYyF4YSeg4g9fFMML4W6Kw,11630
|
11
12
|
aiteamutils/validators.py,sha256=msOrha36xWsapm4VAh63YmFq1GVyC9tzZcjXYFCEZ_g,11949
|
12
|
-
aiteamutils/version.py,sha256=
|
13
|
-
aiteamutils-0.2.
|
14
|
-
aiteamutils-0.2.
|
15
|
-
aiteamutils-0.2.
|
13
|
+
aiteamutils/version.py,sha256=paOXlTEx5pKbXz3iDW_A_JPzLhKBZg6PmXu5SqD_82I,43
|
14
|
+
aiteamutils-0.2.137.dist-info/METADATA,sha256=4dfPUZv6C9Xoq8E0HbsY8ToWEx_lsCLpM8qawukEPz0,1719
|
15
|
+
aiteamutils-0.2.137.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
16
|
+
aiteamutils-0.2.137.dist-info/RECORD,,
|
File without changes
|