plexos2duckdb 0.1.0b7__py3-none-win_amd64.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.
@@ -0,0 +1,9 @@
1
+ """Python interface for the ``plexos2duckdb`` command-line tool.
2
+
3
+ This package currently provides a small subprocess-backed wrapper class for
4
+ the installed CLI. Native Rust bindings are not exposed at this time.
5
+ """
6
+
7
+ from .api import PLEXOS2DuckDB, PLEXOS2DuckDBError
8
+
9
+ __all__ = ["PLEXOS2DuckDB", "PLEXOS2DuckDBError"]
plexos2duckdb/api.py ADDED
@@ -0,0 +1,464 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import pathlib
5
+ import shutil
6
+ import subprocess
7
+ from typing import Any, Callable
8
+
9
+ import duckdb
10
+ from rich.table import Table
11
+
12
+
13
+ class PLEXOS2DuckDBError(RuntimeError):
14
+ """Raised when the plexos2duckdb CLI returns an error."""
15
+
16
+
17
+ class PLEXOS2DuckDB:
18
+ """Python workflow wrapper around the ``plexos2duckdb`` CLI.
19
+
20
+ Use this class when you want to start from a PLEXOS solution ZIP or XML
21
+ file, convert it to DuckDB, access conversion metadata, inspect table
22
+ inventory, and run SQL queries against the result from Python.
23
+
24
+ Typical workflow:
25
+
26
+ 1. Construct a client from the source solution file.
27
+
28
+ >>> client = PLEXOS2DuckDB("Model DAY_AHEAD Solution.zip")
29
+
30
+ 2. Convert it to DuckDB. The output path is optional and defaults
31
+ to the input path with a ``.duckdb`` suffix.
32
+ If the output file already exists, the conversion will error out
33
+ unless you pass ``force=True``.
34
+
35
+ >>> output_path = client.convert(force=True)
36
+
37
+ 3. Open the generated database as a context manager for repeated queries.
38
+
39
+ >>> with client as db:
40
+ ... rows = db.query("SELECT COUNT(*) FROM information_schema.tables")
41
+
42
+ Alternatively, you can use the `query()` method for one-off queries without managing a connection:
43
+
44
+ >>> rows = client.query("SELECT COUNT(*) FROM information_schema.tables")
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ input_path: str | pathlib.Path | None = None,
50
+ *,
51
+ output_path: str | pathlib.Path | None = None,
52
+ executable: str | pathlib.Path | None = None,
53
+ read_only: bool = True,
54
+ ) -> None:
55
+ self._explicit_executable = executable is not None
56
+ self._executable = (
57
+ pathlib.Path(executable)
58
+ if executable is not None
59
+ else self._resolve_executable()
60
+ )
61
+
62
+ self._input_path = pathlib.Path(input_path) if input_path is not None else None
63
+ self._output_path = self._normalize_output_path(output_path, self._input_path)
64
+ self._read_only = read_only
65
+ self._connection: duckdb.DuckDBPyConnection | None = None
66
+
67
+ def __enter__(self) -> PLEXOS2DuckDB:
68
+ self.open()
69
+ return self
70
+
71
+ def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
72
+ self.close()
73
+
74
+ @property
75
+ def executable(self) -> pathlib.Path:
76
+ return self._executable
77
+
78
+ @property
79
+ def input_path(self) -> pathlib.Path | None:
80
+ return self._input_path
81
+
82
+ @property
83
+ def output_path(self) -> pathlib.Path | None:
84
+ return self._output_path
85
+
86
+ @property
87
+ def database(self) -> pathlib.Path | None:
88
+ return self._output_path
89
+
90
+ @property
91
+ def connection(self) -> duckdb.DuckDBPyConnection:
92
+ if self._connection is None:
93
+ raise PLEXOS2DuckDBError(
94
+ "No managed DuckDB connection is open. Run `convert()` first and then use `with client as db`, call `open()`, or use `connect()` for a temporary connection."
95
+ )
96
+ return self._connection
97
+
98
+ @property
99
+ def metadata(self) -> Table:
100
+ return self._build_metadata_table(self._load_metadata())
101
+
102
+ @property
103
+ def inventory(self) -> Table:
104
+ return self._build_inventory_table(self._load_inventory())
105
+
106
+ def inspect(
107
+ self,
108
+ database: str | pathlib.Path | None = None,
109
+ *,
110
+ format: str = "table",
111
+ ) -> Table | dict[str, Any]:
112
+ database_path = self._resolve_output_path(database)
113
+ completed = self._run_command(
114
+ [
115
+ "inspect",
116
+ "--input",
117
+ str(database_path),
118
+ "--format-diagnostics",
119
+ "json",
120
+ ]
121
+ )
122
+ payload = json.loads(completed.stdout)
123
+ if format == "json":
124
+ return payload
125
+ if format != "table":
126
+ raise PLEXOS2DuckDBError("inspect format must be either 'table' or 'json'")
127
+ return self._build_metadata_table(payload["metadata"])
128
+
129
+ def open(
130
+ self,
131
+ database: str | pathlib.Path | None = None,
132
+ *,
133
+ read_only: bool | None = None,
134
+ ) -> duckdb.DuckDBPyConnection:
135
+ database_path = self._resolve_output_path(database)
136
+ if read_only is not None:
137
+ self._read_only = read_only
138
+ if self._connection is None:
139
+ self._connection = duckdb.connect(
140
+ str(database_path), read_only=self._read_only
141
+ )
142
+ return self._connection
143
+
144
+ def close(self) -> None:
145
+ if self._connection is not None:
146
+ self._connection.close()
147
+ self._connection = None
148
+
149
+ def _repr_items(self) -> list[tuple[str, Any]]:
150
+ items: list[tuple[str, Any]] = []
151
+
152
+ if self._input_path is not None:
153
+ items.append(("input_path", str(self._input_path)))
154
+ if self._output_path is not None:
155
+ items.append(("output_path", str(self._output_path)))
156
+ if not self._read_only:
157
+ items.append(("read_only", False))
158
+ if self._explicit_executable:
159
+ items.append(("executable", str(self._executable)))
160
+
161
+ return items
162
+
163
+ def __repr__(self) -> str:
164
+ body = ", ".join(f"{key}={value!r}" for key, value in self._repr_items())
165
+ return f"{type(self).__name__}({body})"
166
+
167
+ def _repr_pretty_(self, printer: Any, cycle: bool) -> None:
168
+ if cycle:
169
+ printer.text(f"{type(self).__name__}(...)")
170
+ return
171
+
172
+ items = self._repr_items()
173
+ if not items:
174
+ printer.text(f"{type(self).__name__}()")
175
+ return
176
+
177
+ printer.text(f"{type(self).__name__}(")
178
+ with printer.group(2, "", ")"):
179
+ for index, (key, value) in enumerate(items):
180
+ if index:
181
+ printer.text(",")
182
+ printer.breakable()
183
+ printer.text(f"{key}=")
184
+ printer.pretty(value)
185
+
186
+ def version(self) -> str:
187
+ completed = self._run_command(["--version"])
188
+ return completed.stdout.strip()
189
+
190
+ def convert(
191
+ self,
192
+ input_path: str | pathlib.Path | None = None,
193
+ output_path: str | pathlib.Path | None = None,
194
+ *,
195
+ force: bool = False,
196
+ in_memory: bool = False,
197
+ n_threads: int | None = None,
198
+ on_event: Callable[[dict[str, Any]], None] | None = None,
199
+ ) -> pathlib.Path:
200
+ input_path = self._resolve_input_path(input_path)
201
+ requested_output_path = (
202
+ pathlib.Path(output_path) if output_path is not None else self._output_path
203
+ )
204
+ args = [
205
+ "convert",
206
+ "--input",
207
+ str(input_path),
208
+ "--format-diagnostics",
209
+ "json",
210
+ "--no-progress-bar",
211
+ ]
212
+ if requested_output_path is not None:
213
+ args.extend(["--output", str(requested_output_path)])
214
+ if force:
215
+ args.append("--force")
216
+ if in_memory:
217
+ args.append("--in-memory")
218
+ if n_threads is not None:
219
+ args.extend(["--n-threads", str(n_threads)])
220
+
221
+ if on_event is None:
222
+ completed = self._run_command(args)
223
+ events = [
224
+ json.loads(line)
225
+ for line in completed.stdout.splitlines()
226
+ if line.strip()
227
+ ]
228
+ else:
229
+ events = self._run_convert_stream(args, on_event)
230
+
231
+ for event in reversed(events):
232
+ if event.get("event") == "completed":
233
+ path = pathlib.Path(event["output"])
234
+ self._input_path = input_path
235
+ self._output_path = path
236
+ return path
237
+ raise PLEXOS2DuckDBError("Conversion did not emit a completion event")
238
+
239
+ def connect(
240
+ self, database: str | pathlib.Path | None = None, *, read_only: bool = True
241
+ ) -> duckdb.DuckDBPyConnection:
242
+ database_path = self._resolve_output_path(database)
243
+ return duckdb.connect(str(database_path), read_only=read_only)
244
+
245
+ def query(
246
+ self,
247
+ sql: str,
248
+ parameters: list[Any] | tuple[Any, ...] | None = None,
249
+ *,
250
+ database: str | pathlib.Path | None = None,
251
+ read_only: bool = True,
252
+ ) -> list[tuple[Any, ...]]:
253
+ if database is None and self._connection is not None:
254
+ result = self._connection.execute(sql, parameters or [])
255
+ return result.fetchall()
256
+
257
+ connection = self.connect(database, read_only=read_only)
258
+ try:
259
+ result = connection.execute(sql, parameters or [])
260
+ return result.fetchall()
261
+ finally:
262
+ connection.close()
263
+
264
+ def _resolve_executable(self) -> pathlib.Path:
265
+ import sysconfig
266
+
267
+ module_scripts_dir = pathlib.Path(__file__).resolve().parents[2] / "bin"
268
+ candidates = [
269
+ module_scripts_dir / "plexos2duckdb",
270
+ module_scripts_dir / "plexos2duckdb.exe",
271
+ pathlib.Path(sysconfig.get_path("scripts")) / "plexos2duckdb",
272
+ pathlib.Path(sysconfig.get_path("scripts")) / "plexos2duckdb.exe",
273
+ ]
274
+ for candidate in candidates:
275
+ if candidate.exists():
276
+ return candidate
277
+ resolved = shutil.which("plexos2duckdb")
278
+ if resolved is not None:
279
+ return pathlib.Path(resolved)
280
+ raise PLEXOS2DuckDBError(
281
+ "Could not find the bundled `plexos2duckdb` executable or a PATH fallback. Install the package with `pip install plexos2duckdb` or pass `executable=` explicitly."
282
+ )
283
+
284
+ def _quote_ident(self, identifier: str) -> str:
285
+ return '"' + identifier.replace('"', '""') + '"'
286
+
287
+ def _load_metadata(
288
+ self, database: str | pathlib.Path | None = None
289
+ ) -> dict[str, Any]:
290
+ database_path = self._resolve_output_path(database)
291
+ connection = self.connect(database_path)
292
+ try:
293
+ rows = connection.execute(
294
+ "SELECT key, value FROM main.plexos2duckdb"
295
+ ).fetchall()
296
+ finally:
297
+ connection.close()
298
+
299
+ metadata = {key: value for key, value in rows}
300
+ return {
301
+ "database": str(database_path),
302
+ "converter_version": metadata.get("plexos2duckdb_version", ""),
303
+ "source_file": metadata.get("plexos_file", ""),
304
+ "model_name": metadata.get("model_name", ""),
305
+ }
306
+
307
+ def _load_inventory(
308
+ self, database: str | pathlib.Path | None = None
309
+ ) -> list[dict[str, Any]]:
310
+ connection = self.connect(database)
311
+ try:
312
+ rows = connection.execute(
313
+ """
314
+ SELECT table_schema, table_name, table_type
315
+ FROM information_schema.tables
316
+ WHERE table_schema NOT IN ('information_schema', 'pg_catalog')
317
+ ORDER BY table_schema, table_name
318
+ """
319
+ ).fetchall()
320
+ inventory = []
321
+ for schema, table_name, table_type in rows:
322
+ kind = "table" if table_type == "BASE TABLE" else "view"
323
+ row_count = "-"
324
+ if table_type == "BASE TABLE":
325
+ quoted_schema = self._quote_ident(schema)
326
+ quoted_table = self._quote_ident(table_name)
327
+ count = connection.execute(
328
+ f"SELECT COUNT(*) FROM {quoted_schema}.{quoted_table}"
329
+ ).fetchone()[0]
330
+ row_count = str(count)
331
+ inventory.append(
332
+ {
333
+ "schema": schema,
334
+ "table": table_name,
335
+ "kind": kind,
336
+ "row_count": row_count,
337
+ }
338
+ )
339
+ return inventory
340
+ finally:
341
+ connection.close()
342
+
343
+ def _build_metadata_table(self, metadata: dict[str, Any]) -> Table:
344
+ table = Table(title="PLEXOS2DuckDB Metadata")
345
+ table.add_column("Field")
346
+ table.add_column("Value")
347
+ for key in ("database", "converter_version", "source_file", "model_name"):
348
+ table.add_row(key, str(metadata.get(key, "")))
349
+ return table
350
+
351
+ def _build_inventory_table(self, inventory: list[dict[str, Any]]) -> Table:
352
+ table = Table(title="PLEXOS2DuckDB Inventory")
353
+ table.add_column("Schema")
354
+ table.add_column("Table")
355
+ table.add_column("Kind")
356
+ table.add_column("Row Count", justify="right")
357
+ for row in inventory:
358
+ table.add_row(
359
+ str(row.get("schema", "")),
360
+ str(row.get("table", "")),
361
+ str(row.get("kind", "")),
362
+ str(row.get("row_count", "")),
363
+ )
364
+ return table
365
+
366
+ def _display_state(self) -> dict[str, Any]:
367
+ return {
368
+ "input_path": str(self._input_path)
369
+ if self._input_path is not None
370
+ else None,
371
+ "output_path": str(self._output_path)
372
+ if self._output_path is not None
373
+ else None,
374
+ "output_exists": self._output_path.exists()
375
+ if self._output_path is not None
376
+ else False,
377
+ "connection_open": self._connection is not None,
378
+ "read_only": self._read_only,
379
+ "executable": str(self._executable),
380
+ }
381
+
382
+ def _normalize_output_path(
383
+ self,
384
+ output_path: str | pathlib.Path | None,
385
+ input_path: pathlib.Path | None,
386
+ ) -> pathlib.Path | None:
387
+ if output_path is not None:
388
+ path = pathlib.Path(output_path)
389
+ return path if path.suffix else path.with_suffix(".duckdb")
390
+ if input_path is None:
391
+ return None
392
+ if input_path.suffix.lower() in {".zip", ".xml"}:
393
+ candidate = input_path.with_suffix(".duckdb")
394
+ return candidate if candidate.exists() else None
395
+ return None
396
+
397
+ def _resolve_input_path(
398
+ self, input_path: str | pathlib.Path | None
399
+ ) -> pathlib.Path:
400
+ if input_path is not None:
401
+ return pathlib.Path(input_path)
402
+ if self._input_path is not None:
403
+ return self._input_path
404
+ raise PLEXOS2DuckDBError("An input solution path is required for conversion")
405
+
406
+ def _resolve_output_path(self, database: str | pathlib.Path | None) -> pathlib.Path:
407
+ if database is not None:
408
+ return pathlib.Path(database)
409
+ if self._output_path is not None:
410
+ return self._output_path
411
+ raise PLEXOS2DuckDBError(
412
+ "An output DuckDB path is required for this operation. Run `convert()` first, pass `output_path=` in the constructor, or provide `database=` explicitly."
413
+ )
414
+
415
+ def _run_convert_stream(
416
+ self,
417
+ args: list[str],
418
+ on_event: Callable[[dict[str, Any]], None],
419
+ ) -> list[dict[str, Any]]:
420
+ process = subprocess.Popen(
421
+ [str(self._executable), *args],
422
+ text=True,
423
+ stdout=subprocess.PIPE,
424
+ stderr=subprocess.PIPE,
425
+ )
426
+ events: list[dict[str, Any]] = []
427
+ stdout = process.stdout
428
+ if stdout is None:
429
+ process.kill()
430
+ raise PLEXOS2DuckDBError("Failed to capture conversion output")
431
+
432
+ try:
433
+ for line in stdout:
434
+ line = line.strip()
435
+ if not line:
436
+ continue
437
+ event = json.loads(line)
438
+ events.append(event)
439
+ on_event(event)
440
+ finally:
441
+ stdout.close()
442
+
443
+ stderr = process.stderr.read() if process.stderr is not None else ""
444
+ if process.stderr is not None:
445
+ process.stderr.close()
446
+ return_code = process.wait()
447
+ if return_code != 0:
448
+ message = stderr.strip() or "Unknown error"
449
+ raise PLEXOS2DuckDBError(message)
450
+ return events
451
+
452
+ def _run_command(self, args: list[str]) -> subprocess.CompletedProcess[str]:
453
+ completed = subprocess.run(
454
+ [str(self._executable), *args],
455
+ text=True,
456
+ capture_output=True,
457
+ check=False,
458
+ )
459
+ if completed.returncode != 0:
460
+ message = (
461
+ completed.stderr.strip() or completed.stdout.strip() or "Unknown error"
462
+ )
463
+ raise PLEXOS2DuckDBError(message)
464
+ return completed
plexos2duckdb/py.typed ADDED
File without changes
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: plexos2duckdb
3
+ Version: 0.1.0b7
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Environment :: Console
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: Intended Audience :: Science/Research
8
+ Classifier: License :: OSI Approved :: BSD License
9
+ Classifier: Operating System :: MacOS
10
+ Classifier: Operating System :: Microsoft :: Windows
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Rust
14
+ Classifier: Topic :: Database
15
+ Classifier: Topic :: Scientific/Engineering
16
+ Classifier: Topic :: Utilities
17
+ Requires-Dist: duckdb>=1.4.0
18
+ Requires-Dist: rich>=13.0.0
19
+ License-File: LICENSE
20
+ License-File: NOTICE.md
21
+ Summary: A command line tool to convert PLEXOS solution files to a DuckDB database.
22
+ Keywords: plexos,duckdb,cli,energy
23
+ Home-Page: https://github.com/epri-dev/plexos2duckdb
24
+ Author-email: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com>
25
+ License-Expression: BSD-3-Clause
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
28
+ Project-URL: Documentation, https://github.com/epri-dev/plexos2duckdb
29
+ Project-URL: Homepage, https://github.com/epri-dev/plexos2duckdb
30
+ Project-URL: Issues, https://github.com/epri-dev/plexos2duckdb/issues
31
+ Project-URL: Repository, https://github.com/epri-dev/plexos2duckdb
32
+
33
+ # plexos2duckdb
34
+
35
+ [![Release](https://img.shields.io/github/v/release/epri-dev/plexos2duckdb)](https://github.com/epri-dev/plexos2duckdb/releases)
36
+ [![License](https://img.shields.io/github/license/epri-dev/plexos2duckdb)](https://github.com/epri-dev/plexos2duckdb/blob/main/LICENSE)
37
+ [![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20MacOS%20%7C%20Linux-blue)]()
38
+ [![Downloads](https://img.shields.io/github/downloads/epri-dev/plexos2duckdb/total?color=brightgreen)](https://github.com/epri-dev/plexos2duckdb/releases)
39
+
40
+ This is a command line tool to convert PLEXOS solution files to a DuckDB database.
41
+
42
+ ## Installation
43
+
44
+ ### Option 1: Install with pip
45
+
46
+ On supported platforms, you can install the command-line tool directly with pip:
47
+
48
+ ```shell
49
+ pip install plexos2duckdb
50
+ ```
51
+
52
+ Then verify the install:
53
+
54
+ ```shell
55
+ plexos2duckdb --version
56
+ ```
57
+
58
+ Note that this requires a published wheel for your platform.
59
+
60
+ Basic Python usage:
61
+
62
+ ```python
63
+ from plexos2duckdb import PLEXOS2DuckDB
64
+
65
+ client = PLEXOS2DuckDB("Model DAY_AHEAD Solution.zip")
66
+ output_path = client.convert()
67
+
68
+ with client as db:
69
+ rows = db.query("SELECT COUNT(*) FROM information_schema.tables")
70
+ ```
71
+
72
+ ### Option 2: Download prebuilt binary
73
+
74
+ Prebuilt binaries are available from the
75
+ [GitHub Releases](https://github.com/epri-dev/plexos2duckdb/releases) page.
76
+
77
+ 1. Go to the [latest release](https://github.com/epri-dev/plexos2duckdb/releases/latest) page on
78
+ GitHub.
79
+ 2. Download the appropriate binary for your operating system.
80
+ 3. Extract the archive:
81
+
82
+ ```shell
83
+ tar -xzf plexos2duckdb-<platform>.tar.gz
84
+ ```
85
+
86
+ or on Windows:
87
+
88
+ ```powershell
89
+ Expand-Archive plexos2duckdb-<platform>.zip -DestinationPath .
90
+ ```
91
+
92
+ 4. Copy the binary to a directory in your `PATH`, e.g.:
93
+
94
+ ```shell
95
+ # MacOS/Linux
96
+ cp plexos2duckdb ~/local/bin/
97
+ # Windows
98
+ copy plexos2duckdb.exe %USERPROFILE%\local\bin\
99
+ ```
100
+
101
+ You will have to make sure `~/local/bin/` is in your `PATH`.
102
+
103
+ ### Option 3: Build from Source
104
+
105
+ If you prefer to build from source, ensure you have [Rust](https://www.rust-lang.org/tools/install)
106
+ and [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) installed.
107
+
108
+ 1. Clone the repository:
109
+
110
+ ```shell
111
+ git clone https://github.com/epri-dev/plexos2duckdb.git
112
+ cd plexos2duckdb
113
+ ```
114
+
115
+ 2. Build the binary:
116
+
117
+ ```shell
118
+ cargo build --release
119
+ ```
120
+
121
+ The compiled binary will be located at:
122
+
123
+ ```text
124
+ target/release/plexos2duckdb
125
+ ```
126
+
127
+ 3. Copy the binary to a directory in your `PATH`:
128
+
129
+ ```shell
130
+ # MacOS/Linux
131
+ cp target/release/plexos2duckdb ~/local/bin/
132
+ # Windows
133
+ copy target\release\plexos2duckdb.exe %USERPROFILE%\local\bin\
134
+ ```
135
+
136
+ You will have to make sure `~/local/bin/` is in your `PATH`.
137
+
138
+ ## Quickstart
139
+
140
+ Verify installation is successful by checking the version:
141
+
142
+ ```shell
143
+ plexos2duckdb --version
144
+ ```
145
+
146
+ Run the help command to see available options:
147
+
148
+ ```shell
149
+ plexos2duckdb --help
150
+ ```
151
+
152
+ Convert a solution zip file to a duckdb database:
153
+
154
+ ```shell
155
+ plexos2duckdb convert --input "Model-DayAhead-Solution.zip" --output "Model-DayAhead-Solution.duckdb"
156
+ ```
157
+
158
+ If the output database already exists, re-run with `--force` to overwrite it:
159
+
160
+ ```shell
161
+ plexos2duckdb convert --input "Model-DayAhead-Solution.zip" --output "Model-DayAhead-Solution.duckdb" --force
162
+ ```
163
+
164
+ Inspect an existing database to view metadata, source file info, model name, and table inventory
165
+ with row counts:
166
+
167
+ ```shell
168
+ plexos2duckdb inspect --input "Model-DayAhead-Solution.duckdb"
169
+ ```
170
+
171
+ For machine-readable diagnostics in automation or CI, use JSON output:
172
+
173
+ ```shell
174
+ plexos2duckdb inspect --input "Model-DayAhead-Solution.duckdb" --format-diagnostics json
175
+ plexos2duckdb convert --input "Model-DayAhead-Solution.zip" --output "Model-DayAhead-Solution.duckdb" --format-diagnostics json
176
+ ```
177
+
178
+ Generate shell completions to stdout with the `generate-shell-completions` subcommand:
179
+
180
+ ```shell
181
+ plexos2duckdb generate-shell-completions bash > ~/.local/share/bash-completion/completions/plexos2duckdb
182
+ plexos2duckdb generate-shell-completions zsh > ~/.zfunc/_plexos2duckdb
183
+ ```
184
+
185
+ You may use any
186
+ [duckdb compatible database viewer](https://duckdb.org/docs/stable/core_extensions/ui) to
187
+ interactively explore the data with SQL:
188
+
189
+ <img width="1728" height="775" alt="image" src="https://github.com/user-attachments/assets/ad829556-bef1-4982-b7b3-f7a62d225985" />
190
+
@@ -0,0 +1,10 @@
1
+ plexos2duckdb/__init__.py,sha256=Vki08dnr0iGk9-Dn6UcrJT59_kyVEs9aIaol2UpM2Vg,328
2
+ plexos2duckdb/api.py,sha256=GqtJ3zXf2qbUEq0Ffc-CmmRmHLHnkdcEhKJZT8pXkaY,16847
3
+ plexos2duckdb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ plexos2duckdb-0.1.0b7.data/scripts/plexos2duckdb.exe,sha256=8yj-cvSlzjYprHTbcPUEVH4j5UF6FarZMAwdaLenJkg,25124864
5
+ plexos2duckdb-0.1.0b7.dist-info/METADATA,sha256=aC-CbM7Knu6hr6KyaEGGgC49q8H00fA2bgyLh-8o70E,5885
6
+ plexos2duckdb-0.1.0b7.dist-info/WHEEL,sha256=uJOc2U-Q1x95AlblQcqMRb3iR4QnPtdI7X2ycPN99rM,94
7
+ plexos2duckdb-0.1.0b7.dist-info/licenses/LICENSE,sha256=EhG_vWNKAkrkYgHCPApwnrGsxsuYpOnV6NutaHuNowI,1489
8
+ plexos2duckdb-0.1.0b7.dist-info/licenses/NOTICE.md,sha256=fo78WZqgNyQfbkRTPXFeZhcqUCUIXHXoWkmcr_VVm6k,687
9
+ plexos2duckdb-0.1.0b7.dist-info/sboms/plexos2duckdb.cyclonedx.json,sha256=69mTWJSaz-sX8p0eWFyb3i0biDDI4HFaY-m637NFkN0,392342
10
+ plexos2duckdb-0.1.0b7.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.12.6)
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-win_amd64