aiteamutils 0.2.58__py3-none-any.whl → 0.2.60__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/database.py CHANGED
@@ -1,207 +1,48 @@
1
- """데이터베이스 유틸리티 모듈."""
2
- from typing import Any, Dict, Optional, Type, List, Union
3
- from sqlalchemy import select, and_
1
+ #기본 라이브러리
2
+ from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
4
3
  from sqlalchemy.ext.asyncio import AsyncSession
5
- from sqlalchemy.exc import IntegrityError, SQLAlchemyError
4
+ from sqlalchemy import select, and_
5
+ from sqlalchemy.orm import DeclarativeBase
6
+ from sqlalchemy.exc import SQLAlchemyError
6
7
 
7
- from .exceptions import ErrorCode, CustomException
8
- from .base_model import Base
8
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
9
9
 
10
- class DatabaseService:
11
- def __init__(self, session: AsyncSession):
12
- """DatabaseService 초기화
13
-
14
- Args:
15
- session (AsyncSession): 외부에서 주입받은 데이터베이스 세션
16
- """
17
- self._session = session
18
-
19
- @property
20
- def session(self) -> AsyncSession:
21
- """현재 세션을 반환합니다."""
22
- if self._session is None:
23
- raise CustomException(
24
- ErrorCode.DB_CONNECTION_ERROR,
25
- detail="session",
26
- source_function="DatabaseService.session"
27
- )
28
- return self._session
10
+ #패키지 라이브러리
11
+ from .exceptions import ErrorCode, CustomException
29
12
 
30
- async def create_entity(
31
- self,
32
- model: Type[Base],
33
- entity_data: Dict[str, Any]
34
- ) -> Any:
35
- """엔티티를 생성합니다.
36
-
37
- Args:
38
- model: 모델 클래스
39
- entity_data: 생성할 엔티티 데이터
40
-
41
- Returns:
42
- 생성된 엔티티
43
-
44
- Raises:
45
- CustomException: 엔티티 생성 실패 시
46
- """
47
- try:
48
- entity = model(**entity_data)
49
- self.session.add(entity)
50
- await self.session.flush()
51
- await self.session.refresh(entity)
52
- return entity
53
- except IntegrityError as e:
54
- await self.session.rollback()
55
- raise CustomException(
56
- ErrorCode.DB_INTEGRITY_ERROR,
57
- detail=str(e),
58
- source_function="DatabaseService.create_entity",
59
- original_error=e
60
- )
61
- except Exception as e:
62
- await self.session.rollback()
63
- raise CustomException(
64
- ErrorCode.DB_CREATE_ERROR,
65
- detail=str(e),
66
- source_function="DatabaseService.create_entity",
67
- original_error=e
68
- )
13
+ ##################
14
+ # 1. 쿼리 실행 #
15
+ ##################
16
+ async def list_entities(
17
+ session: AsyncSession,
18
+ model: Type[ModelType],
19
+ skip: int = 0,
20
+ limit: int = 100,
21
+ filters: Optional[Dict[str, Any]] = None,
22
+ joins: Optional[List[Any]] = None
23
+ ) -> List[Dict[str, Any]]:
24
+ try:
25
+ query = select(model)
69
26
 
70
- async def get_entity(
71
- self,
72
- model: Type[Base],
73
- filters: Dict[str, Any]
74
- ) -> Optional[Any]:
75
- """필터 조건으로 엔티티를 조회합니다.
76
-
77
- Args:
78
- model: 모델 클래스
79
- filters: 필터 조건
80
-
81
- Returns:
82
- 조회된 엔티티 또는 None
83
-
84
- Raises:
85
- CustomException: 조회 실패 시
86
- """
87
- try:
88
- stmt = select(model).filter_by(**filters)
89
- result = await self.session.execute(stmt)
90
- return result.scalars().first()
91
- except Exception as e:
92
- raise CustomException(
93
- ErrorCode.DB_QUERY_ERROR,
94
- detail=str(e),
95
- source_function="DatabaseService.get_entity",
96
- original_error=e
97
- )
27
+ # 필터 조건 적용
28
+ if filters:
29
+ conditions = [getattr(model, key) == value for key, value in filters.items()]
30
+ query = query.where(and_(*conditions))
98
31
 
99
- async def list_entities(
100
- self,
101
- model: Type[Base],
102
- filters: Optional[Dict[str, Any]] = None,
103
- skip: int = 0,
104
- limit: int = 100
105
- ) -> List[Any]:
106
- """엔티티 목록을 조회합니다.
107
-
108
- Args:
109
- model: 모델 클래스
110
- filters: 필터 조건
111
- skip: 건너뛸 레코드 수
112
- limit: 조회할 최대 레코드 수
113
-
114
- Returns:
115
- 엔티티 목록
116
-
117
- Raises:
118
- CustomException: 조회 실패 시
119
- """
120
- try:
121
- stmt = select(model)
122
- if filters:
123
- stmt = stmt.filter_by(**filters)
124
- stmt = stmt.offset(skip).limit(limit)
125
- result = await self.session.execute(stmt)
126
- return result.scalars().all()
127
- except Exception as e:
128
- raise CustomException(
129
- ErrorCode.DB_QUERY_ERROR,
130
- detail=str(e),
131
- source_function="DatabaseService.list_entities",
132
- original_error=e
133
- )
32
+ # 조인 로딩 적용
33
+ if joins:
34
+ for join_option in joins:
35
+ query = query.options(join_option)
134
36
 
135
- async def update_entity(
136
- self,
137
- entity: Base,
138
- update_data: Dict[str, Any]
139
- ) -> Any:
140
- """엔티티를 수정합니다.
141
-
142
- Args:
143
- entity: 수정할 엔티티
144
- update_data: 수정할 데이터
145
-
146
- Returns:
147
- 수정된 엔티티
148
-
149
- Raises:
150
- CustomException: 수정 실패 시
151
- """
152
- try:
153
- for key, value in update_data.items():
154
- setattr(entity, key, value)
155
- await self.session.flush()
156
- await self.session.refresh(entity)
157
- return entity
158
- except IntegrityError as e:
159
- await self.session.rollback()
160
- raise CustomException(
161
- ErrorCode.DB_INTEGRITY_ERROR,
162
- detail=str(e),
163
- source_function="DatabaseService.update_entity",
164
- original_error=e
165
- )
166
- except Exception as e:
167
- await self.session.rollback()
168
- raise CustomException(
169
- ErrorCode.DB_UPDATE_ERROR,
170
- detail=str(e),
171
- source_function="DatabaseService.update_entity",
172
- original_error=e
173
- )
37
+ # 페이지네이션 적용
38
+ query = query.limit(limit).offset(skip)
174
39
 
175
- async def delete_entity(
176
- self,
177
- entity: Base,
178
- soft_delete: bool = True
179
- ) -> bool:
180
- """엔티티를 삭제합니다.
181
-
182
- Args:
183
- entity: 삭제할 엔티티
184
- soft_delete: 소프트 삭제 여부
185
-
186
- Returns:
187
- 삭제 성공 여부
188
-
189
- Raises:
190
- CustomException: 삭제 실패 시
191
- """
192
- try:
193
- if soft_delete:
194
- entity.is_deleted = True
195
- await self.session.flush()
196
- else:
197
- await self.session.delete(entity)
198
- await self.session.flush()
199
- return True
200
- except Exception as e:
201
- await self.session.rollback()
202
- raise CustomException(
203
- ErrorCode.DB_DELETE_ERROR,
204
- detail=str(e),
205
- source_function="DatabaseService.delete_entity",
206
- original_error=e
207
- )
40
+ result = await session.execute(query)
41
+ return result.scalars().unique().all()
42
+ except SQLAlchemyError as e:
43
+ raise CustomException(
44
+ ErrorCode.DB_READ_ERROR,
45
+ detail=f"{model.__name__}|{str(e)}",
46
+ source_function="database.list_entities",
47
+ original_error=e
48
+ )
aiteamutils/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """버전 정보"""
2
- __version__ = "0.2.58"
2
+ __version__ = "0.2.60"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiteamutils
3
- Version: 0.2.58
3
+ Version: 0.2.60
4
4
  Summary: AI Team Utilities
5
5
  Project-URL: Homepage, https://github.com/yourusername/aiteamutils
6
6
  Project-URL: Issues, https://github.com/yourusername/aiteamutils/issues
@@ -0,0 +1,15 @@
1
+ aiteamutils/__init__.py,sha256=h7iFifWvRlaComsk4VPapRr16YhD19Kp1LuBzLHqdgQ,1170
2
+ aiteamutils/base_model.py,sha256=ODEnjvUVoxQ1RPCfq8-uZTfTADIA4c7Z3E6G4EVsSX0,2708
3
+ aiteamutils/base_repository.py,sha256=ir2ftophKKd_9BQ9B0LbszMqUyFeWkQ1MmFF6WBLcWc,1682
4
+ aiteamutils/base_service.py,sha256=Ncomi5yBUL4oHrMx_J1yUKVrUIYo6YmnYJWAWOIwQx8,2217
5
+ aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
6
+ aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
7
+ aiteamutils/database.py,sha256=MtmrX_pDzKFQM-P3OAfm2mALvhRg-v5JWtGBoiegeU0,1484
8
+ aiteamutils/enums.py,sha256=ipZi6k_QD5-3QV7Yzv7bnL0MjDz-vqfO9I5L77biMKs,632
9
+ aiteamutils/exceptions.py,sha256=_lKWXq_ujNj41xN6LDE149PwsecAP7lgYWbOBbLOntg,15368
10
+ aiteamutils/security.py,sha256=xFVrjttxwXB1TTjqgRQQgQJQohQBT28vuW8FVLjvi-M,10103
11
+ aiteamutils/validators.py,sha256=3N245cZFjgwtW_KzjESkizx5BBUDaJLbbxfNO4WOFZ0,7764
12
+ aiteamutils/version.py,sha256=W0y4-03EvxLX9F9qSLA2iCovEsiyhT86gHqCOTRNhGU,42
13
+ aiteamutils-0.2.60.dist-info/METADATA,sha256=Gu1bs923MYkC_xKFhMKgS8PrSsK2ne2UJRUxBBeflRI,1718
14
+ aiteamutils-0.2.60.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ aiteamutils-0.2.60.dist-info/RECORD,,
@@ -1,227 +0,0 @@
1
- """의존성 관리 모듈."""
2
- from typing import Type, TypeVar, Dict, Any, Optional, Callable, List, AsyncGenerator
3
- from fastapi import Request, Depends, HTTPException, status
4
- from sqlalchemy.ext.asyncio import AsyncSession
5
- from jose import jwt, JWTError
6
- import logging
7
-
8
- from .exceptions import CustomException, ErrorCode
9
- from .config import get_settings
10
- from .base_service import BaseService
11
- from .base_repository import BaseRepository
12
- from .database import DatabaseService
13
-
14
- T = TypeVar("T", bound=BaseService)
15
- R = TypeVar("R", bound=BaseRepository)
16
-
17
- _service_registry: Dict[str, Dict[str, Any]] = {}
18
- _session_provider = None
19
-
20
- __all__ = [
21
- "setup_dependencies",
22
- "register_service",
23
- "get_service",
24
- "get_current_user"
25
- ]
26
-
27
- def setup_dependencies(session_provider: Callable[[], AsyncGenerator[AsyncSession, None]]) -> None:
28
- """의존성 설정을 초기화합니다.
29
-
30
- Args:
31
- session_provider: 데이터베이스 세션을 제공하는 함수
32
- """
33
- global _session_provider
34
- _session_provider = session_provider
35
-
36
- def register_service(
37
- service_class: Type[T],
38
- repository_class: Optional[Type[R]] = None,
39
- **kwargs
40
- ) -> None:
41
- """서비스를 등록합니다.
42
-
43
- Args:
44
- service_class: 서비스 클래스
45
- repository_class: 저장소 클래스 (선택)
46
- **kwargs: 추가 의존성
47
- """
48
- if _session_provider is None:
49
- raise CustomException(
50
- ErrorCode.INTERNAL_ERROR,
51
- detail="Dependencies not initialized",
52
- source_function="dependencies.register_service"
53
- )
54
-
55
- service_name = service_class.__name__
56
- _service_registry[service_name] = {
57
- "service_class": service_class,
58
- "repository_class": repository_class,
59
- "dependencies": kwargs
60
- }
61
-
62
- async def _get_service(
63
- service_name: str,
64
- session: AsyncSession,
65
- request: Request
66
- ) -> BaseService:
67
- """서비스 인스턴스를 생성합니다.
68
-
69
- Args:
70
- service_name: 서비스 이름
71
- session: 데이터베이스 세션
72
- request: FastAPI 요청 객체
73
-
74
- Returns:
75
- BaseService: 서비스 인스턴스
76
-
77
- Raises:
78
- CustomException: 서비스 생성 실패 시
79
- """
80
- try:
81
- service_info = _service_registry.get(service_name)
82
- if not service_info:
83
- raise CustomException(
84
- ErrorCode.SERVICE_NOT_FOUND,
85
- detail=service_name,
86
- source_function="dependencies._get_service"
87
- )
88
-
89
- service_class = service_info["service_class"]
90
- repository_class = service_info["repository_class"]
91
- dependencies = service_info["dependencies"]
92
-
93
- # 데이터베이스 서비스 생성
94
- db_service = DatabaseService(session=session)
95
-
96
- # 저장소 인스턴스 생성
97
- repository = None
98
- if repository_class:
99
- repository = repository_class(session=session)
100
-
101
- # 서비스 인스턴스 생성
102
- service = service_class(
103
- db=db_service,
104
- repository=repository,
105
- request=request,
106
- **dependencies
107
- )
108
-
109
- return service
110
-
111
- except CustomException as e:
112
- raise e
113
- except Exception as e:
114
- raise CustomException(
115
- ErrorCode.INTERNAL_ERROR,
116
- detail=str(e),
117
- source_function="dependencies._get_service",
118
- original_error=e
119
- )
120
-
121
- def get_service(service_name: str) -> Callable:
122
- """서비스 의존성을 반환합니다.
123
-
124
- Args:
125
- service_name: 서비스 이름
126
-
127
- Returns:
128
- Callable: 서비스 의존성 함수
129
- """
130
- if _session_provider is None:
131
- raise CustomException(
132
- ErrorCode.INTERNAL_ERROR,
133
- detail="Dependencies not initialized",
134
- source_function="dependencies.get_service"
135
- )
136
-
137
- async def _get_service_dependency(
138
- request: Request,
139
- session: AsyncSession = Depends(_session_provider)
140
- ) -> BaseService:
141
- return await _get_service(service_name, session, request)
142
- return _get_service_dependency
143
-
144
- async def get_current_user(
145
- request: Request,
146
- session: AsyncSession,
147
- auth_service: BaseService = Depends(get_service("AuthService"))
148
- ) -> Dict[str, Any]:
149
- """현재 사용자 정보를 반환합니다.
150
-
151
- Args:
152
- request: FastAPI 요청 객체
153
- session: 데이터베이스 세션
154
- auth_service: 인증 서비스
155
-
156
- Returns:
157
- Dict[str, Any]: 사용자 정보
158
-
159
- Raises:
160
- HTTPException: 인증 실패 시
161
- """
162
- settings = get_settings()
163
-
164
- # Authorization 헤더 검증
165
- authorization = request.headers.get("Authorization")
166
- if not authorization or not authorization.startswith("Bearer "):
167
- raise HTTPException(
168
- status_code=status.HTTP_401_UNAUTHORIZED,
169
- detail="Not authenticated",
170
- headers={"WWW-Authenticate": "Bearer"}
171
- )
172
-
173
- token = authorization.split(" ")[1]
174
-
175
- try:
176
- # JWT 토큰 디코딩
177
- payload = jwt.decode(
178
- token,
179
- settings.jwt_secret,
180
- algorithms=[settings.jwt_algorithm],
181
- issuer=settings.token_issuer,
182
- audience=settings.token_audience
183
- )
184
-
185
- # 토큰 타입 검증
186
- token_type = payload.get("token_type")
187
- if token_type != "access":
188
- raise HTTPException(
189
- status_code=status.HTTP_401_UNAUTHORIZED,
190
- detail="Invalid token type",
191
- headers={"WWW-Authenticate": "Bearer"}
192
- )
193
-
194
- # 사용자 조회
195
- user_ulid = payload.get("user_ulid")
196
- if not user_ulid:
197
- raise HTTPException(
198
- status_code=status.HTTP_401_UNAUTHORIZED,
199
- detail="Invalid token payload",
200
- headers={"WWW-Authenticate": "Bearer"}
201
- )
202
-
203
- user = await auth_service.get_by_ulid(user_ulid)
204
- if not user:
205
- raise HTTPException(
206
- status_code=status.HTTP_401_UNAUTHORIZED,
207
- detail="User not found",
208
- headers={"WWW-Authenticate": "Bearer"}
209
- )
210
-
211
- return user
212
-
213
- except JWTError as e:
214
- raise HTTPException(
215
- status_code=status.HTTP_401_UNAUTHORIZED,
216
- detail=str(e),
217
- headers={"WWW-Authenticate": "Bearer"}
218
- )
219
- except HTTPException as e:
220
- raise e
221
- except Exception as e:
222
- logging.error(f"Error in get_current_user: {str(e)}")
223
- raise HTTPException(
224
- status_code=status.HTTP_401_UNAUTHORIZED,
225
- detail="Authentication failed",
226
- headers={"WWW-Authenticate": "Bearer"}
227
- )
@@ -1,16 +0,0 @@
1
- aiteamutils/__init__.py,sha256=U9UEyU0AqpLZzbPJiBvwDEh-s2405y1IeWZ0zCqFSl0,1307
2
- aiteamutils/base_model.py,sha256=ODEnjvUVoxQ1RPCfq8-uZTfTADIA4c7Z3E6G4EVsSX0,2708
3
- aiteamutils/base_repository.py,sha256=vqsundoN0h7FVvgqTBEnnJNMcFpvMK0s_nxBWdIYg-U,7846
4
- aiteamutils/base_service.py,sha256=s2AcA-6_ogOQKgt2xf_3AG2s6tqBceU4nJoXO1II7S8,24588
5
- aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
6
- aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
7
- aiteamutils/database.py,sha256=x0x5gnSyGfwo_klL9O65RnGOQID6c9tH2miwFveVyoE,6326
8
- aiteamutils/dependencies.py,sha256=UjHGgee4qKxPwHJnfMZ3YKB7m7UQNIvKNr7IdOx590c,6899
9
- aiteamutils/enums.py,sha256=ipZi6k_QD5-3QV7Yzv7bnL0MjDz-vqfO9I5L77biMKs,632
10
- aiteamutils/exceptions.py,sha256=_lKWXq_ujNj41xN6LDE149PwsecAP7lgYWbOBbLOntg,15368
11
- aiteamutils/security.py,sha256=xFVrjttxwXB1TTjqgRQQgQJQohQBT28vuW8FVLjvi-M,10103
12
- aiteamutils/validators.py,sha256=3N245cZFjgwtW_KzjESkizx5BBUDaJLbbxfNO4WOFZ0,7764
13
- aiteamutils/version.py,sha256=57HkAV8pVrmV03eTVINw5qTQURkXwGPl2H--uJC4OWs,42
14
- aiteamutils-0.2.58.dist-info/METADATA,sha256=yULGE3mtpwvWXzHDczGJfqu52urFzknJhm5V9PgCOGc,1718
15
- aiteamutils-0.2.58.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- aiteamutils-0.2.58.dist-info/RECORD,,