laketower 0.6.0__tar.gz → 0.6.1__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.

Potentially problematic release.


This version of laketower might be problematic. Click here for more details.

Files changed (71) hide show
  1. {laketower-0.6.0 → laketower-0.6.1}/.github/workflows/ci-cd.yml +1 -1
  2. {laketower-0.6.0 → laketower-0.6.1}/.gitignore +3 -0
  3. {laketower-0.6.0 → laketower-0.6.1}/CHANGELOG.md +26 -1
  4. {laketower-0.6.0 → laketower-0.6.1}/PKG-INFO +37 -1
  5. {laketower-0.6.0 → laketower-0.6.1}/README.md +34 -0
  6. laketower-0.6.1/demo/laketower.yml +56 -0
  7. laketower-0.6.1/docs/static/queries_view.png +0 -0
  8. {laketower-0.6.0 → laketower-0.6.1}/docs/static/tables_history.png +0 -0
  9. {laketower-0.6.0 → laketower-0.6.1}/docs/static/tables_import.png +0 -0
  10. laketower-0.6.1/docs/static/tables_overview.png +0 -0
  11. laketower-0.6.1/docs/static/tables_query.png +0 -0
  12. {laketower-0.6.0 → laketower-0.6.1}/docs/static/tables_statistics.png +0 -0
  13. {laketower-0.6.0 → laketower-0.6.1}/docs/static/tables_view.png +0 -0
  14. laketower-0.6.1/laketower/__about__.py +1 -0
  15. {laketower-0.6.0 → laketower-0.6.1}/laketower/cli.py +42 -6
  16. {laketower-0.6.0 → laketower-0.6.1}/laketower/config.py +6 -0
  17. laketower-0.6.1/laketower/static/editor.bundle.js +27433 -0
  18. laketower-0.6.1/laketower/static/editor.js +74 -0
  19. laketower-0.6.1/laketower/static/vendor/bootstrap/bootstrap.bundle.min.js +7 -0
  20. laketower-0.6.1/laketower/static/vendor/bootstrap-icons/bootstrap-icons.min.css +5 -0
  21. laketower-0.6.1/laketower/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  22. laketower-0.6.1/laketower/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  23. laketower-0.6.1/laketower/static/vendor/halfmoon/halfmoon.min.css +22 -0
  24. laketower-0.6.1/laketower/static/vendor/halfmoon/halfmoon.modern.css +282 -0
  25. {laketower-0.6.0 → laketower-0.6.1}/laketower/tables.py +18 -2
  26. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/_base.html +22 -19
  27. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/queries/view.html +18 -4
  28. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/tables/query.html +25 -1
  29. {laketower-0.6.0 → laketower-0.6.1}/laketower/web.py +75 -4
  30. laketower-0.6.1/package-lock.json +1139 -0
  31. laketower-0.6.1/package.json +16 -0
  32. {laketower-0.6.0 → laketower-0.6.1}/pyproject.toml +8 -4
  33. {laketower-0.6.0 → laketower-0.6.1}/tasks.py +52 -3
  34. {laketower-0.6.0 → laketower-0.6.1}/tests/conftest.py +11 -1
  35. {laketower-0.6.0 → laketower-0.6.1}/tests/test_cli.py +109 -2
  36. {laketower-0.6.0 → laketower-0.6.1}/tests/test_config.py +1 -0
  37. {laketower-0.6.0 → laketower-0.6.1}/tests/test_tables.py +13 -0
  38. {laketower-0.6.0 → laketower-0.6.1}/tests/test_web.py +122 -14
  39. {laketower-0.6.0 → laketower-0.6.1}/uv.lock +162 -95
  40. laketower-0.6.0/demo/laketower.yml +0 -34
  41. laketower-0.6.0/docs/static/queries_view.png +0 -0
  42. laketower-0.6.0/docs/static/tables_overview.png +0 -0
  43. laketower-0.6.0/docs/static/tables_query.png +0 -0
  44. laketower-0.6.0/laketower/__about__.py +0 -1
  45. {laketower-0.6.0 → laketower-0.6.1}/.python-version +0 -0
  46. {laketower-0.6.0 → laketower-0.6.1}/LICENSE +0 -0
  47. {laketower-0.6.0 → laketower-0.6.1}/demo/generate.py +0 -0
  48. {laketower-0.6.0 → laketower-0.6.1}/demo/sample_table/_delta_log/00000000000000000000.json +0 -0
  49. {laketower-0.6.0 → laketower-0.6.1}/demo/sample_table/_delta_log/00000000000000000001.json +0 -0
  50. {laketower-0.6.0 → laketower-0.6.1}/demo/sample_table/_delta_log/00000000000000000002.json +0 -0
  51. {laketower-0.6.0 → laketower-0.6.1}/demo/sample_table/_delta_log/00000000000000000003.json +0 -0
  52. {laketower-0.6.0 → laketower-0.6.1}/demo/sample_table/part-00001-1a31a393-6db6-4d1a-bf4e-81ea061ff8cd-c000.snappy.parquet +0 -0
  53. {laketower-0.6.0 → laketower-0.6.1}/demo/sample_table/part-00001-5af77102-9207-4c89-aaf6-37e1f815ec26-c000.snappy.parquet +0 -0
  54. {laketower-0.6.0 → laketower-0.6.1}/demo/sample_table/part-00001-b11bab55-43d0-4d05-ae88-5b9481ae57db-c000.snappy.parquet +0 -0
  55. {laketower-0.6.0 → laketower-0.6.1}/demo/weather/_delta_log/00000000000000000000.json +0 -0
  56. {laketower-0.6.0 → laketower-0.6.1}/demo/weather/_delta_log/00000000000000000001.json +0 -0
  57. {laketower-0.6.0 → laketower-0.6.1}/demo/weather/_delta_log/00000000000000000002.json +0 -0
  58. {laketower-0.6.0 → laketower-0.6.1}/demo/weather/part-00001-2323b963-be56-44e0-8c10-e237e7e6d4b9-c000.snappy.parquet +0 -0
  59. {laketower-0.6.0 → laketower-0.6.1}/demo/weather/part-00001-6360cbf8-f8a9-475f-8729-6f20b4ca64a9-c000.snappy.parquet +0 -0
  60. {laketower-0.6.0 → laketower-0.6.1}/laketower/__init__.py +0 -0
  61. {laketower-0.6.0 → laketower-0.6.1}/laketower/__main__.py +0 -0
  62. {laketower-0.6.0 → laketower-0.6.1}/laketower/static/.gitkeep +0 -0
  63. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/index.html +0 -0
  64. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/tables/_macros.html +0 -0
  65. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/tables/history.html +0 -0
  66. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/tables/import.html +0 -0
  67. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/tables/index.html +0 -0
  68. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/tables/statistics.html +0 -0
  69. {laketower-0.6.0 → laketower-0.6.1}/laketower/templates/tables/view.html +0 -0
  70. {laketower-0.6.0 → laketower-0.6.1}/renovate.json +0 -0
  71. {laketower-0.6.0 → laketower-0.6.1}/tests/__init__.py +0 -0
@@ -78,7 +78,7 @@ jobs:
78
78
  - name: Export code coverage report to job summary
79
79
  run: cat "${CODECOV_MD_FILE}" >> "${GITHUB_STEP_SUMMARY}"
80
80
  - name: Add or update code coverage comment to pull request
81
- uses: actions/github-script@v7
81
+ uses: actions/github-script@v8
82
82
  if: ${{ github.event_name == 'pull_request' }}
83
83
  with:
84
84
  script: |
@@ -7,6 +7,9 @@ wheels/
7
7
  *.egg-info
8
8
  .coverage
9
9
 
10
+ # Node.js
11
+ node_modules/
12
+
10
13
  # Virtual environments
11
14
  .venv
12
15
 
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.1] - 2025-09-11
11
+ Patch release with minor enhancements (SQL syntax highlighting, query parameters,
12
+ predefined query Markdown description) and quality of life improvements
13
+ (hide read-only SQL editor in predefined queries, web app offline usage).
14
+
15
+ ### Added
16
+ - web: display app version in sidebar
17
+ - web: use CodeMirror SQL query editor
18
+ - web: add support for tables query parameters
19
+ - web: add support for predefined queries parameters
20
+ - web: add optional markdown description for predefined queries
21
+ - cli: add support for tables query parameters
22
+ - cli: add support for predefined queries parameters
23
+
24
+ ### Changed
25
+ - web: hide SQL editor for predefined queries
26
+ - demo: add parameters to daily avg temperature query
27
+
28
+ ### Fixed
29
+ - handle empty SQL queries
30
+
31
+ ### Misc
32
+ - vendor static assets for offline usage
33
+
10
34
  ## [0.6.0] - 2025-08-27
11
35
  Minor release with new features (CSV import/export, S3/ADLS remote tables)
12
36
  and quality of life improvements (tables lazy loading, quoted SQL identifiers).
@@ -106,7 +130,8 @@ Initial release of `laketower`.
106
130
  - View a given table with simple query builder
107
131
  - Query all registered tables with DuckDB SQL dialect
108
132
 
109
- [Unreleased]: https://github.com/datalpia/laketower/compare/0.6.0...HEAD
133
+ [Unreleased]: https://github.com/datalpia/laketower/compare/0.6.1...HEAD
134
+ [0.6.1]: https://github.com/datalpia/laketower/compare/0.6.0...0.6.1
110
135
  [0.6.0]: https://github.com/datalpia/laketower/compare/0.5.1...0.6.0
111
136
  [0.5.1]: https://github.com/datalpia/laketower/compare/0.5.0...0.5.1
112
137
  [0.5.0]: https://github.com/datalpia/laketower/compare/0.4.1...0.5.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: laketower
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: Oversee your lakehouse
5
5
  Project-URL: Repository, https://github.com/datalpia/laketower
6
6
  Project-URL: Issues, https://github.com/datalpia/laketower/issues
@@ -22,10 +22,12 @@ Classifier: Topic :: Database
22
22
  Classifier: Topic :: Software Development
23
23
  Classifier: Topic :: Utilities
24
24
  Requires-Python: <3.14,>=3.10
25
+ Requires-Dist: bleach
25
26
  Requires-Dist: deltalake<2,>=1
26
27
  Requires-Dist: duckdb
27
28
  Requires-Dist: fastapi
28
29
  Requires-Dist: jinja2!=3.1.5,>=3
30
+ Requires-Dist: markdown
29
31
  Requires-Dist: pandas
30
32
  Requires-Dist: pyarrow!=19.0.0
31
33
  Requires-Dist: pydantic-settings>=2
@@ -98,6 +100,10 @@ tables:
98
100
  queries:
99
101
  - name: <query_name>
100
102
  title: <Query name>
103
+ description: <Query description>
104
+ parameters:
105
+ <param_name_1>:
106
+ default: <default_value>
101
107
  sql: <sql expression>
102
108
  ```
103
109
 
@@ -527,6 +533,20 @@ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) a
527
533
  └───────────────────────────┴────────────────────┘
528
534
  ```
529
535
 
536
+ Use named parameters within a giving query (note: escape `$` prefixes properly!):
537
+
538
+ ```bash
539
+ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) as day, avg(temperature_2m) as mean_temperature from weather where day between \$start_date and \$end_date group by day order by day desc" -p start_date 2025-01-29 -p end_date 2025-01-31
540
+
541
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
542
+ ┃ day ┃ mean_temperature ┃
543
+ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
544
+ │ 2025-01-31 00:00:00+01:00 │ 5.683333257834117 │
545
+ │ 2025-01-30 00:00:00+01:00 │ 8.900000015894571 │
546
+ │ 2025-01-29 00:00:00+01:00 │ 7.770833313465118 │
547
+ └───────────────────────────┴────────────────────┘
548
+ ```
549
+
530
550
  Export query results to CSV:
531
551
 
532
552
  ```bash
@@ -574,6 +594,22 @@ $ laketower -c demo/laketower.yml queries view daily_avg_temperature
574
594
  └───────────────────────────┴─────────────────┘
575
595
  ```
576
596
 
597
+ Executing a predefined query with parameters (here `start_date` and `end_date`):
598
+
599
+ ```bash
600
+ $ laketower -c demo/laketower.yml queries view daily_avg_temperature_params -p start_date 2025-02-01 -p end_date 2025-02-05
601
+
602
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
603
+ ┃ day ┃ avg_temperature ┃
604
+ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
605
+ │ 2025-02-01 00:00:00+01:00 │ 4.0 │
606
+ │ 2025-02-02 00:00:00+01:00 │ 4.0 │
607
+ │ 2025-02-03 00:00:00+01:00 │ 4.0 │
608
+ │ 2025-02-04 00:00:00+01:00 │ 3.0 │
609
+ │ 2025-02-05 00:00:00+01:00 │ 3.0 │
610
+ └───────────────────────────┴─────────────────┘
611
+ ```
612
+
577
613
  ## License
578
614
 
579
615
  Licensed under [Apache License 2.0](LICENSE)
@@ -59,6 +59,10 @@ tables:
59
59
  queries:
60
60
  - name: <query_name>
61
61
  title: <Query name>
62
+ description: <Query description>
63
+ parameters:
64
+ <param_name_1>:
65
+ default: <default_value>
62
66
  sql: <sql expression>
63
67
  ```
64
68
 
@@ -488,6 +492,20 @@ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) a
488
492
  └───────────────────────────┴────────────────────┘
489
493
  ```
490
494
 
495
+ Use named parameters within a giving query (note: escape `$` prefixes properly!):
496
+
497
+ ```bash
498
+ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) as day, avg(temperature_2m) as mean_temperature from weather where day between \$start_date and \$end_date group by day order by day desc" -p start_date 2025-01-29 -p end_date 2025-01-31
499
+
500
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
501
+ ┃ day ┃ mean_temperature ┃
502
+ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
503
+ │ 2025-01-31 00:00:00+01:00 │ 5.683333257834117 │
504
+ │ 2025-01-30 00:00:00+01:00 │ 8.900000015894571 │
505
+ │ 2025-01-29 00:00:00+01:00 │ 7.770833313465118 │
506
+ └───────────────────────────┴────────────────────┘
507
+ ```
508
+
491
509
  Export query results to CSV:
492
510
 
493
511
  ```bash
@@ -535,6 +553,22 @@ $ laketower -c demo/laketower.yml queries view daily_avg_temperature
535
553
  └───────────────────────────┴─────────────────┘
536
554
  ```
537
555
 
556
+ Executing a predefined query with parameters (here `start_date` and `end_date`):
557
+
558
+ ```bash
559
+ $ laketower -c demo/laketower.yml queries view daily_avg_temperature_params -p start_date 2025-02-01 -p end_date 2025-02-05
560
+
561
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
562
+ ┃ day ┃ avg_temperature ┃
563
+ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
564
+ │ 2025-02-01 00:00:00+01:00 │ 4.0 │
565
+ │ 2025-02-02 00:00:00+01:00 │ 4.0 │
566
+ │ 2025-02-03 00:00:00+01:00 │ 4.0 │
567
+ │ 2025-02-04 00:00:00+01:00 │ 3.0 │
568
+ │ 2025-02-05 00:00:00+01:00 │ 3.0 │
569
+ └───────────────────────────┴─────────────────┘
570
+ ```
571
+
538
572
  ## License
539
573
 
540
574
  Licensed under [Apache License 2.0](LICENSE)
@@ -0,0 +1,56 @@
1
+ tables:
2
+ - name: sample_table
3
+ uri: demo/sample_table
4
+ format: delta
5
+ - name: weather
6
+ uri: demo/weather
7
+ format: delta
8
+ - name: invalid_uri_table
9
+ uri: demo/invalid_uri_table
10
+ format: delta
11
+
12
+ queries:
13
+ - name: all_data
14
+ title: All data
15
+ description: Display all rows from `sample_table` and `weather` tables
16
+ sql: |
17
+ select
18
+ sample_table.*,
19
+ weather.*
20
+ from
21
+ sample_table,
22
+ weather
23
+ limit 10
24
+ - name: daily_avg_temperature
25
+ title: Daily average temperature
26
+ description: Display daily average temperature values from `weather` table
27
+ sql: |
28
+ select
29
+ date_trunc('day', time) as day,
30
+ round(avg(temperature_2m)) as avg_temperature
31
+ from
32
+ weather
33
+ group by
34
+ day
35
+ order by
36
+ day asc
37
+ - name: daily_avg_temperature_params
38
+ title: Daily average temperature with parameters
39
+ description: Display daily average temperature values from `weather` table, with dynamic filters for start and end dates
40
+ parameters:
41
+ start_date:
42
+ default: "2025-01-15"
43
+ end_date:
44
+ default: "2025-01-31"
45
+ sql: |
46
+ select
47
+ date_trunc('day', time) as day,
48
+ round(avg(temperature_2m)) as avg_temperature
49
+ from
50
+ weather
51
+ where
52
+ day between $start_date and $end_date
53
+ group by
54
+ day
55
+ order by
56
+ day asc
@@ -0,0 +1 @@
1
+ __version__ = "0.6.1"
@@ -15,6 +15,7 @@ from laketower.tables import (
15
15
  ImportFileFormatEnum,
16
16
  ImportModeEnum,
17
17
  execute_query,
18
+ extract_query_parameter_names,
18
19
  generate_table_query,
19
20
  generate_table_statistics_query,
20
21
  import_file_to_table,
@@ -180,13 +181,21 @@ def view_table(
180
181
 
181
182
 
182
183
  def query_table(
183
- config_path: Path, sql_query: str, output_path: Path | None = None
184
+ config_path: Path,
185
+ sql_query: str,
186
+ sql_params: list[list[str]] = [],
187
+ output_path: Path | None = None,
184
188
  ) -> None:
185
189
  out: rich.jupyter.JupyterMixin
186
190
  try:
187
191
  config = load_yaml_config(config_path)
188
192
  tables_dataset = load_datasets(config.tables)
189
- results = execute_query(tables_dataset, sql_query)
193
+ sql_params_dict = {param[0]: param[1] for param in sql_params}
194
+ query_param_names = extract_query_parameter_names(sql_query)
195
+ query_params = {
196
+ name: sql_params_dict.get(name) or "" for name in query_param_names
197
+ }
198
+ results = execute_query(tables_dataset, sql_query, sql_params=query_params)
190
199
 
191
200
  out = rich.table.Table()
192
201
  for column in results.columns:
@@ -243,14 +252,23 @@ def list_queries(config_path: Path) -> None:
243
252
  console.print(tree)
244
253
 
245
254
 
246
- def view_query(config_path: Path, query_name: str) -> None:
255
+ def view_query(
256
+ config_path: Path, query_name: str, query_params: list[list[str]] = []
257
+ ) -> None:
247
258
  out: rich.jupyter.JupyterMixin
248
259
  try:
249
260
  config = load_yaml_config(config_path)
250
261
  tables_dataset = load_datasets(config.tables)
251
262
  query_config = next(filter(lambda x: x.name == query_name, config.queries))
263
+ default_parameters = {k: v.default for k, v in query_config.parameters.items()}
252
264
  sql_query = query_config.sql
253
- results = execute_query(tables_dataset, sql_query)
265
+ query_params_dict = {param[0]: param[1] for param in query_params}
266
+ sql_param_names = extract_query_parameter_names(sql_query)
267
+ sql_params = {
268
+ name: query_params_dict.get(name) or default_parameters.get(name) or ""
269
+ for name in sql_param_names
270
+ }
271
+ results = execute_query(tables_dataset, sql_query, sql_params=sql_params)
254
272
 
255
273
  out = rich.table.Table()
256
274
  for column in results.columns:
@@ -369,9 +387,17 @@ def cli() -> None:
369
387
  parser_tables_query.add_argument(
370
388
  "--output", help="Output query results to a file (default format: CSV)"
371
389
  )
390
+ parser_tables_query.add_argument(
391
+ "--param",
392
+ "-p",
393
+ nargs=2,
394
+ action="append",
395
+ default=[],
396
+ help="Inject query named parameters values",
397
+ )
372
398
  parser_tables_query.add_argument("sql", help="SQL query to execute")
373
399
  parser_tables_query.set_defaults(
374
- func=lambda x: query_table(x.config, x.sql, x.output)
400
+ func=lambda x: query_table(x.config, x.sql, x.param, x.output)
375
401
  )
376
402
 
377
403
  parser_tables_import = subsparsers_tables.add_parser(
@@ -419,7 +445,17 @@ def cli() -> None:
419
445
  "view", help="View a given query"
420
446
  )
421
447
  parser_queries_view.add_argument("query", help="Name of the query")
422
- parser_queries_view.set_defaults(func=lambda x: view_query(x.config, x.query))
448
+ parser_queries_view.add_argument(
449
+ "--param",
450
+ "-p",
451
+ nargs=2,
452
+ action="append",
453
+ default=[],
454
+ help="Inject query named parameters values",
455
+ )
456
+ parser_queries_view.set_defaults(
457
+ func=lambda x: view_query(x.config, x.query, x.param)
458
+ )
423
459
 
424
460
  args = parser.parse_args()
425
461
  args.func(args)
@@ -96,9 +96,15 @@ class ConfigTable(pydantic.BaseModel):
96
96
  connection: ConfigTableConnection | None = None
97
97
 
98
98
 
99
+ class ConfigQueryParameter(pydantic.BaseModel):
100
+ default: str
101
+
102
+
99
103
  class ConfigQuery(pydantic.BaseModel):
100
104
  name: str
101
105
  title: str
106
+ description: str | None = None
107
+ parameters: dict[str, ConfigQueryParameter] = {}
102
108
  sql: str
103
109
 
104
110