aiteamutils 0.2.58__tar.gz → 0.2.60__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -21,7 +21,6 @@ from .base_repository import BaseRepository
21
21
  from .validators import validate_with
22
22
  from .enums import ActivityType
23
23
  from .version import __version__
24
- from .dependencies import setup_dependencies, register_service
25
24
 
26
25
  __all__ = [
27
26
  # Base Models
@@ -32,10 +31,6 @@ __all__ = [
32
31
  # Database
33
32
  "DatabaseService",
34
33
 
35
- # Dependencies
36
- "setup_dependencies",
37
- "register_service",
38
-
39
34
  # Exceptions
40
35
  "CustomException",
41
36
  "ErrorCode",
@@ -0,0 +1,54 @@
1
+ #기본 라이브러리
2
+ from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
3
+ from sqlalchemy.orm import DeclarativeBase
4
+ from sqlalchemy.ext.asyncio import AsyncSession
5
+ from sqlalchemy.exc import SQLAlchemyError
6
+ from sqlalchemy import select
7
+
8
+ #패키지 라이브러리
9
+ from .exceptions import ErrorCode, CustomException
10
+ from .database import list_entities
11
+
12
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
13
+
14
+ class BaseRepository(Generic[ModelType]):
15
+ def __init__(self, session: AsyncSession, model: Type[ModelType]):
16
+ if session is None:
17
+ raise CustomException(
18
+ ErrorCode.DB_CONNECTION_ERROR,
19
+ detail="Database session is not set",
20
+ source_function=f"{self.__class__.__name__}.session"
21
+ )
22
+ if model is None:
23
+ raise CustomException(
24
+ ErrorCode.DB_CONNECTION_ERROR,
25
+ detail="Model is not set",
26
+ source_function=f"{self.__class__.__name__}.model"
27
+ )
28
+
29
+ self.session = session
30
+ self.model = model
31
+
32
+ @property
33
+ def session(self) -> AsyncSession:
34
+ return self._session
35
+
36
+ async def list(
37
+ self,
38
+ skip: int = 0,
39
+ limit: int = 100,
40
+ filters: Optional[Dict[str, Any]] = None,
41
+ joins: Optional[List[Any]] = None
42
+ ) -> List[ModelType]:
43
+ """
44
+ 엔티티 목록 조회.
45
+ """
46
+ # 기본 CRUD 작업 호출
47
+ return await list_entities(
48
+ session=self.db_session,
49
+ model=self.model,
50
+ skip=skip,
51
+ limit=limit,
52
+ filters=filters,
53
+ joins=joins,
54
+ )
@@ -0,0 +1,60 @@
1
+ #기본 라이브러리
2
+ from fastapi import Request
3
+ from typing import TypeVar, Generic, Type, Dict, Any, Union, List
4
+ from sqlalchemy.orm import DeclarativeBase
5
+ from sqlalchemy.ext.asyncio import AsyncSession
6
+ from datetime import datetime
7
+ #패키지 라이브러리
8
+ from .exceptions import ErrorCode, CustomException
9
+ from .base_repository import BaseRepository
10
+
11
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
12
+
13
+ class BaseService(Generic[ModelType]):
14
+ ##################
15
+ # 1. 초기화 영역 #
16
+ ##################
17
+ def __init__(
18
+ self,
19
+ model: Type[ModelType],
20
+ repository: BaseRepository[ModelType],
21
+ db_session: AsyncSession
22
+ ):
23
+ self.model = model
24
+ self.repository = repository
25
+ self.db_session = db_session
26
+
27
+ async def list(
28
+ self,
29
+ skip: int = 0,
30
+ limit: int = 100,
31
+ filters: Dict[str, Any] | None = None,
32
+ search_params: Dict[str, Any] | None = None,
33
+ model_name: str | None = None,
34
+ ) -> List[Dict[str, Any]]:
35
+ try:
36
+ # 모델 이름을 통한 동적 처리
37
+ if model_name:
38
+ if model_name not in self.additional_models:
39
+ raise CustomException(
40
+ ErrorCode.INVALID_REQUEST,
41
+ detail=f"Model {model_name} not registered",
42
+ source_function=f"{self.__class__.__name__}.list"
43
+ )
44
+ model = self.additional_models[model_name]
45
+ return await self.repository.list(skip=skip, limit=limit, filters=filters, model=model)
46
+
47
+ return await self.repository.list(skip=skip, limit=limit, filters=filters)
48
+ except CustomException as e:
49
+ e.detail = f"Service list error for {self.repository.model.__tablename__}: {e.detail}"
50
+ e.source_function = f"{self.__class__.__name__}.list -> {e.source_function}"
51
+ raise e
52
+ except Exception as e:
53
+ raise CustomException(
54
+ ErrorCode.INTERNAL_ERROR,
55
+ detail=str(e),
56
+ source_function=f"{self.__class__.__name__}.list",
57
+ original_error=e
58
+ )
59
+
60
+
@@ -0,0 +1,48 @@
1
+ #기본 라이브러리
2
+ from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
3
+ from sqlalchemy.ext.asyncio import AsyncSession
4
+ from sqlalchemy import select, and_
5
+ from sqlalchemy.orm import DeclarativeBase
6
+ from sqlalchemy.exc import SQLAlchemyError
7
+
8
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
9
+
10
+ #패키지 라이브러리
11
+ from .exceptions import ErrorCode, CustomException
12
+
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)
26
+
27
+ # 필터 조건 적용
28
+ if filters:
29
+ conditions = [getattr(model, key) == value for key, value in filters.items()]
30
+ query = query.where(and_(*conditions))
31
+
32
+ # 조인 로딩 적용
33
+ if joins:
34
+ for join_option in joins:
35
+ query = query.options(join_option)
36
+
37
+ # 페이지네이션 적용
38
+ query = query.limit(limit).offset(skip)
39
+
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
+ )
@@ -0,0 +1,2 @@
1
+ """버전 정보"""
2
+ __version__ = "0.2.60"
@@ -1,208 +0,0 @@
1
- """기본 레포지토리 모듈."""
2
- from typing import TypeVar, Generic, Dict, Any, List, Optional, Type, Union
3
- from sqlalchemy.orm import DeclarativeBase, Load
4
- from sqlalchemy.exc import IntegrityError, SQLAlchemyError
5
- from sqlalchemy import select, or_, and_
6
- from .exceptions import CustomException, ErrorCode
7
- from sqlalchemy.orm import joinedload
8
- from sqlalchemy.sql import Select
9
- from fastapi import Request
10
- from sqlalchemy.ext.asyncio import AsyncSession
11
-
12
- ModelType = TypeVar("ModelType", bound=DeclarativeBase)
13
-
14
- class BaseRepository(Generic[ModelType]):
15
- ##################
16
- # 1. 초기화 영역 #
17
- ##################
18
- def __init__(self, session: AsyncSession, model: Type[ModelType]):
19
- """
20
- Args:
21
- session (AsyncSession): 데이터베이스 세션
22
- model (Type[ModelType]): 모델 클래스
23
- """
24
- self.session = session
25
- self.model = model
26
-
27
- @property
28
- def session(self):
29
- """현재 세션을 반환합니다."""
30
- if self._session is None:
31
- raise CustomException(
32
- ErrorCode.DB_CONNECTION_ERROR,
33
- detail="Database session is not set",
34
- source_function=f"{self.__class__.__name__}.session"
35
- )
36
- return self._session
37
-
38
- @session.setter
39
- def session(self, value):
40
- """세션을 설정합니다."""
41
- self._session = value
42
-
43
- #######################
44
- # 2. CRUD 작업 #
45
- #######################
46
- async def get(
47
- self,
48
- ulid: str
49
- ) -> Optional[Dict[str, Any]]:
50
- """ULID로 엔티티를 조회합니다."""
51
- try:
52
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
53
- result = await self.session.execute(stmt)
54
- entity = result.scalars().unique().first()
55
-
56
- if not entity:
57
- raise CustomException(
58
- ErrorCode.DB_NO_RESULT,
59
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
60
- source_function=f"{self.__class__.__name__}.get"
61
- )
62
- return entity
63
- except CustomException as e:
64
- e.detail = f"Repository error for {self.model.__tablename__}: {e.detail}"
65
- e.source_function = f"{self.__class__.__name__}.get -> {e.source_function}"
66
- raise e
67
- except SQLAlchemyError as e:
68
- raise CustomException(
69
- ErrorCode.DB_QUERY_ERROR,
70
- detail=f"Database error in {self.model.__tablename__}: {str(e)}",
71
- source_function=f"{self.__class__.__name__}.get",
72
- original_error=e
73
- )
74
-
75
- async def list(
76
- self,
77
- skip: int = 0,
78
- limit: int = 100,
79
- filters: Dict[str, Any] | None = None,
80
- search_params: Dict[str, Any] | None = None
81
- ) -> List[Any]:
82
- """엔티티 목록을 조회합니다."""
83
- try:
84
- stmt = select(self.model).where(self.model.is_deleted == False)
85
-
86
- # 필터 적용
87
- if filters:
88
- stmt = self._apply_filters(stmt, filters)
89
-
90
- # 검색 적용
91
- if search_params:
92
- stmt = self._apply_search_params(stmt, search_params)
93
-
94
- # 페이지네이션 적용
95
- stmt = stmt.limit(limit).offset(skip)
96
-
97
- result = await self.session.execute(stmt)
98
- return result.scalars().unique().all()
99
-
100
- except SQLAlchemyError as e:
101
- raise CustomException(
102
- ErrorCode.DB_QUERY_ERROR,
103
- detail=f"Unexpected repository list error in {self.model.__tablename__}: {str(e)}",
104
- source_function=f"{self.__class__.__name__}.list",
105
- original_error=e,
106
- )
107
-
108
- async def create(self, data: Dict[str, Any]) -> ModelType:
109
- """새로운 엔티티를 생성합니다."""
110
- try:
111
- entity = self.model(**data)
112
- self.session.add(entity)
113
- await self.session.flush()
114
- await self.session.refresh(entity)
115
- return entity
116
- except IntegrityError as e:
117
- await self.session.rollback()
118
- self._handle_integrity_error(e, "create", data)
119
- except SQLAlchemyError as e:
120
- await self.session.rollback()
121
- raise CustomException(
122
- ErrorCode.DB_CREATE_ERROR,
123
- detail=f"Database create error in {self.model.__tablename__}: {str(e)}",
124
- source_function=f"{self.__class__.__name__}.create",
125
- original_error=e
126
- )
127
-
128
- async def update(self, ulid: str, data: Dict[str, Any]) -> Optional[ModelType]:
129
- """기존 엔티티를 수정합니다."""
130
- try:
131
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
132
- result = await self.session.execute(stmt)
133
- entity = result.scalars().first()
134
-
135
- if not entity:
136
- raise CustomException(
137
- ErrorCode.DB_NO_RESULT,
138
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
139
- source_function=f"{self.__class__.__name__}.update"
140
- )
141
-
142
- for key, value in data.items():
143
- setattr(entity, key, value)
144
-
145
- await self.session.flush()
146
- await self.session.refresh(entity)
147
- return entity
148
-
149
- except IntegrityError as e:
150
- await self.session.rollback()
151
- self._handle_integrity_error(e, "update", data)
152
- except SQLAlchemyError as e:
153
- await self.session.rollback()
154
- raise CustomException(
155
- ErrorCode.DB_UPDATE_ERROR,
156
- detail=f"Database update error in {self.model.__tablename__}: {str(e)}",
157
- source_function=f"{self.__class__.__name__}.update",
158
- original_error=e
159
- )
160
-
161
- async def delete(self, ulid: str) -> bool:
162
- """엔티티를 소프트 삭제합니다."""
163
- try:
164
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
165
- result = await self.session.execute(stmt)
166
- entity = result.scalars().first()
167
-
168
- if not entity:
169
- raise CustomException(
170
- ErrorCode.DB_NO_RESULT,
171
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
172
- source_function=f"{self.__class__.__name__}.delete"
173
- )
174
-
175
- entity.is_deleted = True
176
- await self.session.flush()
177
- return True
178
-
179
- except SQLAlchemyError as e:
180
- await self.session.rollback()
181
- raise CustomException(
182
- ErrorCode.DB_DELETE_ERROR,
183
- detail=f"Database delete error in {self.model.__tablename__}: {str(e)}",
184
- source_function=f"{self.__class__.__name__}.delete",
185
- original_error=e
186
- )
187
-
188
- async def real_delete(self, ulid: str) -> bool:
189
- """엔티티를 실제로 삭제합니다."""
190
- try:
191
- stmt = select(self.model).filter_by(ulid=ulid)
192
- result = await self.session.execute(stmt)
193
- entity = result.scalars().first()
194
-
195
- if entity:
196
- await self.session.delete(entity)
197
- await self.session.flush()
198
- return True
199
- return False
200
-
201
- except SQLAlchemyError as e:
202
- await self.session.rollback()
203
- raise CustomException(
204
- ErrorCode.DB_DELETE_ERROR,
205
- detail=f"Database real delete error in {self.model.__tablename__}: {str(e)}",
206
- source_function=f"{self.__class__.__name__}.real_delete",
207
- original_error=e
208
- )