laketower 0.5.0__py3-none-any.whl → 0.6.0__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.

Potentially problematic release.


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

laketower/web.py CHANGED
@@ -3,17 +3,21 @@ from pathlib import Path
3
3
  from typing import Annotated
4
4
 
5
5
  import pydantic_settings
6
- from fastapi import APIRouter, FastAPI, Query, Request
7
- from fastapi.responses import HTMLResponse
6
+ from fastapi import APIRouter, FastAPI, File, Form, Query, Request, UploadFile
7
+ from fastapi.responses import HTMLResponse, Response
8
8
  from fastapi.staticfiles import StaticFiles
9
9
  from fastapi.templating import Jinja2Templates
10
10
 
11
11
  from laketower.config import Config, load_yaml_config
12
12
  from laketower.tables import (
13
13
  DEFAULT_LIMIT,
14
+ ImportFileFormatEnum,
15
+ ImportModeEnum,
14
16
  execute_query,
15
17
  generate_table_statistics_query,
16
18
  generate_table_query,
19
+ import_file_to_table,
20
+ load_datasets,
17
21
  load_table,
18
22
  )
19
23
 
@@ -55,10 +59,7 @@ def index(request: Request) -> HTMLResponse:
55
59
  @router.get("/tables/query", response_class=HTMLResponse)
56
60
  def get_tables_query(request: Request, sql: str) -> HTMLResponse:
57
61
  config: Config = request.app.state.config
58
- tables_dataset = {
59
- table_config.name: load_table(table_config).dataset()
60
- for table_config in config.tables
61
- }
62
+ tables_dataset = load_datasets(config.tables)
62
63
 
63
64
  try:
64
65
  results = execute_query(tables_dataset, sql)
@@ -80,13 +81,36 @@ def get_tables_query(request: Request, sql: str) -> HTMLResponse:
80
81
  )
81
82
 
82
83
 
84
+ @router.get("/tables/query/csv")
85
+ def export_tables_query_csv(request: Request, sql: str) -> Response:
86
+ config: Config = request.app.state.config
87
+ tables_dataset = load_datasets(config.tables)
88
+
89
+ results = execute_query(tables_dataset, sql)
90
+ csv_content = results.to_csv(header=True, index=False, sep=",")
91
+
92
+ return Response(
93
+ content=csv_content,
94
+ media_type="text/csv",
95
+ headers={"Content-Disposition": "attachment; filename=query_results.csv"},
96
+ )
97
+
98
+
83
99
  @router.get("/tables/{table_id}", response_class=HTMLResponse)
84
100
  def get_table_index(request: Request, table_id: str) -> HTMLResponse:
85
101
  config: Config = request.app.state.config
86
102
  table_config = next(
87
103
  filter(lambda table_config: table_config.name == table_id, config.tables)
88
104
  )
89
- table = load_table(table_config)
105
+ try:
106
+ table = load_table(table_config)
107
+ table_metadata = table.metadata()
108
+ table_schema = table.schema()
109
+ error = None
110
+ except ValueError as e:
111
+ error = {"message": str(e)}
112
+ table_metadata = None
113
+ table_schema = None
90
114
 
91
115
  return templates.TemplateResponse(
92
116
  request=request,
@@ -95,8 +119,9 @@ def get_table_index(request: Request, table_id: str) -> HTMLResponse:
95
119
  "tables": config.tables,
96
120
  "queries": config.queries,
97
121
  "table_id": table_id,
98
- "table_metadata": table.metadata(),
99
- "table_schema": table.schema(),
122
+ "table_metadata": table_metadata,
123
+ "table_schema": table_schema,
124
+ "error": error,
100
125
  },
101
126
  )
102
127
 
@@ -107,7 +132,13 @@ def get_table_history(request: Request, table_id: str) -> HTMLResponse:
107
132
  table_config = next(
108
133
  filter(lambda table_config: table_config.name == table_id, config.tables)
109
134
  )
110
- table = load_table(table_config)
135
+ try:
136
+ table = load_table(table_config)
137
+ table_history = table.history()
138
+ error = None
139
+ except ValueError as e:
140
+ error = {"message": str(e)}
141
+ table_history = None
111
142
 
112
143
  return templates.TemplateResponse(
113
144
  request=request,
@@ -116,7 +147,8 @@ def get_table_history(request: Request, table_id: str) -> HTMLResponse:
116
147
  "tables": config.tables,
117
148
  "queries": config.queries,
118
149
  "table_id": table_id,
119
- "table_history": table.history(),
150
+ "table_history": table_history,
151
+ "error": error,
120
152
  },
121
153
  )
122
154
 
@@ -131,12 +163,18 @@ def get_table_statistics(
131
163
  table_config = next(
132
164
  filter(lambda table_config: table_config.name == table_id, config.tables)
133
165
  )
134
- table = load_table(table_config)
135
- table_name = table_config.name
136
- table_metadata = table.metadata()
137
- table_dataset = table.dataset(version=version)
138
- sql_query = generate_table_statistics_query(table_name)
139
- query_results = execute_query({table_name: table_dataset}, sql_query)
166
+ try:
167
+ table = load_table(table_config)
168
+ table_name = table_config.name
169
+ table_metadata = table.metadata()
170
+ table_dataset = table.dataset(version=version)
171
+ sql_query = generate_table_statistics_query(table_name)
172
+ query_results = execute_query({table_name: table_dataset}, sql_query)
173
+ error = None
174
+ except ValueError as e:
175
+ error = {"message": str(e)}
176
+ table_metadata = None
177
+ query_results = None
140
178
 
141
179
  return templates.TemplateResponse(
142
180
  request=request,
@@ -147,6 +185,7 @@ def get_table_statistics(
147
185
  "table_id": table_id,
148
186
  "table_metadata": table_metadata,
149
187
  "table_results": query_results,
188
+ "error": error,
150
189
  },
151
190
  )
152
191
 
@@ -165,14 +204,21 @@ def get_table_view(
165
204
  table_config = next(
166
205
  filter(lambda table_config: table_config.name == table_id, config.tables)
167
206
  )
168
- table = load_table(table_config)
169
- table_name = table_config.name
170
- table_metadata = table.metadata()
171
- table_dataset = table.dataset(version=version)
172
- sql_query = generate_table_query(
173
- table_name, limit=limit, cols=cols, sort_asc=sort_asc, sort_desc=sort_desc
174
- )
175
- results = execute_query({table_name: table_dataset}, sql_query)
207
+ try:
208
+ table = load_table(table_config)
209
+ table_name = table_config.name
210
+ table_metadata = table.metadata()
211
+ table_dataset = table.dataset(version=version)
212
+ sql_query = generate_table_query(
213
+ table_name, limit=limit, cols=cols, sort_asc=sort_asc, sort_desc=sort_desc
214
+ )
215
+ results = execute_query({table_name: table_dataset}, sql_query)
216
+ error = None
217
+ except ValueError as e:
218
+ error = {"message": str(e)}
219
+ table_metadata = None
220
+ sql_query = None
221
+ results = None
176
222
 
177
223
  return templates.TemplateResponse(
178
224
  request=request,
@@ -185,6 +231,78 @@ def get_table_view(
185
231
  "table_results": results,
186
232
  "sql_query": sql_query,
187
233
  "default_limit": DEFAULT_LIMIT,
234
+ "error": error,
235
+ },
236
+ )
237
+
238
+
239
+ @router.get("/tables/{table_id}/import", response_class=HTMLResponse)
240
+ def get_table_import(
241
+ request: Request,
242
+ table_id: str,
243
+ ) -> HTMLResponse:
244
+ config: Config = request.app.state.config
245
+ table_config = next(
246
+ filter(lambda table_config: table_config.name == table_id, config.tables)
247
+ )
248
+ try:
249
+ table = load_table(table_config)
250
+ table_metadata = table.metadata()
251
+ message = None
252
+ except ValueError as e:
253
+ message = {"type": "error", "body": str(e)}
254
+ table_metadata = None
255
+
256
+ return templates.TemplateResponse(
257
+ request=request,
258
+ name="tables/import.html",
259
+ context={
260
+ "tables": config.tables,
261
+ "queries": config.queries,
262
+ "table_id": table_id,
263
+ "table_metadata": table_metadata,
264
+ "message": message,
265
+ },
266
+ )
267
+
268
+
269
+ @router.post("/tables/{table_id}/import", response_class=HTMLResponse)
270
+ def post_table_import(
271
+ request: Request,
272
+ table_id: str,
273
+ input_file: Annotated[UploadFile, File()],
274
+ mode: Annotated[ImportModeEnum, Form()],
275
+ file_format: Annotated[ImportFileFormatEnum, Form()],
276
+ delimiter: Annotated[str, Form()],
277
+ encoding: Annotated[str, Form()],
278
+ ) -> HTMLResponse:
279
+ config: Config = request.app.state.config
280
+ table_config = next(
281
+ filter(lambda table_config: table_config.name == table_id, config.tables)
282
+ )
283
+ try:
284
+ table = load_table(table_config)
285
+ table_metadata = table.metadata()
286
+ rows_imported = import_file_to_table(
287
+ table_config, input_file.file, mode, file_format, delimiter, encoding
288
+ )
289
+ message = {
290
+ "type": "success",
291
+ "body": f"Successfully imported {rows_imported} rows",
292
+ }
293
+ except Exception as e:
294
+ message = {"type": "error", "body": str(e)}
295
+ table_metadata = None
296
+
297
+ return templates.TemplateResponse(
298
+ request=request,
299
+ name="tables/import.html",
300
+ context={
301
+ "tables": config.tables,
302
+ "queries": config.queries,
303
+ "table_id": table_id,
304
+ "table_metadata": table_metadata,
305
+ "message": message,
188
306
  },
189
307
  )
190
308
 
@@ -195,10 +313,7 @@ def get_query_view(request: Request, query_id: str) -> HTMLResponse:
195
313
  query_config = next(
196
314
  filter(lambda query_config: query_config.name == query_id, config.queries)
197
315
  )
198
- tables_dataset = {
199
- table_config.name: load_table(table_config).dataset()
200
- for table_config in config.tables
201
- }
316
+ tables_dataset = load_datasets(config.tables)
202
317
 
203
318
  try:
204
319
  results = execute_query(tables_dataset, query_config.sql)
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: laketower
3
- Version: 0.5.0
3
+ Version: 0.6.0
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
7
- Project-URL: Changelog, https://github.com/datalpia/laketower/blob/master/CHANGELOG.md
7
+ Project-URL: Changelog, https://github.com/datalpia/laketower/blob/main/CHANGELOG.md
8
8
  Author-email: Romain Clement <git@romain-clement.net>
9
9
  License: Apache-2.0
10
10
  License-File: LICENSE
@@ -22,7 +22,7 @@ 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: deltalake
25
+ Requires-Dist: deltalake<2,>=1
26
26
  Requires-Dist: duckdb
27
27
  Requires-Dist: fastapi
28
28
  Requires-Dist: jinja2!=3.1.5,>=3
@@ -30,6 +30,7 @@ Requires-Dist: pandas
30
30
  Requires-Dist: pyarrow!=19.0.0
31
31
  Requires-Dist: pydantic-settings>=2
32
32
  Requires-Dist: pydantic>=2
33
+ Requires-Dist: python-multipart
33
34
  Requires-Dist: pyyaml
34
35
  Requires-Dist: rich
35
36
  Requires-Dist: sqlglot
@@ -50,13 +51,16 @@ Utility application to explore and manage tables in your data lakehouse, especia
50
51
  ## Features
51
52
 
52
53
  - Delta Lake table format support
54
+ - Remote tables support (S3, ADLS)
53
55
  - Inspect table metadata
54
56
  - Inspect table schema
55
57
  - Inspect table history
56
58
  - Get table statistics
59
+ - Import data into a table from CSV files
57
60
  - View table content with a simple query builder
58
61
  - Query all registered tables with DuckDB SQL dialect
59
62
  - Execute saved queries
63
+ - Export query results to CSV files
60
64
  - Static and versionable YAML configuration
61
65
  - Web application
62
66
  - CLI application
@@ -99,7 +103,9 @@ queries:
99
103
 
100
104
  Current limitations:
101
105
 
102
- - `tables.uri`: only local paths are allowed
106
+ - `tables.uri`:
107
+ - Local paths are supported (`./path/to/table`, `/abs/path/to/table`, `file:///abs/path/to/table`)
108
+ - Remote paths to S3 (`s3://<bucket>/<path>`) and ADLS (`abfss://<container>/<path>`)
103
109
  - `tables.format`: only `delta` is allowed
104
110
 
105
111
  Example from the provided demo:
@@ -138,6 +144,103 @@ queries:
138
144
  day asc
139
145
  ```
140
146
 
147
+ Support for environment variables substitution is also supported within the YAML
148
+ configuration using a object containing a single key `env` with the name of the
149
+ environment variable to be injected. The value of the variable can contain JSON
150
+ and will be decoded in a best effort manner (default to string value). For instance:
151
+
152
+ ```yaml
153
+ # export TABLE_URI=path/to/table
154
+
155
+ tables:
156
+ - name: sample_table
157
+ uri:
158
+ env: TABLE_URI
159
+ format: delta
160
+ ```
161
+
162
+ #### Remote S3 Tables
163
+
164
+ Configuring S3 tables (AWS, MinIO, Cloudflare R2):
165
+
166
+ ```yaml
167
+ tables:
168
+ - name: delta_table_s3
169
+ uri: s3://<bucket>/path/to/table
170
+ format: delta
171
+ connection:
172
+ s3:
173
+ s3_access_key_id: access-key-id
174
+ s3_secret_access_key: secret-access-key
175
+ s3_region: s3-region
176
+ s3_endpoint_url: http://s3.domain.com
177
+ s3_allow_http: false
178
+ ```
179
+
180
+ Depending on your object storage location and configuration, one might have to
181
+ set part or all the available `connection.s3` parameters. The only required ones
182
+ are `s3_access_key_id` and `s3_secret_access_key`.
183
+
184
+ Also as a security best practice, it is best not to write secrets directly in
185
+ static configuration files, so one can use environment variables to all dynamic substitution,
186
+ e.g.
187
+
188
+ ```yaml
189
+ tables:
190
+ - name: delta_table_s3
191
+ uri: s3://<bucket>/path/to/table
192
+ format: delta
193
+ connection:
194
+ s3:
195
+ s3_access_key_id: access-key-id
196
+ s3_secret_access_key:
197
+ env: S3_SECRET_ACCESS_KEY
198
+ s3_region: s3-region
199
+ s3_endpoint_url: http://s3.domain.com
200
+ s3_allow_http: false
201
+ ```
202
+
203
+ #### Remote ADLS Tables
204
+
205
+ Configuring Azure ADLS tables:
206
+
207
+ ```yaml
208
+ tables:
209
+ - name: delta_table_adls
210
+ uri: abfss://<container>/path/to/table
211
+ format: delta
212
+ connection:
213
+ adls:
214
+ adls_account_name: adls-account-name
215
+ adls_access_key: adls-access-key
216
+ adls_sas_key: adls-sas-key
217
+ adls_tenant_id: adls-tenant-id
218
+ adls_client_id: adls-client-id
219
+ adls_client_secret: adls-client-secret
220
+ azure_msi_endpoint: https://msi.azure.com
221
+ use_azure_cli: false
222
+ ```
223
+
224
+ Depending on your object storage location and configuration, one might have to
225
+ set part or all the available `connection.adls` parameters. The only required one
226
+ is `adls_account_name`.
227
+
228
+ Also as a security best practice, it is best not to write secrets directly in
229
+ static configuration files, so one can use environment variables to all dynamic substitution,
230
+ e.g.
231
+
232
+ ```yaml
233
+ tables:
234
+ - name: delta_table_adls
235
+ uri: abfss://<container>/path/to/table
236
+ format: delta
237
+ connection:
238
+ adls:
239
+ adls_account_name: adls-account-name
240
+ adls_access_key:
241
+ env: ADLS_ACCESS_KEY
242
+ ```
243
+
141
244
  ### Web Application
142
245
 
143
246
  The easiest way to get started is to launch the Laketower web application:
@@ -148,12 +251,13 @@ $ laketower -c demo/laketower.yml web
148
251
 
149
252
  #### Screenshots
150
253
 
151
- ![Laketower UI - Tables Overview](docs/static/tables_overview.png)
152
- ![Laketower UI - Tables View](docs/static/tables_view.png)
153
- ![Laketower UI - Tables Statistics](docs/static/tables_statistics.png)
154
- ![Laketower UI - Tables History](docs/static/tables_history.png)
155
- ![Laketower UI - Tables Query](docs/static/tables_query.png)
156
- ![Laketower UI - Queries View](docs/static/queries_view.png)
254
+ ![Laketower UI - Tables Overview](https://raw.githubusercontent.com/datalpia/laketower/refs/heads/main/docs/static/tables_overview.png)
255
+ ![Laketower UI - Tables View](https://raw.githubusercontent.com/datalpia/laketower/refs/heads/main/docs/static/tables_view.png)
256
+ ![Laketower UI - Tables Statistics](https://raw.githubusercontent.com/datalpia/laketower/refs/heads/main/docs/static/tables_statistics.png)
257
+ ![Laketower UI - Tables History](https://raw.githubusercontent.com/datalpia/laketower/refs/heads/main/docs/static/tables_history.png)
258
+ ![Laketower UI - Tables Import](https://raw.githubusercontent.com/datalpia/laketower/refs/heads/main/docs/static/tables_import.png)
259
+ ![Laketower UI - Tables Query](https://raw.githubusercontent.com/datalpia/laketower/refs/heads/main/docs/static/tables_query.png)
260
+ ![Laketower UI - Queries View](https://raw.githubusercontent.com/datalpia/laketower/refs/heads/main/docs/static/queries_view.png)
157
261
 
158
262
  ### CLI
159
263
 
@@ -321,6 +425,29 @@ $ laketower -c demo/laketower.yml tables statistics --version 0 weather
321
425
  └──────────────────────┴───────┴──────┴──────┴──────┴──────┘
322
426
  ```
323
427
 
428
+ #### Import data into a given table
429
+
430
+ Import a CSV dataset into a table in append mode:
431
+
432
+ ```bash
433
+ $ laketower -c demo/laketower.yml tables import weather --file data.csv --mode append --format csv --delimiter ',' --encoding 'utf-8'
434
+ ```
435
+
436
+ `--mode` argument can be one of:
437
+ - `append`: append rows to the table (default)
438
+ - `overwrite`: replace all rows with the ones from the input file
439
+
440
+ `--format` argument can be one of:
441
+ - `csv`: CSV file format (default)
442
+
443
+ `--delimiter` argument can be:
444
+ - Any single character (only valid for CSV file format)
445
+ - Default is _comma_ (`','`)
446
+
447
+ `--encoding` argument can be:
448
+ - Any [standard Python encoding](https://docs.python.org/3/library/codecs.html#standard-encodings),
449
+ - Default is `'utf-8'`
450
+
324
451
  #### View a given table
325
452
 
326
453
  Using a simple query builder, the content of a table can be displayed.
@@ -400,6 +527,14 @@ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) a
400
527
  └───────────────────────────┴────────────────────┘
401
528
  ```
402
529
 
530
+ Export query results to CSV:
531
+
532
+ ```bash
533
+ $ laketower -c demo/laketower.yml tables query --output results.csv "select date_trunc('day', time) as day, avg(temperature_2m) as mean_temperature from weather group by day order by day desc limit 3"
534
+
535
+ Query results written to: results.csv
536
+ ```
537
+
403
538
  #### List saved queries
404
539
 
405
540
  ```bash
@@ -0,0 +1,23 @@
1
+ laketower/__about__.py,sha256=cID1jLnC_vj48GgMN6Yb1FA3JsQ95zNmCHmRYE8TFhY,22
2
+ laketower/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ laketower/__main__.py,sha256=czKxJKG8OfncnxWmpaOWx7b1JBwFnZNQi7wKSTncB4M,108
4
+ laketower/cli.py,sha256=tvCr90q4jRVAoP2qzwhTLG7PUsae0QOGQwJRid3GVLc,15324
5
+ laketower/config.py,sha256=uIQSE1MjEuA-kp0TwA0QREwPbaNGL9hLGmKWqNaA8VY,3298
6
+ laketower/tables.py,sha256=gs4klWJkyyS7_oIDz1HKFicXAF6jbGfvzJWrDw8r-rQ,10235
7
+ laketower/web.py,sha256=-cXg_8pUCdhU5QF6WH_3luXi545F0Et9rpSC05J63Ng,10736
8
+ laketower/static/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ laketower/templates/_base.html,sha256=S-8kjAfYBx3Btb4FwzM2qyfkGYrOBHhpvCWR32mCvOw,3729
10
+ laketower/templates/index.html,sha256=dLF2Og0qgzBkvGyVRidRNzTv0u4o97ifOx1jVeig8Kg,59
11
+ laketower/templates/queries/view.html,sha256=naqU3XGyVVW6Er8wQ95-DYlp38Czolvh-h5noIAWs84,1978
12
+ laketower/templates/tables/_macros.html,sha256=sCI1TOFW0QA74oSXW87H6dNTudOs7n-FretnTPFcRh4,1174
13
+ laketower/templates/tables/history.html,sha256=a5GBLXCiLlbWno5eR0XT5i_oMAghylUBBFOpr27NB3Q,1853
14
+ laketower/templates/tables/import.html,sha256=bQZwRrv84tDBuf0AHJyc7L-PjW-XSoZhMHNDIo6TP4c,2604
15
+ laketower/templates/tables/index.html,sha256=saNdQbJAjMJAzayTk4rA5Mmw_bCXvor2WpghVmoWSAI,2507
16
+ laketower/templates/tables/query.html,sha256=ymWcqZj4TtJgUeCIMseJD0PIOqy0gf1SVzrQzN9UD5Q,1652
17
+ laketower/templates/tables/statistics.html,sha256=h6TiQtFwiRWvPqDphcRRF1rZ886FP00UbJuMHuW5l6U,1827
18
+ laketower/templates/tables/view.html,sha256=ruiAX_S--wpodmgEbcQ-GT7BQzz-vzSCk4NpzlO3I80,3985
19
+ laketower-0.6.0.dist-info/METADATA,sha256=HMP4sBtBKVgvO0Ay_KIJd6que4EKaICfXLXYA9TQ12o,25496
20
+ laketower-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ laketower-0.6.0.dist-info/entry_points.txt,sha256=sJpQgRwdeZhRBudNqBTqtHPCE-uLC9YgFXJY2CTEyCk,53
22
+ laketower-0.6.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
23
+ laketower-0.6.0.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ laketower = laketower.__main__:cli
@@ -1,22 +0,0 @@
1
- laketower/__about__.py,sha256=LBK46heutvn3KmsCrKIYu8RQikbfnjZaj2xFrXaeCzQ,22
2
- laketower/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- laketower/__main__.py,sha256=czKxJKG8OfncnxWmpaOWx7b1JBwFnZNQi7wKSTncB4M,108
4
- laketower/cli.py,sha256=U4gI12egcOs51wxjmQlU70XhA2QGcowc0AmTYpUKEFE,11962
5
- laketower/config.py,sha256=NdUDF7lr2hEW9Gujp0OpkOKcDP46ju1y_r0IM4Hrx2M,1100
6
- laketower/tables.py,sha256=QwqoK73Q9pDRzyuoN9pwmwP_WWj3Rg2qPJaIcCdnJbw,4402
7
- laketower/web.py,sha256=5NMKj26aVz3cKnUAe-3sLDJ_4Ue3u0VXhATrDQ8GVF8,7205
8
- laketower/static/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- laketower/templates/_base.html,sha256=S-8kjAfYBx3Btb4FwzM2qyfkGYrOBHhpvCWR32mCvOw,3729
10
- laketower/templates/index.html,sha256=dLF2Og0qgzBkvGyVRidRNzTv0u4o97ifOx1jVeig8Kg,59
11
- laketower/templates/queries/view.html,sha256=2jW08X-PflXmsfy7u9ibX1BF8G5dx9X_n_Eoq3jzizA,1686
12
- laketower/templates/tables/_macros.html,sha256=fnj_8nBco0iS6mlBmGmfT2PZhI2Y82yP0cm8tRxchpU,965
13
- laketower/templates/tables/history.html,sha256=yAW0xw9_Uxp0QZYKje6qhcbpeznxI3fb740hfNyILZ8,1740
14
- laketower/templates/tables/index.html,sha256=oY13l_p8qozlLONanLpga1WhEo4oTP92pRf9sBSuFZI,2394
15
- laketower/templates/tables/query.html,sha256=YAFnW8Q5abDsbeglFHHZmJfGJXPjI2s4Nxf6gF_-Eg0,1360
16
- laketower/templates/tables/statistics.html,sha256=rgIOuF2PlHo2jvcYDAnxa5ObNortwyALlrURpM7qxMw,1714
17
- laketower/templates/tables/view.html,sha256=psfeRKkN19Q3Ko5Sm2570qRhehvuoEBPG89zFU5KQlc,3872
18
- laketower-0.5.0.dist-info/METADATA,sha256=AKQgJ1YSUgNA3bXyyBweGY3BAMDtR2Mlr2SzQFZZ8P8,20935
19
- laketower-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- laketower-0.5.0.dist-info/entry_points.txt,sha256=OL_4klopvyEzasJOFJ-sKu54lv24Jvomni32h1WVUjk,48
21
- laketower-0.5.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
22
- laketower-0.5.0.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- laketower = laketower:cli.cli