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.
- {datajunction_query-0.0.1a58.dist-info → datajunction_query-0.0.28.dist-info}/METADATA +8 -7
- datajunction_query-0.0.28.dist-info/RECORD +24 -0
- {datajunction_query-0.0.1a58.dist-info → datajunction_query-0.0.28.dist-info}/WHEEL +1 -1
- djqs/__about__.py +2 -1
- djqs/api/helpers.py +3 -38
- djqs/api/main.py +24 -14
- djqs/api/queries.py +112 -50
- djqs/api/tables.py +19 -22
- djqs/config.py +168 -65
- djqs/constants.py +3 -0
- djqs/db/postgres.py +139 -0
- djqs/engine.py +104 -40
- djqs/enum.py +1 -0
- djqs/exceptions.py +82 -32
- djqs/models/query.py +30 -46
- djqs/models/table.py +1 -0
- djqs/utils.py +1 -50
- datajunction_query-0.0.1a58.dist-info/RECORD +0 -27
- djqs/api/catalogs.py +0 -115
- djqs/api/engines.py +0 -61
- djqs/models/catalog.py +0 -75
- djqs/models/engine.py +0 -50
- {datajunction_query-0.0.1a58.dist-info → datajunction_query-0.0.28.dist-info}/licenses/AUTHORS.rst +0 -0
- {datajunction_query-0.0.1a58.dist-info → datajunction_query-0.0.28.dist-info}/licenses/LICENSE.txt +0 -0
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
|
|
54
|
+
class DJError:
|
|
57
55
|
"""
|
|
58
56
|
An error.
|
|
59
57
|
"""
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
96
|
+
class DJWarning: # pylint: disable=too-few-public-methods
|
|
83
97
|
"""
|
|
84
98
|
A warning.
|
|
85
99
|
"""
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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) ->
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
199
|
-
|
|
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
|
-
|
|
208
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
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] = []
|