tigrbl_engine_xlsx 0.1.1.dev1__tar.gz → 0.1.1.dev3__tar.gz
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.
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/PKG-INFO +1 -1
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/pyproject.toml +1 -1
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/src/tigrbl_engine_xlsx/session.py +84 -7
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/tests/test_tigrblapp_rpc_usage.py +1 -1
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/.gitignore +0 -0
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/LICENSE +0 -0
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/README.md +0 -0
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/distout/.gitignore +0 -0
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/src/tigrbl_engine_xlsx/__init__.py +0 -0
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/src/tigrbl_engine_xlsx/engine.py +0 -0
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/tests/test_smoke.py +0 -0
- {tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/tests/test_xlsx_workbook_io.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tigrbl_engine_xlsx
|
|
3
|
-
Version: 0.1.1.
|
|
3
|
+
Version: 0.1.1.dev3
|
|
4
4
|
Summary: XLSX engine plugin for tigrbl where each workbook is a database and each sheet is a table.
|
|
5
5
|
Project-URL: Homepage, https://github.com/swarmauri/swarmauri-sdk
|
|
6
6
|
Author-email: Jacob Stewart <jacob@swarmauri.com>
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tigrbl_engine_xlsx"
|
|
7
|
-
version = "0.1.1.
|
|
7
|
+
version = "0.1.1.dev3"
|
|
8
8
|
description = "XLSX engine plugin for tigrbl where each workbook is a database and each sheet is a table."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
{tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/src/tigrbl_engine_xlsx/session.py
RENAMED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import tempfile
|
|
5
|
+
import uuid
|
|
5
6
|
|
|
6
7
|
from typing import (
|
|
7
8
|
TYPE_CHECKING,
|
|
@@ -122,6 +123,7 @@ class XlsxSession(TigrblSessionBase):
|
|
|
122
123
|
self._snap_ver: Dict[str, int] = {}
|
|
123
124
|
self._puts: Dict[Tuple[type, Any], Dict[str, Any]] = {}
|
|
124
125
|
self._dels: set[Tuple[type, Any]] = set()
|
|
126
|
+
self._tracked: Dict[Tuple[type, Any], Any] = {}
|
|
125
127
|
|
|
126
128
|
def workbook(self):
|
|
127
129
|
return self._cat.workbook
|
|
@@ -130,7 +132,19 @@ class XlsxSession(TigrblSessionBase):
|
|
|
130
132
|
return self._cat.workbook[name]
|
|
131
133
|
|
|
132
134
|
def table(self, name: str) -> list[dict[str, Any]]:
|
|
133
|
-
|
|
135
|
+
rows = [dict(row) for row in self._cat.get_live(name)]
|
|
136
|
+
pk = self._pk_of(name)
|
|
137
|
+
by_pk = {row.get(pk): row for row in rows}
|
|
138
|
+
|
|
139
|
+
for (model, ident), row in self._puts.items():
|
|
140
|
+
if self._table(model) == name:
|
|
141
|
+
by_pk[ident] = dict(row)
|
|
142
|
+
|
|
143
|
+
for model, ident in self._dels:
|
|
144
|
+
if self._table(model) == name:
|
|
145
|
+
by_pk.pop(ident, None)
|
|
146
|
+
|
|
147
|
+
return list(by_pk.values())
|
|
134
148
|
|
|
135
149
|
async def run_sync(self, fn: Callable[[Any], Any]) -> Any:
|
|
136
150
|
out = fn(self)
|
|
@@ -141,6 +155,7 @@ class XlsxSession(TigrblSessionBase):
|
|
|
141
155
|
self._snap_ver.clear()
|
|
142
156
|
self._puts.clear()
|
|
143
157
|
self._dels.clear()
|
|
158
|
+
self._tracked.clear()
|
|
144
159
|
|
|
145
160
|
async def _tx_commit_impl(self) -> None:
|
|
146
161
|
iso = (self._spec.isolation if self._spec else None) or "read_committed"
|
|
@@ -179,22 +194,51 @@ class XlsxSession(TigrblSessionBase):
|
|
|
179
194
|
|
|
180
195
|
self._puts.clear()
|
|
181
196
|
self._dels.clear()
|
|
197
|
+
self._tracked.clear()
|
|
182
198
|
|
|
183
199
|
async def _tx_rollback_impl(self) -> None:
|
|
184
200
|
self._snap.clear()
|
|
185
201
|
self._snap_ver.clear()
|
|
186
202
|
self._puts.clear()
|
|
187
203
|
self._dels.clear()
|
|
204
|
+
self._tracked.clear()
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def _pk_default(model: type, pk: str) -> Any:
|
|
208
|
+
table = getattr(model, "__table__", None)
|
|
209
|
+
if table is None:
|
|
210
|
+
return None
|
|
211
|
+
try:
|
|
212
|
+
column = table.columns.get(pk)
|
|
213
|
+
except Exception:
|
|
214
|
+
return None
|
|
215
|
+
if column is None:
|
|
216
|
+
return None
|
|
217
|
+
default = getattr(column, "default", None)
|
|
218
|
+
if default is None:
|
|
219
|
+
return None
|
|
220
|
+
arg = getattr(default, "arg", None)
|
|
221
|
+
if callable(arg):
|
|
222
|
+
try:
|
|
223
|
+
return arg()
|
|
224
|
+
except TypeError:
|
|
225
|
+
return arg(None)
|
|
226
|
+
return arg
|
|
188
227
|
|
|
189
228
|
def _add_impl(self, obj: Any) -> Any:
|
|
190
229
|
model = obj.__class__
|
|
191
230
|
pk = _single_pk_name(model)
|
|
192
231
|
ident = getattr(obj, pk)
|
|
232
|
+
if ident is None:
|
|
233
|
+
ident = self._pk_default(model, pk)
|
|
234
|
+
if ident is not None:
|
|
235
|
+
setattr(obj, pk, ident)
|
|
193
236
|
if ident is None:
|
|
194
237
|
raise ValueError(f"primary key {pk!r} must be set")
|
|
195
238
|
row = {column: getattr(obj, column, None) for column in _model_columns(model)}
|
|
196
239
|
self._puts[(model, ident)] = row
|
|
197
240
|
self._dels.discard((model, ident))
|
|
241
|
+
self._tracked[(model, ident)] = obj
|
|
198
242
|
return None
|
|
199
243
|
|
|
200
244
|
async def _delete_impl(self, obj: Any) -> None:
|
|
@@ -203,8 +247,16 @@ class XlsxSession(TigrblSessionBase):
|
|
|
203
247
|
ident = getattr(obj, pk)
|
|
204
248
|
self._puts.pop((model, ident), None)
|
|
205
249
|
self._dels.add((model, ident))
|
|
250
|
+
self._tracked.pop((model, ident), None)
|
|
206
251
|
|
|
207
252
|
async def _flush_impl(self) -> None:
|
|
253
|
+
for (model, ident), obj in list(self._tracked.items()):
|
|
254
|
+
if (model, ident) in self._dels:
|
|
255
|
+
continue
|
|
256
|
+
row = {
|
|
257
|
+
column: getattr(obj, column, None) for column in _model_columns(model)
|
|
258
|
+
}
|
|
259
|
+
self._puts[(model, ident)] = row
|
|
208
260
|
return
|
|
209
261
|
|
|
210
262
|
async def _refresh_impl(self, obj: Any) -> None:
|
|
@@ -217,16 +269,19 @@ class XlsxSession(TigrblSessionBase):
|
|
|
217
269
|
setattr(obj, column, getattr(fresh, column, None))
|
|
218
270
|
|
|
219
271
|
async def _get_impl(self, model: type, ident: Any) -> Any | None:
|
|
272
|
+
tracked = self._tracked.get((model, ident))
|
|
273
|
+
if tracked is not None:
|
|
274
|
+
return tracked
|
|
220
275
|
row = self._puts.get((model, ident))
|
|
221
276
|
if row is not None:
|
|
222
|
-
return self.
|
|
277
|
+
return self._inflate_tracked(model, ident, row)
|
|
223
278
|
if (model, ident) in self._dels:
|
|
224
279
|
return None
|
|
225
280
|
table_rows = self._rows_for(model)
|
|
226
281
|
pk = _single_pk_name(model)
|
|
227
282
|
for table_row in table_rows:
|
|
228
283
|
if table_row.get(pk) == ident:
|
|
229
|
-
return self.
|
|
284
|
+
return self._inflate_tracked(model, ident, table_row)
|
|
230
285
|
return None
|
|
231
286
|
|
|
232
287
|
async def _execute_impl(self, stmt: Any) -> Any:
|
|
@@ -245,6 +300,7 @@ class XlsxSession(TigrblSessionBase):
|
|
|
245
300
|
ident = getattr(obj, pk)
|
|
246
301
|
self._puts.pop((model, ident), None)
|
|
247
302
|
self._dels.add((model, ident))
|
|
303
|
+
self._tracked.pop((model, ident), None)
|
|
248
304
|
result = _ExecuteResult([])
|
|
249
305
|
result.rowcount = len(items)
|
|
250
306
|
return result
|
|
@@ -280,13 +336,22 @@ class XlsxSession(TigrblSessionBase):
|
|
|
280
336
|
setattr(obj, column, data[column])
|
|
281
337
|
return obj
|
|
282
338
|
|
|
339
|
+
def _inflate_tracked(self, model: type, ident: Any, data: Mapping[str, Any]) -> Any:
|
|
340
|
+
obj = self._tracked.get((model, ident))
|
|
341
|
+
if obj is None:
|
|
342
|
+
obj = self._inflate(model, data)
|
|
343
|
+
self._tracked[(model, ident)] = obj
|
|
344
|
+
return obj
|
|
345
|
+
|
|
283
346
|
def _scan_model(self, model: type) -> List[Any]:
|
|
284
347
|
out = [self._inflate(model, row) for row in self._rows_for(model)]
|
|
285
348
|
pk = _single_pk_name(model)
|
|
286
349
|
by_id = {getattr(obj, pk): obj for obj in out}
|
|
287
350
|
for (known_model, ident), row in self._puts.items():
|
|
288
351
|
if known_model is model:
|
|
289
|
-
by_id[ident] = self.
|
|
352
|
+
by_id[ident] = self._inflate_tracked(model, ident, row)
|
|
353
|
+
for ident, obj in list(by_id.items()):
|
|
354
|
+
self._tracked[(model, ident)] = obj
|
|
290
355
|
for known_model, ident in self._dels:
|
|
291
356
|
if known_model is model:
|
|
292
357
|
by_id.pop(ident, None)
|
|
@@ -309,9 +374,15 @@ class XlsxSession(TigrblSessionBase):
|
|
|
309
374
|
columns = [self._pk_of(table)]
|
|
310
375
|
sheet.append(columns)
|
|
311
376
|
for row in rows:
|
|
312
|
-
sheet.append([row.get(col) for col in columns])
|
|
377
|
+
sheet.append([self._excel_value(row.get(col)) for col in columns])
|
|
313
378
|
self._atomic_save_workbook(workbook, self._cat.path)
|
|
314
379
|
|
|
380
|
+
@staticmethod
|
|
381
|
+
def _excel_value(value: Any) -> Any:
|
|
382
|
+
if isinstance(value, uuid.UUID):
|
|
383
|
+
return str(value)
|
|
384
|
+
return value
|
|
385
|
+
|
|
315
386
|
def _atomic_save_workbook(self, workbook: Any, path: str) -> None:
|
|
316
387
|
directory = os.path.dirname(path) or "."
|
|
317
388
|
fd, tmp = tempfile.mkstemp(dir=directory, prefix=".tmp_", suffix=".xlsx")
|
|
@@ -351,12 +422,18 @@ class XlsxSession(TigrblSessionBase):
|
|
|
351
422
|
return entity
|
|
352
423
|
|
|
353
424
|
def _all_subclasses(base: type) -> list[type]:
|
|
425
|
+
def _safe_subclasses(cls: type) -> list[type]:
|
|
426
|
+
try:
|
|
427
|
+
return list(cls.__subclasses__())
|
|
428
|
+
except TypeError:
|
|
429
|
+
return []
|
|
430
|
+
|
|
354
431
|
out: list[type] = []
|
|
355
|
-
stack =
|
|
432
|
+
stack = _safe_subclasses(base)
|
|
356
433
|
while stack:
|
|
357
434
|
cls = stack.pop()
|
|
358
435
|
out.append(cls)
|
|
359
|
-
stack.extend(cls
|
|
436
|
+
stack.extend(_safe_subclasses(cls))
|
|
360
437
|
return out
|
|
361
438
|
|
|
362
439
|
def _find_by_table(name: str) -> type | None:
|
{tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/tests/test_tigrblapp_rpc_usage.py
RENAMED
|
@@ -45,7 +45,7 @@ def app_and_db(tmp_path: Path) -> tuple[TigrblApp, object]:
|
|
|
45
45
|
db = session_factory()
|
|
46
46
|
|
|
47
47
|
api = TigrblApp(engine=spec)
|
|
48
|
-
api.
|
|
48
|
+
api.include_table(XlsxWidget, mount_router=False)
|
|
49
49
|
api.initialize()
|
|
50
50
|
return api, db
|
|
51
51
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/src/tigrbl_engine_xlsx/__init__.py
RENAMED
|
File without changes
|
{tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/src/tigrbl_engine_xlsx/engine.py
RENAMED
|
File without changes
|
|
File without changes
|
{tigrbl_engine_xlsx-0.1.1.dev1 → tigrbl_engine_xlsx-0.1.1.dev3}/tests/test_xlsx_workbook_io.py
RENAMED
|
File without changes
|