datajunction-query 0.0.1a58__py3-none-any.whl → 0.0.28__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.
djqs/exceptions.py CHANGED
@@ -4,8 +4,6 @@ Errors and warnings.
4
4
 
5
5
  from typing import Any, Dict, List, Literal, Optional, TypedDict
6
6
 
7
- from sqlmodel import SQLModel
8
-
9
7
  from djqs.enum import IntEnum
10
8
 
11
9
 
@@ -53,14 +51,20 @@ class DJErrorType(TypedDict):
53
51
  debug: Optional[DebugType]
54
52
 
55
53
 
56
- class DJError(SQLModel):
54
+ class DJError:
57
55
  """
58
56
  An error.
59
57
  """
60
58
 
61
- code: ErrorCode
62
- message: str
63
- debug: Optional[Dict[str, Any]]
59
+ def __init__(
60
+ self,
61
+ code: ErrorCode,
62
+ message: str,
63
+ debug: Optional[Dict[str, Any]] = None,
64
+ ):
65
+ self.code = code
66
+ self.message = message
67
+ self.debug = debug
64
68
 
65
69
  def __str__(self) -> str:
66
70
  """
@@ -68,6 +72,16 @@ class DJError(SQLModel):
68
72
  """
69
73
  return f"{self.message} (error code: {self.code})"
70
74
 
75
+ def dict(self) -> Dict[str, Any]:
76
+ """
77
+ Convert the error to a dictionary.
78
+ """
79
+ return {
80
+ "code": self.code,
81
+ "message": self.message,
82
+ "debug": self.debug,
83
+ }
84
+
71
85
 
72
86
  class DJWarningType(TypedDict):
73
87
  """
@@ -79,14 +93,30 @@ class DJWarningType(TypedDict):
79
93
  debug: Optional[DebugType]
80
94
 
81
95
 
82
- class DJWarning(SQLModel):
96
+ class DJWarning: # pylint: disable=too-few-public-methods
83
97
  """
84
98
  A warning.
85
99
  """
86
100
 
87
- code: Optional[ErrorCode] = None
88
- message: str
89
- debug: Optional[Dict[str, Any]]
101
+ def __init__(
102
+ self,
103
+ message: str,
104
+ code: Optional[ErrorCode] = None,
105
+ debug: Optional[Dict[str, Any]] = None,
106
+ ):
107
+ self.code = code
108
+ self.message = message
109
+ self.debug = debug
110
+
111
+ def dict(self) -> Dict[str, Any]:
112
+ """
113
+ Convert the warning to a dictionary.
114
+ """
115
+ return {
116
+ "code": self.code,
117
+ "message": self.message,
118
+ "debug": self.debug,
119
+ }
90
120
 
91
121
 
92
122
  DBAPIExceptions = Literal[
@@ -118,16 +148,6 @@ class DJException(Exception):
118
148
  Base class for errors.
119
149
  """
120
150
 
121
- message: str
122
- errors: List[DJError]
123
- warnings: List[DJWarning]
124
-
125
- # exception that should be raised when ``DJException`` is caught by the DB API cursor
126
- dbapi_exception: DBAPIExceptions = "Error"
127
-
128
- # status code that should be returned when ``DJException`` is caught by the API layer
129
- http_status_code: int = 500
130
-
131
151
  def __init__( # pylint: disable=too-many-arguments
132
152
  self,
133
153
  message: Optional[str] = None,
@@ -139,15 +159,12 @@ class DJException(Exception):
139
159
  self.errors = errors or []
140
160
  self.warnings = warnings or []
141
161
  self.message = message or "\n".join(error.message for error in self.errors)
142
-
143
- if dbapi_exception is not None:
144
- self.dbapi_exception = dbapi_exception
145
- if http_status_code is not None:
146
- self.http_status_code = http_status_code
162
+ self.dbapi_exception = dbapi_exception or "Error"
163
+ self.http_status_code = http_status_code or 500
147
164
 
148
165
  super().__init__(self.message)
149
166
 
150
- def to_dict(self) -> DJExceptionType:
167
+ def to_dict(self) -> dict:
151
168
  """
152
169
  Convert to dict.
153
170
  """
@@ -186,8 +203,13 @@ class DJInvalidInputException(DJException):
186
203
  Exception raised when the input provided by the user is invalid.
187
204
  """
188
205
 
189
- dbapi_exception: DBAPIExceptions = "ProgrammingError"
190
- http_status_code: int = 422
206
+ def __init__(self, *args, **kwargs):
207
+ super().__init__(
208
+ *args,
209
+ dbapi_exception="ProgrammingError",
210
+ http_status_code=422,
211
+ **kwargs,
212
+ )
191
213
 
192
214
 
193
215
  class DJNotImplementedException(DJException):
@@ -195,8 +217,13 @@ class DJNotImplementedException(DJException):
195
217
  Exception raised when some functionality hasn't been implemented in DJ yet.
196
218
  """
197
219
 
198
- dbapi_exception: DBAPIExceptions = "NotSupportedError"
199
- http_status_code: int = 500
220
+ def __init__(self, *args, **kwargs):
221
+ super().__init__(
222
+ *args,
223
+ dbapi_exception="NotSupportedError",
224
+ http_status_code=500,
225
+ **kwargs,
226
+ )
200
227
 
201
228
 
202
229
  class DJInternalErrorException(DJException):
@@ -204,8 +231,13 @@ class DJInternalErrorException(DJException):
204
231
  Exception raised when we do something wrong in the code.
205
232
  """
206
233
 
207
- dbapi_exception: DBAPIExceptions = "InternalError"
208
- http_status_code: int = 500
234
+ def __init__(self, *args, **kwargs):
235
+ super().__init__(
236
+ *args,
237
+ dbapi_exception="InternalError",
238
+ http_status_code=500,
239
+ **kwargs,
240
+ )
209
241
 
210
242
 
211
243
  class DJInvalidTableRef(DJException):
@@ -218,3 +250,21 @@ class DJTableNotFound(DJException):
218
250
  """
219
251
  Raised for tables that cannot be found
220
252
  """
253
+
254
+
255
+ class DJDatabaseError(DJException):
256
+ """
257
+ Ran into an issue while submitting a query to the backend DB
258
+ """
259
+
260
+
261
+ class DJUnknownCatalog(DJException):
262
+ """
263
+ Raised when a catalog cannot be found
264
+ """
265
+
266
+
267
+ class DJUnknownEngine(DJException):
268
+ """
269
+ Raised when an engine or engine version cannot be found
270
+ """
djqs/models/query.py CHANGED
@@ -3,66 +3,60 @@ Models for queries.
3
3
  """
4
4
 
5
5
  import uuid
6
+ from dataclasses import dataclass, field
6
7
  from datetime import datetime
7
8
  from typing import Any, List, Optional
8
9
  from uuid import UUID, uuid4
9
10
 
10
11
  import msgpack
11
- from pydantic import AnyHttpUrl
12
- from sqlalchemy.sql.schema import Column as SqlaColumn
13
- from sqlalchemy_utils import UUIDType
14
- from sqlmodel import Field, SQLModel
15
12
 
16
13
  from djqs.enum import IntEnum
17
14
  from djqs.typing import QueryState, Row
18
15
 
19
16
 
20
- class BaseQuery(SQLModel):
17
+ @dataclass
18
+ class BaseQuery:
21
19
  """
22
20
  Base class for query models.
23
21
  """
24
22
 
25
- catalog_name: Optional[str]
23
+ catalog_name: Optional[str] = None
26
24
  engine_name: Optional[str] = None
27
25
  engine_version: Optional[str] = None
28
26
 
29
- class Config: # pylint: disable=too-few-public-methods, missing-class-docstring
30
- allow_population_by_field_name = True
31
27
 
32
-
33
- class Query(BaseQuery, table=True): # type: ignore
28
+ @dataclass
29
+ class Query(BaseQuery): # pylint: disable=too-many-instance-attributes
34
30
  """
35
31
  A query.
36
32
  """
37
33
 
38
- id: UUID = Field(
39
- default_factory=uuid4,
40
- sa_column=SqlaColumn(UUIDType(), primary_key=True),
41
- )
42
- submitted_query: str
43
- catalog_name: str
44
- engine_name: str
45
- engine_version: str
46
- async_: bool
34
+ id: UUID = field(default_factory=uuid4) # pylint: disable=invalid-name
35
+ submitted_query: str = ""
36
+ catalog_name: str = ""
37
+ engine_name: str = ""
38
+ engine_version: str = ""
39
+ async_: bool = False
47
40
  executed_query: Optional[str] = None
48
41
  scheduled: Optional[datetime] = None
49
42
  started: Optional[datetime] = None
50
43
  finished: Optional[datetime] = None
51
-
52
44
  state: QueryState = QueryState.UNKNOWN
53
45
  progress: float = 0.0
54
46
 
55
47
 
48
+ @dataclass
56
49
  class QueryCreate(BaseQuery):
57
50
  """
58
51
  Model for submitted queries.
59
52
  """
60
53
 
61
- submitted_query: str
54
+ submitted_query: str = ""
62
55
  async_: bool = False
63
56
 
64
57
 
65
- class ColumnMetadata(SQLModel):
58
+ @dataclass
59
+ class ColumnMetadata:
66
60
  """
67
61
  A simple model for column metadata.
68
62
  """
@@ -71,7 +65,8 @@ class ColumnMetadata(SQLModel):
71
65
  type: str
72
66
 
73
67
 
74
- class StatementResults(SQLModel):
68
+ @dataclass
69
+ class StatementResults:
75
70
  """
76
71
  Results for a given statement.
77
72
 
@@ -79,43 +74,32 @@ class StatementResults(SQLModel):
79
74
  """
80
75
 
81
76
  sql: str
82
- columns: List[ColumnMetadata]
83
- rows: List[Row]
84
-
85
- # this indicates the total number of rows, and is useful for paginated requests
86
- row_count: int = 0
77
+ columns: List[ColumnMetadata] = field(default_factory=list)
78
+ rows: List[Row] = field(default_factory=list)
79
+ row_count: int = 0 # used for pagination
87
80
 
88
81
 
89
- class Results(SQLModel):
90
- """
91
- Results for a given query.
92
- """
93
-
94
- __root__: List[StatementResults]
95
-
96
-
97
- class QueryResults(BaseQuery):
82
+ @dataclass
83
+ class QueryResults(BaseQuery): # pylint: disable=too-many-instance-attributes
98
84
  """
99
85
  Model for query with results.
100
86
  """
101
87
 
102
- id: uuid.UUID
88
+ id: uuid.UUID = field(default_factory=uuid4) # pylint: disable=invalid-name
103
89
  engine_name: Optional[str] = None
104
90
  engine_version: Optional[str] = None
105
- submitted_query: str
91
+ submitted_query: str = ""
106
92
  executed_query: Optional[str] = None
107
-
108
93
  scheduled: Optional[datetime] = None
109
94
  started: Optional[datetime] = None
110
95
  finished: Optional[datetime] = None
111
-
112
96
  state: QueryState = QueryState.UNKNOWN
97
+ async_: bool = False
113
98
  progress: float = 0.0
114
-
115
- results: Results
116
- next: Optional[AnyHttpUrl] = None
117
- previous: Optional[AnyHttpUrl] = None
118
- errors: List[str]
99
+ results: List[StatementResults] = field(default_factory=list)
100
+ next: Optional[str] = None # Changed to str, as AnyHttpUrl was from pydantic
101
+ previous: Optional[str] = None # Changed to str, as AnyHttpUrl was from pydantic
102
+ errors: List[str] = field(default_factory=list)
119
103
 
120
104
 
121
105
  class QueryExtType(IntEnum):
djqs/models/table.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Models for use in table API requests and responses
3
3
  """
4
+
4
5
  from typing import Dict, List
5
6
 
6
7
  from pydantic import BaseModel
djqs/utils.py CHANGED
@@ -3,17 +3,12 @@ Utility functions.
3
3
  """
4
4
  # pylint: disable=line-too-long
5
5
 
6
- import datetime
7
6
  import logging
8
7
  import os
9
8
  from functools import lru_cache
10
- from typing import Iterator
11
9
 
12
10
  from dotenv import load_dotenv
13
- from pydantic.datetime_parse import parse_datetime
14
11
  from rich.logging import RichHandler
15
- from sqlalchemy.engine import Engine
16
- from sqlmodel import Session, create_engine
17
12
 
18
13
  from djqs.config import Settings
19
14
 
@@ -36,7 +31,7 @@ def setup_logging(loglevel: str) -> None:
36
31
  )
37
32
 
38
33
 
39
- @lru_cache
34
+ @lru_cache(1)
40
35
  def get_settings() -> Settings:
41
36
  """
42
37
  Return a cached settings object.
@@ -44,47 +39,3 @@ def get_settings() -> Settings:
44
39
  dotenv_file = os.environ.get("DOTENV_FILE", ".env")
45
40
  load_dotenv(dotenv_file)
46
41
  return Settings()
47
-
48
-
49
- def get_metadata_engine() -> Engine:
50
- """
51
- Create the metadata engine.
52
- """
53
- settings = get_settings()
54
- engine = create_engine(settings.index)
55
-
56
- return engine
57
-
58
-
59
- def get_session() -> Iterator[Session]:
60
- """
61
- Per-request session.
62
- """
63
- engine = get_metadata_engine()
64
-
65
- with Session(engine, autoflush=False) as session: # pragma: no cover
66
- yield session
67
-
68
-
69
- class UTCDatetime(datetime.datetime): # pragma: no cover
70
- """
71
- A UTC extension of pydantic's normal datetime handling
72
- """
73
-
74
- @classmethod
75
- def __get_validators__(cls):
76
- """
77
- Extend the builtin pydantic datetime parser with a custom validate method
78
- """
79
- yield parse_datetime
80
- yield cls.validate
81
-
82
- @classmethod
83
- def validate(cls, value) -> str:
84
- """
85
- Convert to UTC
86
- """
87
- if value.tzinfo is None:
88
- return value.replace(tzinfo=datetime.timezone.utc)
89
-
90
- return value.astimezone(datetime.timezone.utc)
@@ -1,27 +0,0 @@
1
- djqs/__about__.py,sha256=8V1Y3GlOIENmZdxogzyStiLOAhaYsAxXEbZpddJSW2s,51
2
- djqs/__init__.py,sha256=nN5-uJoSVEwuc8n-wMygqeF0Xhxi_zqqbCgutZvAt3E,384
3
- djqs/config.py,sha256=Flva9W8McnTunUlHGhYd0CsgbsjWbZIoPW25ZTzFxws,3029
4
- djqs/constants.py,sha256=T-sO5C5-NNdEywGglTIu6bjT1VooFwW4XWNA_ZwSYM8,412
5
- djqs/engine.py,sha256=5HvrFnQmy89j2duPNT-zSktpfl7zKj-FZdeJ5SvFf8M,5719
6
- djqs/enum.py,sha256=GJVLYDJ2zWjjUBENgyjZZ_94A24BJtvci-AKTmA0zek,590
7
- djqs/exceptions.py,sha256=z-EjGLRwHL_v3zl1-RICwQis7wUwl3000tse0YwuoBE,4992
8
- djqs/fixes.py,sha256=TcXnh0I1z4vEAupPatzrvnqyToGihndnxmLJtIn-_Z8,33
9
- djqs/typing.py,sha256=TpZHhrK_lzEYg_ZlT5qVCJz8seQBKwrULiTPO-lMxEU,6220
10
- djqs/utils.py,sha256=R-t2A8RvwLu2Lxcs8r5kxQ7AfuDRKJ7G9XcjfdXAhvQ,2075
11
- djqs/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- djqs/api/catalogs.py,sha256=I_i8-Nok0V3ND1FpOCQ5Nmgaof5MLH8i0oeTjvDxQgw,3062
13
- djqs/api/engines.py,sha256=278fsgIgQXe4WAmbfbxoA08ALe57us_zKlaK_hbiDrc,1611
14
- djqs/api/helpers.py,sha256=91UGqs-SAq3fCU9yElBMev9vqtDospOjBq0icfFl1qs,2468
15
- djqs/api/main.py,sha256=cbZHxkfmushgw8e0G2q8f5Ytexqk-qjKtZPyHK5sbjQ,2075
16
- djqs/api/queries.py,sha256=gSdzI7pVhCsZDpjP5l0NTvHq4ykF20CFzKiLZrrlUmQ,5523
17
- djqs/api/tables.py,sha256=1hwYPRAG9kZfS8HE3r_omC4AjHadAbdhFIwx3e4KOvo,1582
18
- djqs/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- djqs/models/catalog.py,sha256=o3Wlghwr6ax4Eo-3zE5Mdq478xyUxeaf2o3whFprXtU,2038
20
- djqs/models/engine.py,sha256=k9cBvnYAfzz4yaBDvErDqXIwlqrRsKRbGikBKTQHJPA,889
21
- djqs/models/query.py,sha256=Qsy69Cfrkn5myOZZfLrR83JB4ynPXogitUo8A7hRsz8,3338
22
- djqs/models/table.py,sha256=7HmiXWHBWEthqdwPYLlsOgeKD-w9urCHoC4xaQ1RnM8,238
23
- datajunction_query-0.0.1a58.dist-info/METADATA,sha256=ehByOgLgh-X4iKB7tEfAeTVWddeCTp46KzwnwW9GOQY,9495
24
- datajunction_query-0.0.1a58.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
25
- datajunction_query-0.0.1a58.dist-info/licenses/AUTHORS.rst,sha256=G9YmXPfQ0kAuxqlCwvWVvUnZitP9hAc-rPfZ5q7Pv1U,235
26
- datajunction_query-0.0.1a58.dist-info/licenses/LICENSE.txt,sha256=KuSxhVgPuUGoYWphJig4POcTAIUNLUj8vOx-cqQFMj8,1081
27
- datajunction_query-0.0.1a58.dist-info/RECORD,,
djqs/api/catalogs.py DELETED
@@ -1,115 +0,0 @@
1
- """
2
- Catalog related APIs.
3
- """
4
-
5
- import logging
6
- from http import HTTPStatus
7
- from typing import List
8
-
9
- from fastapi import APIRouter, Depends, HTTPException
10
- from sqlmodel import Session, select
11
-
12
- from djqs.api.engines import EngineInfo
13
- from djqs.api.helpers import get_catalog, get_engine
14
- from djqs.exceptions import DJException
15
- from djqs.models.catalog import Catalog, CatalogInfo
16
- from djqs.models.engine import BaseEngineInfo
17
- from djqs.utils import get_session
18
-
19
- _logger = logging.getLogger(__name__)
20
- get_router = APIRouter(tags=["Catalogs & Engines"])
21
- post_router = APIRouter(tags=["Catalogs & Engines - Dynamic Configuration"])
22
-
23
-
24
- @get_router.get("/catalogs/", response_model=List[CatalogInfo])
25
- def list_catalogs(*, session: Session = Depends(get_session)) -> List[CatalogInfo]:
26
- """
27
- List all available catalogs
28
- """
29
- return list(session.exec(select(Catalog)))
30
-
31
-
32
- @get_router.get("/catalogs/{name}/", response_model=CatalogInfo)
33
- def read_catalog(name: str, *, session: Session = Depends(get_session)) -> CatalogInfo:
34
- """
35
- Return a catalog by name
36
- """
37
- return get_catalog(session, name)
38
-
39
-
40
- @post_router.post("/catalogs/", response_model=CatalogInfo, status_code=201)
41
- def add_catalog(
42
- data: CatalogInfo,
43
- *,
44
- session: Session = Depends(get_session),
45
- ) -> CatalogInfo:
46
- """
47
- Add a Catalog
48
- """
49
- try:
50
- get_catalog(session, data.name)
51
- except DJException:
52
- pass
53
- else:
54
- raise HTTPException(
55
- status_code=HTTPStatus.CONFLICT,
56
- detail=f"Catalog already exists: `{data.name}`",
57
- )
58
-
59
- catalog = Catalog.from_orm(data)
60
- catalog.engines.extend(
61
- list_new_engines(
62
- session=session,
63
- catalog=catalog,
64
- create_engines=data.engines,
65
- ),
66
- )
67
- session.add(catalog)
68
- session.commit()
69
- session.refresh(catalog)
70
-
71
- return catalog
72
-
73
-
74
- @post_router.post(
75
- "/catalogs/{name}/engines/",
76
- response_model=CatalogInfo,
77
- status_code=201,
78
- )
79
- def add_engines_to_catalog(
80
- name: str,
81
- data: List[BaseEngineInfo],
82
- *,
83
- session: Session = Depends(get_session),
84
- ) -> CatalogInfo:
85
- """
86
- Attach one or more engines to a catalog
87
- """
88
- catalog = get_catalog(session, name)
89
- catalog.engines.extend(
90
- list_new_engines(session=session, catalog=catalog, create_engines=data),
91
- )
92
- session.add(catalog)
93
- session.commit()
94
- session.refresh(catalog)
95
- return catalog
96
-
97
-
98
- def list_new_engines(
99
- session: Session,
100
- catalog: Catalog,
101
- create_engines: List[EngineInfo],
102
- ) -> List[EngineInfo]:
103
- """
104
- Filter to engines that are not already set on a catalog
105
- """
106
- new_engines = []
107
- for engine_ref in create_engines:
108
- already_set = False
109
- engine = get_engine(session, engine_ref.name, engine_ref.version)
110
- for set_engine in catalog.engines:
111
- if engine.name == set_engine.name and engine.version == set_engine.version:
112
- already_set = True
113
- if not already_set:
114
- new_engines.append(engine)
115
- return new_engines
djqs/api/engines.py DELETED
@@ -1,61 +0,0 @@
1
- """
2
- Engine related APIs.
3
- """
4
-
5
- from http import HTTPStatus
6
- from typing import List
7
-
8
- from fastapi import APIRouter, Depends, HTTPException
9
- from sqlmodel import Session, select
10
-
11
- from djqs.api.helpers import get_engine
12
- from djqs.models.engine import BaseEngineInfo, Engine, EngineInfo
13
- from djqs.utils import get_session
14
-
15
- get_router = APIRouter(tags=["Catalogs & Engines"])
16
- post_router = APIRouter(tags=["Catalogs & Engines - Dynamic Configuration"])
17
-
18
-
19
- @get_router.get("/engines/", response_model=List[EngineInfo])
20
- def list_engines(*, session: Session = Depends(get_session)) -> List[EngineInfo]:
21
- """
22
- List all available engines
23
- """
24
- return list(session.exec(select(Engine)))
25
-
26
-
27
- @get_router.get("/engines/{name}/{version}/", response_model=BaseEngineInfo)
28
- def list_engine(
29
- name: str, version: str, *, session: Session = Depends(get_session)
30
- ) -> BaseEngineInfo:
31
- """
32
- Return an engine by name and version
33
- """
34
- return get_engine(session, name, version)
35
-
36
-
37
- @post_router.post("/engines/", response_model=BaseEngineInfo, status_code=201)
38
- def add_engine(
39
- data: EngineInfo,
40
- *,
41
- session: Session = Depends(get_session),
42
- ) -> BaseEngineInfo:
43
- """
44
- Add an Engine
45
- """
46
- try:
47
- get_engine(session, data.name, data.version)
48
- except HTTPException:
49
- pass
50
- else:
51
- raise HTTPException(
52
- status_code=HTTPStatus.CONFLICT,
53
- detail=f"Engine already exists: `{data.name}` version `{data.version}`",
54
- )
55
-
56
- engine = Engine.from_orm(data)
57
- session.add(engine)
58
- session.commit()
59
- session.refresh(engine)
60
-
61
- return engine
djqs/models/catalog.py DELETED
@@ -1,75 +0,0 @@
1
- """
2
- Models for columns.
3
- """
4
- from datetime import datetime, timezone
5
- from functools import partial
6
- from typing import TYPE_CHECKING, Dict, List, Optional
7
- from uuid import UUID, uuid4
8
-
9
- from sqlalchemy import DateTime
10
- from sqlalchemy.sql.schema import Column as SqlaColumn
11
- from sqlalchemy_utils import UUIDType
12
- from sqlmodel import JSON, Field, Relationship, SQLModel
13
-
14
- from djqs.models.engine import BaseEngineInfo, Engine
15
-
16
- if TYPE_CHECKING:
17
- from djqs.utils import UTCDatetime
18
-
19
-
20
- class CatalogEngines(SQLModel, table=True): # type: ignore
21
- """
22
- Join table for catalogs and engines.
23
- """
24
-
25
- catalog_id: Optional[int] = Field(
26
- default=None,
27
- foreign_key="catalog.id",
28
- primary_key=True,
29
- )
30
- engine_id: Optional[int] = Field(
31
- default=None,
32
- foreign_key="engine.id",
33
- primary_key=True,
34
- )
35
-
36
-
37
- class Catalog(SQLModel, table=True): # type: ignore
38
- """
39
- A catalog.
40
- """
41
-
42
- id: Optional[int] = Field(default=None, primary_key=True)
43
- uuid: UUID = Field(default_factory=uuid4, sa_column=SqlaColumn(UUIDType()))
44
- name: str
45
- engines: List[Engine] = Relationship(
46
- link_model=CatalogEngines,
47
- sa_relationship_kwargs={
48
- "primaryjoin": "Catalog.id==CatalogEngines.catalog_id",
49
- "secondaryjoin": "Engine.id==CatalogEngines.engine_id",
50
- },
51
- )
52
- created_at: "UTCDatetime" = Field(
53
- sa_column=SqlaColumn(DateTime(timezone=True)),
54
- default_factory=partial(datetime.now, timezone.utc),
55
- )
56
- updated_at: "UTCDatetime" = Field(
57
- sa_column=SqlaColumn(DateTime(timezone=True)),
58
- default_factory=partial(datetime.now, timezone.utc),
59
- )
60
- extra_params: Dict = Field(default={}, sa_column=SqlaColumn(JSON))
61
-
62
- def __str__(self) -> str:
63
- return self.name # pragma: no cover
64
-
65
- def __hash__(self) -> int:
66
- return hash(self.id) # pragma: no cover
67
-
68
-
69
- class CatalogInfo(SQLModel):
70
- """
71
- Class for catalog creation
72
- """
73
-
74
- name: str
75
- engines: List[BaseEngineInfo] = []