calkit-python 0.2.1__tar.gz → 0.3.0__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.
- {calkit_python-0.2.1 → calkit_python-0.3.0}/PKG-INFO +11 -3
- {calkit_python-0.2.1 → calkit_python-0.3.0}/README.md +6 -2
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/__init__.py +2 -1
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cli/__init__.py +1 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cli/main.py +12 -2
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cli/new.py +121 -3
- calkit_python-0.3.0/calkit/cli/office.py +50 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/config.py +2 -2
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/core.py +6 -3
- calkit_python-0.3.0/calkit/office.py +38 -0
- calkit_python-0.3.0/calkit/server.py +611 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/tests/cli/test_new.py +4 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/pyproject.toml +4 -0
- calkit_python-0.2.1/calkit/server.py +0 -203
- {calkit_python-0.2.1 → calkit_python-0.3.0}/.github/FUNDING.yml +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/.github/workflows/publish-test.yml +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/.github/workflows/publish.yml +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/.gitignore +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/LICENSE +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cli/config.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cli/core.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cli/import_.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cli/list.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/cloud.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/data.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/docker.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/dvc.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/git.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/gui.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/jupyter.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/models.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/tests/cli/test_list.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/tests/cli/test_main.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/tests/test_dvc.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/examples/cfd-study/README.md +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/examples/cfd-study/calkit.yaml +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/examples/cfd-study/config/simulations/runs.csv +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/examples/cfd-study/notebook.ipynb +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/examples/ms-office/.gitignore +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/examples/ms-office/README.md +0 -0
- {calkit_python-0.2.1 → calkit_python-0.3.0}/examples/ms-office/calkit.yaml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: calkit-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Reproducibility simplified.
|
|
5
5
|
Project-URL: Homepage, https://github.com/calkit/calkit
|
|
6
6
|
Project-URL: Issues, https://github.com/calkit/calkit/issues
|
|
@@ -10,17 +10,21 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Requires-Python: >=3.8
|
|
13
|
+
Requires-Dist: docx2pdf
|
|
13
14
|
Requires-Dist: dvc
|
|
14
15
|
Requires-Dist: eval-type-backport; python_version < '3.10'
|
|
15
16
|
Requires-Dist: fastapi
|
|
16
17
|
Requires-Dist: gitpython
|
|
17
18
|
Requires-Dist: keyring
|
|
18
19
|
Requires-Dist: nbconvert
|
|
20
|
+
Requires-Dist: pillow
|
|
19
21
|
Requires-Dist: pydantic-settings
|
|
20
22
|
Requires-Dist: pydantic[email]
|
|
21
23
|
Requires-Dist: pyjwt
|
|
24
|
+
Requires-Dist: pywin32; platform_system == 'Windows'
|
|
22
25
|
Requires-Dist: requests
|
|
23
26
|
Requires-Dist: typer
|
|
27
|
+
Requires-Dist: uvicorn
|
|
24
28
|
Provides-Extra: data
|
|
25
29
|
Requires-Dist: pandas; extra == 'data'
|
|
26
30
|
Requires-Dist: polars; extra == 'data'
|
|
@@ -31,10 +35,14 @@ Description-Content-Type: text/markdown
|
|
|
31
35
|
[Calkit](https://calkit.io) simplifies reproducibility,
|
|
32
36
|
acting as a layer on top of
|
|
33
37
|
[Git](https://git-scm.com/), [DVC](https://dvc.org/),
|
|
34
|
-
[
|
|
38
|
+
[Docker](https://docker.com), and more,
|
|
35
39
|
such that all all aspects of the research process can be fully described in a
|
|
36
40
|
single repository.
|
|
37
41
|
|
|
42
|
+
## Tutorials
|
|
43
|
+
|
|
44
|
+
- [Reproducible OpenFOAM simulations](https://petebachant.me/reproducible-openfoam/)
|
|
45
|
+
|
|
38
46
|
## Why does reproducibility matter?
|
|
39
47
|
|
|
40
48
|
If your work is reproducible, that means that someone else can "run" it and
|
|
@@ -55,7 +63,7 @@ community:
|
|
|
55
63
|
|
|
56
64
|
## Why another tool/platform?
|
|
57
65
|
|
|
58
|
-
Git, GitHub, DVC,
|
|
66
|
+
Git, GitHub, DVC, Docker et al. are amazing tools/platforms, but their
|
|
59
67
|
use involves multiple fairly difficult learning curves.
|
|
60
68
|
Our goal is to provide a single tool and platform to unify all of these so
|
|
61
69
|
that there is a single, gentle learning curve.
|
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
[Calkit](https://calkit.io) simplifies reproducibility,
|
|
4
4
|
acting as a layer on top of
|
|
5
5
|
[Git](https://git-scm.com/), [DVC](https://dvc.org/),
|
|
6
|
-
[
|
|
6
|
+
[Docker](https://docker.com), and more,
|
|
7
7
|
such that all all aspects of the research process can be fully described in a
|
|
8
8
|
single repository.
|
|
9
9
|
|
|
10
|
+
## Tutorials
|
|
11
|
+
|
|
12
|
+
- [Reproducible OpenFOAM simulations](https://petebachant.me/reproducible-openfoam/)
|
|
13
|
+
|
|
10
14
|
## Why does reproducibility matter?
|
|
11
15
|
|
|
12
16
|
If your work is reproducible, that means that someone else can "run" it and
|
|
@@ -27,7 +31,7 @@ community:
|
|
|
27
31
|
|
|
28
32
|
## Why another tool/platform?
|
|
29
33
|
|
|
30
|
-
Git, GitHub, DVC,
|
|
34
|
+
Git, GitHub, DVC, Docker et al. are amazing tools/platforms, but their
|
|
31
35
|
use involves multiple fairly difficult learning curves.
|
|
32
36
|
Our goal is to provide a single tool and platform to unify all of these so
|
|
33
37
|
that there is a single, gentle learning curve.
|
|
@@ -19,6 +19,7 @@ from calkit.cli.import_ import import_app
|
|
|
19
19
|
from calkit.cli.list import list_app
|
|
20
20
|
from calkit.cli.new import new_app
|
|
21
21
|
from calkit.cli.notebooks import notebooks_app
|
|
22
|
+
from calkit.cli.office import office_app
|
|
22
23
|
|
|
23
24
|
app = typer.Typer(
|
|
24
25
|
invoke_without_command=True,
|
|
@@ -36,6 +37,7 @@ app.add_typer(
|
|
|
36
37
|
app.add_typer(notebooks_app, name="nb", help="Work with Jupyter notebooks.")
|
|
37
38
|
app.add_typer(list_app, name="list", help="List Calkit objects.")
|
|
38
39
|
app.add_typer(import_app, name="import", help="Import objects.")
|
|
40
|
+
app.add_typer(office_app, name="office", help="Work with Microsoft Office.")
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
@app.callback()
|
|
@@ -157,6 +159,9 @@ def add(
|
|
|
157
159
|
else:
|
|
158
160
|
dvc_extensions = [
|
|
159
161
|
".png",
|
|
162
|
+
".jpeg",
|
|
163
|
+
".jpg",
|
|
164
|
+
".gif",
|
|
160
165
|
".h5",
|
|
161
166
|
".parquet",
|
|
162
167
|
".pickle",
|
|
@@ -164,6 +169,10 @@ def add(
|
|
|
164
169
|
".avi",
|
|
165
170
|
".webm",
|
|
166
171
|
".pdf",
|
|
172
|
+
".xlsx",
|
|
173
|
+
".docx",
|
|
174
|
+
".xls",
|
|
175
|
+
".doc",
|
|
167
176
|
]
|
|
168
177
|
dvc_size_thresh_bytes = 1_000_000
|
|
169
178
|
if "." in paths and to is None:
|
|
@@ -298,7 +307,7 @@ def run_server():
|
|
|
298
307
|
port=8866,
|
|
299
308
|
host="localhost",
|
|
300
309
|
reload=True,
|
|
301
|
-
reload_dirs=[os.path.dirname(__file__)],
|
|
310
|
+
reload_dirs=[os.path.dirname(os.path.dirname(__file__))],
|
|
302
311
|
)
|
|
303
312
|
|
|
304
313
|
|
|
@@ -368,7 +377,7 @@ def run_dvc_repro(
|
|
|
368
377
|
args += ["--pipeline", pipeline]
|
|
369
378
|
if downstream is not None:
|
|
370
379
|
args += downstream
|
|
371
|
-
subprocess.
|
|
380
|
+
subprocess.check_call(["dvc", "repro"] + args)
|
|
372
381
|
# Now parse stage metadata for calkit objects
|
|
373
382
|
if not os.path.isfile("dvc.yaml"):
|
|
374
383
|
raise_error("No dvc.yaml file found")
|
|
@@ -575,6 +584,7 @@ def build_docker(
|
|
|
575
584
|
_ = out[0].pop("Id")
|
|
576
585
|
_ = out[0].pop("RepoDigests")
|
|
577
586
|
_ = out[0].pop("Metadata")
|
|
587
|
+
_ = out[0].pop("DockerVersion")
|
|
578
588
|
return out
|
|
579
589
|
|
|
580
590
|
typer.echo(f"Checking for existing image with tag {tag}")
|
|
@@ -21,7 +21,7 @@ new_app = typer.Typer(no_args_is_help=True)
|
|
|
21
21
|
def new_figure(
|
|
22
22
|
path: str,
|
|
23
23
|
title: Annotated[str, typer.Option("--title")],
|
|
24
|
-
description: Annotated[str, typer.Option("--description")]
|
|
24
|
+
description: Annotated[str, typer.Option("--description")],
|
|
25
25
|
stage_name: Annotated[
|
|
26
26
|
str,
|
|
27
27
|
typer.Option(
|
|
@@ -71,6 +71,8 @@ def new_figure(
|
|
|
71
71
|
paths = [f.get("path") for f in figures]
|
|
72
72
|
if not overwrite and path in paths:
|
|
73
73
|
raise_error(f"Figure at path {path} already exists")
|
|
74
|
+
elif overwrite and path in paths:
|
|
75
|
+
figures = [fig for fig in figures if fig.get("path") != path]
|
|
74
76
|
if cmd is not None and stage_name is None:
|
|
75
77
|
raise_error("Stage name must be provided if command is specified")
|
|
76
78
|
if (deps or outs or outs_from_stage) and not cmd:
|
|
@@ -88,7 +90,13 @@ def new_figure(
|
|
|
88
90
|
stages = pipeline.get("stages", {})
|
|
89
91
|
if outs_from_stage not in stages:
|
|
90
92
|
raise_error(f"Stage {outs_from_stage} does not exist")
|
|
91
|
-
|
|
93
|
+
stage = stages[outs_from_stage]
|
|
94
|
+
if "foreach" in stage:
|
|
95
|
+
for val in stage["foreach"]:
|
|
96
|
+
for out in stage.get("do", {}).get("outs", []):
|
|
97
|
+
deps.append(out.replace("${item}", val))
|
|
98
|
+
else:
|
|
99
|
+
deps += stage.get("outs", [])
|
|
92
100
|
if path not in outs:
|
|
93
101
|
outs.append(path)
|
|
94
102
|
deps_cmd = []
|
|
@@ -99,6 +107,7 @@ def new_figure(
|
|
|
99
107
|
outs_cmd += ["-o", out]
|
|
100
108
|
subprocess.check_call(
|
|
101
109
|
["dvc", "stage", "add", "-n", stage_name]
|
|
110
|
+
+ (["-f"] if overwrite else [])
|
|
102
111
|
+ deps_cmd
|
|
103
112
|
+ outs_cmd
|
|
104
113
|
+ [cmd]
|
|
@@ -112,7 +121,8 @@ def new_figure(
|
|
|
112
121
|
repo.git.add("calkit.yaml")
|
|
113
122
|
if cmd:
|
|
114
123
|
repo.git.add("dvc.yaml")
|
|
115
|
-
repo.git.
|
|
124
|
+
if repo.git.diff("--staged"):
|
|
125
|
+
repo.git.commit(["-m", f"Add figure {path}"])
|
|
116
126
|
|
|
117
127
|
|
|
118
128
|
@new_app.command("question")
|
|
@@ -354,3 +364,111 @@ def new_foreach_stage(
|
|
|
354
364
|
repo.git.add("dvc.yaml")
|
|
355
365
|
if not no_commit and repo.git.diff("--staged"):
|
|
356
366
|
repo.git.commit(["-m", f"Add foreach stage {name}"])
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
@new_app.command(name="dataset")
|
|
370
|
+
def new_dataset(
|
|
371
|
+
path: str,
|
|
372
|
+
title: Annotated[str, typer.Option("--title")],
|
|
373
|
+
description: Annotated[str, typer.Option("--description")],
|
|
374
|
+
stage_name: Annotated[
|
|
375
|
+
str,
|
|
376
|
+
typer.Option(
|
|
377
|
+
"--stage",
|
|
378
|
+
help="Name of the pipeline stage that generates this dataset.",
|
|
379
|
+
),
|
|
380
|
+
] = None,
|
|
381
|
+
cmd: Annotated[
|
|
382
|
+
str,
|
|
383
|
+
typer.Option(
|
|
384
|
+
"--cmd", help="Command to add to the stage, if specified."
|
|
385
|
+
),
|
|
386
|
+
] = None,
|
|
387
|
+
deps: Annotated[
|
|
388
|
+
list[str], typer.Option("--dep", help="Path to stage dependency.")
|
|
389
|
+
] = [],
|
|
390
|
+
outs: Annotated[
|
|
391
|
+
list[str],
|
|
392
|
+
typer.Option(
|
|
393
|
+
"--out",
|
|
394
|
+
help=(
|
|
395
|
+
"Path to stage output. "
|
|
396
|
+
"Dataset path will be added automatically."
|
|
397
|
+
),
|
|
398
|
+
),
|
|
399
|
+
] = [],
|
|
400
|
+
outs_from_stage: Annotated[
|
|
401
|
+
str,
|
|
402
|
+
typer.Option(
|
|
403
|
+
"--deps-from-stage-outs",
|
|
404
|
+
help="Stage name from which to add outputs as dependencies.",
|
|
405
|
+
),
|
|
406
|
+
] = None,
|
|
407
|
+
no_commit: Annotated[bool, typer.Option("--no-commit")] = False,
|
|
408
|
+
overwrite: Annotated[
|
|
409
|
+
bool,
|
|
410
|
+
typer.Option(
|
|
411
|
+
"--overwrite",
|
|
412
|
+
"-f",
|
|
413
|
+
help="Overwrite existing dataset if one exists.",
|
|
414
|
+
),
|
|
415
|
+
] = False,
|
|
416
|
+
):
|
|
417
|
+
"""Create a new dataset."""
|
|
418
|
+
ck_info = calkit.load_calkit_info()
|
|
419
|
+
datasets = ck_info.get("datasets", [])
|
|
420
|
+
paths = [f.get("path") for f in datasets]
|
|
421
|
+
if not overwrite and path in paths:
|
|
422
|
+
raise_error(f"Dataset at path {path} already exists")
|
|
423
|
+
elif overwrite and path in paths:
|
|
424
|
+
datasets = [fig for fig in datasets if fig.get("path") != path]
|
|
425
|
+
if cmd is not None and stage_name is None:
|
|
426
|
+
raise_error("Stage name must be provided if command is specified")
|
|
427
|
+
if (deps or outs or outs_from_stage) and not cmd:
|
|
428
|
+
raise_error("Command must be provided")
|
|
429
|
+
if (deps or outs or outs_from_stage) and not stage_name:
|
|
430
|
+
raise_error("Stage name must be provided")
|
|
431
|
+
obj = dict(path=path, title=title)
|
|
432
|
+
if description is not None:
|
|
433
|
+
obj["description"] = description
|
|
434
|
+
if stage_name is not None:
|
|
435
|
+
obj["stage"] = stage_name
|
|
436
|
+
if cmd:
|
|
437
|
+
if outs_from_stage:
|
|
438
|
+
pipeline = calkit.dvc.read_pipeline()
|
|
439
|
+
stages = pipeline.get("stages", {})
|
|
440
|
+
if outs_from_stage not in stages:
|
|
441
|
+
raise_error(f"Stage {outs_from_stage} does not exist")
|
|
442
|
+
stage = stages[outs_from_stage]
|
|
443
|
+
if "foreach" in stage:
|
|
444
|
+
for val in stage["foreach"]:
|
|
445
|
+
for out in stage.get("do", {}).get("outs", []):
|
|
446
|
+
deps.append(out.replace("${item}", val))
|
|
447
|
+
else:
|
|
448
|
+
deps += stage.get("outs", [])
|
|
449
|
+
if path not in outs:
|
|
450
|
+
outs.append(path)
|
|
451
|
+
deps_cmd = []
|
|
452
|
+
for dep in deps:
|
|
453
|
+
deps_cmd += ["-d", dep]
|
|
454
|
+
outs_cmd = []
|
|
455
|
+
for out in outs:
|
|
456
|
+
outs_cmd += ["-o", out]
|
|
457
|
+
subprocess.check_call(
|
|
458
|
+
["dvc", "stage", "add", "-n", stage_name]
|
|
459
|
+
+ (["-f"] if overwrite else [])
|
|
460
|
+
+ deps_cmd
|
|
461
|
+
+ outs_cmd
|
|
462
|
+
+ [cmd]
|
|
463
|
+
)
|
|
464
|
+
datasets.append(obj)
|
|
465
|
+
ck_info["datasets"] = datasets
|
|
466
|
+
with open("calkit.yaml", "w") as f:
|
|
467
|
+
ryaml.dump(ck_info, f)
|
|
468
|
+
if not no_commit:
|
|
469
|
+
repo = git.Repo()
|
|
470
|
+
repo.git.add("calkit.yaml")
|
|
471
|
+
if cmd:
|
|
472
|
+
repo.git.add("dvc.yaml")
|
|
473
|
+
if repo.git.diff("--staged"):
|
|
474
|
+
repo.git.commit(["-m", f"Add dataset {path}"])
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""CLI for working with Office."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import platform
|
|
6
|
+
|
|
7
|
+
import docx2pdf
|
|
8
|
+
import typer
|
|
9
|
+
from typing_extensions import Annotated
|
|
10
|
+
|
|
11
|
+
import calkit
|
|
12
|
+
from calkit.cli import raise_error
|
|
13
|
+
|
|
14
|
+
office_app = typer.Typer(no_args_is_help=True)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@office_app.command(
|
|
18
|
+
name="excel-chart-to-png",
|
|
19
|
+
help="Extract a chart from Excel and save to PNG.",
|
|
20
|
+
)
|
|
21
|
+
def excel_chart_to_png(
|
|
22
|
+
input_fpath: Annotated[str, typer.Argument(help="Input Excel file path.")],
|
|
23
|
+
output_fpath: Annotated[str, typer.Argument(help="Output PNG file path.")],
|
|
24
|
+
sheet: Annotated[
|
|
25
|
+
int, typer.Option("--sheet", help="Sheet in workbook.")
|
|
26
|
+
] = 1,
|
|
27
|
+
chart_index: Annotated[
|
|
28
|
+
int, typer.Option("--chart-index", help="Chart index.")
|
|
29
|
+
] = 0,
|
|
30
|
+
):
|
|
31
|
+
if platform.system() != "Windows":
|
|
32
|
+
raise_error("This command is only available on Windows")
|
|
33
|
+
typer.echo(
|
|
34
|
+
f"Exporting chart at index {chart_index} from sheet {sheet} "
|
|
35
|
+
f"in {input_fpath} to {output_fpath}"
|
|
36
|
+
)
|
|
37
|
+
calkit.office.excel_chart_to_png(
|
|
38
|
+
input_fpath=input_fpath,
|
|
39
|
+
output_fpath=output_fpath,
|
|
40
|
+
sheet=sheet,
|
|
41
|
+
chart_index=chart_index,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@office_app.command(name="word-to-pdf", help="Convert a Word document to PDF.")
|
|
46
|
+
def word_to_pdf(
|
|
47
|
+
input_fpath: Annotated[str, typer.Argument(help="Input Excel file path.")],
|
|
48
|
+
output_fpath: Annotated[str, typer.Argument(help="Output PNG file path.")],
|
|
49
|
+
):
|
|
50
|
+
docx2pdf.convert(input_path=input_fpath, output_path=output_fpath)
|
|
@@ -7,7 +7,7 @@ from typing import Literal
|
|
|
7
7
|
|
|
8
8
|
import keyring
|
|
9
9
|
import yaml
|
|
10
|
-
from pydantic import
|
|
10
|
+
from pydantic import computed_field
|
|
11
11
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
12
12
|
|
|
13
13
|
|
|
@@ -40,7 +40,7 @@ class Settings(BaseSettings):
|
|
|
40
40
|
),
|
|
41
41
|
extra="ignore",
|
|
42
42
|
)
|
|
43
|
-
username:
|
|
43
|
+
username: str | None = None
|
|
44
44
|
token: str | None = None
|
|
45
45
|
dvc_token: str | None = None
|
|
46
46
|
dataframe_engine: Literal["pandas", "polars"] = "pandas"
|
|
@@ -46,8 +46,11 @@ def find_project_dirs(relative=False, max_depth=3) -> list[str]:
|
|
|
46
46
|
return final_res
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def load_calkit_info() -> dict:
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
def load_calkit_info(wdir=None) -> dict:
|
|
50
|
+
fpath = "calkit.yaml"
|
|
51
|
+
if wdir is not None:
|
|
52
|
+
fpath = os.path.join(wdir, fpath)
|
|
53
|
+
if os.path.isfile(fpath):
|
|
54
|
+
with open(fpath) as f:
|
|
52
55
|
return ryaml.load(f)
|
|
53
56
|
return {}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Functionality for working with Microsoft Office."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from PIL import ImageGrab
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def excel_chart_to_png(
|
|
9
|
+
input_fpath: str,
|
|
10
|
+
output_fpath: str,
|
|
11
|
+
sheet: int = 1,
|
|
12
|
+
chart_index: int = 0,
|
|
13
|
+
):
|
|
14
|
+
"""Export a chart from an Excel sheet to PNG."""
|
|
15
|
+
import win32com.client
|
|
16
|
+
|
|
17
|
+
# Open the excel application using win32com
|
|
18
|
+
excel = win32com.client.Dispatch("Excel.Application")
|
|
19
|
+
# Disable alerts and visibility to the user
|
|
20
|
+
excel.Visible = 0
|
|
21
|
+
excel.DisplayAlerts = 0
|
|
22
|
+
# Open workbook
|
|
23
|
+
wb = excel.Workbooks.Open(os.path.abspath(input_fpath))
|
|
24
|
+
factor = 1.0
|
|
25
|
+
# Extract sheet
|
|
26
|
+
sheet = excel.Sheets(sheet)
|
|
27
|
+
shape = sheet.Shapes[chart_index]
|
|
28
|
+
shape.Copy()
|
|
29
|
+
image = ImageGrab.grabclipboard()
|
|
30
|
+
length_x, width_y = image.size
|
|
31
|
+
size = int(factor * length_x), int(factor * width_y)
|
|
32
|
+
image_resize = image.resize(size)
|
|
33
|
+
# Save the image into the existing png file, overwriting if exists
|
|
34
|
+
image_resize.save(
|
|
35
|
+
os.path.abspath(output_fpath), "png", quality=95, dpi=(300, 300)
|
|
36
|
+
)
|
|
37
|
+
wb.Close(True)
|
|
38
|
+
excel.Quit()
|