calkit-python 0.3.3__tar.gz → 0.4.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.3.3 → calkit_python-0.4.0}/PKG-INFO +10 -7
- {calkit_python-0.3.3 → calkit_python-0.4.0}/README.md +9 -6
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/__init__.py +2 -1
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/list.py +7 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/main.py +31 -12
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/new.py +187 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/core.py +17 -3
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/models.py +2 -0
- calkit_python-0.4.0/calkit/templates/__init__.py +1 -0
- calkit_python-0.4.0/calkit/templates/core.py +118 -0
- calkit_python-0.4.0/calkit/templates/latex/__init__.py +1 -0
- calkit_python-0.4.0/calkit/templates/latex/article/paper.tex +43 -0
- calkit_python-0.4.0/calkit/templates/latex/core.py +11 -0
- calkit_python-0.4.0/calkit/templates/latex/jfm/jfm.bst +1659 -0
- calkit_python-0.4.0/calkit/templates/latex/jfm/jfm.cls +1518 -0
- calkit_python-0.4.0/calkit/templates/latex/jfm/lineno-FLM.sty +113 -0
- calkit_python-0.4.0/calkit/templates/latex/jfm/paper.tex +468 -0
- calkit_python-0.4.0/calkit/templates/latex/jfm/upmath.sty +158 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/cli/test_list.py +4 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/cli/test_new.py +39 -0
- calkit_python-0.4.0/calkit/tests/test_templates.py +14 -0
- calkit_python-0.4.0/docs/tutorials/adding-latex-pub-docker.md +47 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/.github/FUNDING.yml +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/.github/workflows/publish-test.yml +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/.github/workflows/publish.yml +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/.gitignore +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/LICENSE +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/config.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/core.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/import_.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/office.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cloud.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/config.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/data.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/docker.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/dvc.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/git.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/gui.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/jupyter.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/office.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/server.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/cli/test_main.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/test_dvc.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.3.3 → calkit_python-0.4.0}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: calkit-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.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
|
|
@@ -31,21 +31,23 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
# Calkit
|
|
33
33
|
|
|
34
|
-
[Calkit](https://calkit.io)
|
|
34
|
+
[Calkit](https://calkit.io) helps simplify reproducibility,
|
|
35
35
|
acting as a layer on top of
|
|
36
36
|
[Git](https://git-scm.com/), [DVC](https://dvc.org/),
|
|
37
|
-
[Docker](https://docker.com),
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
[Docker](https://docker.com),
|
|
38
|
+
and adds a domain-specific data model
|
|
39
|
+
such that all aspects of the research process can be fully described in a
|
|
40
|
+
single repository and therefore easily consumed by others.
|
|
40
41
|
|
|
41
42
|
## Tutorials
|
|
42
43
|
|
|
44
|
+
- [Microsoft Office (Word and Excel)](https://petebachant.me/office-repro/)
|
|
43
45
|
- [Reproducible OpenFOAM simulations](https://petebachant.me/reproducible-openfoam/)
|
|
44
46
|
|
|
45
47
|
## Why does reproducibility matter?
|
|
46
48
|
|
|
47
49
|
If your work is reproducible, that means that someone else can "run" it and
|
|
48
|
-
|
|
50
|
+
calculate the same results or outputs.
|
|
49
51
|
This is a major step towards addressing
|
|
50
52
|
[the replication crisis](https://en.wikipedia.org/wiki/Replication_crisis)
|
|
51
53
|
and has some major benefits for both you as an individual and the research
|
|
@@ -63,7 +65,8 @@ community:
|
|
|
63
65
|
## Why another tool/platform?
|
|
64
66
|
|
|
65
67
|
Git, GitHub, DVC, Docker et al. are amazing tools/platforms, but their
|
|
66
|
-
use involves multiple fairly difficult learning curves
|
|
68
|
+
use involves multiple fairly difficult learning curves,
|
|
69
|
+
and tying them together might mean developing something new for each project.
|
|
67
70
|
Our goal is to provide a single tool and platform to unify all of these so
|
|
68
71
|
that there is a single, gentle learning curve.
|
|
69
72
|
However, it is not our goal to hide or replace these underlying components.
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
# Calkit
|
|
2
2
|
|
|
3
|
-
[Calkit](https://calkit.io)
|
|
3
|
+
[Calkit](https://calkit.io) helps simplify reproducibility,
|
|
4
4
|
acting as a layer on top of
|
|
5
5
|
[Git](https://git-scm.com/), [DVC](https://dvc.org/),
|
|
6
|
-
[Docker](https://docker.com),
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
[Docker](https://docker.com),
|
|
7
|
+
and adds a domain-specific data model
|
|
8
|
+
such that all aspects of the research process can be fully described in a
|
|
9
|
+
single repository and therefore easily consumed by others.
|
|
9
10
|
|
|
10
11
|
## Tutorials
|
|
11
12
|
|
|
13
|
+
- [Microsoft Office (Word and Excel)](https://petebachant.me/office-repro/)
|
|
12
14
|
- [Reproducible OpenFOAM simulations](https://petebachant.me/reproducible-openfoam/)
|
|
13
15
|
|
|
14
16
|
## Why does reproducibility matter?
|
|
15
17
|
|
|
16
18
|
If your work is reproducible, that means that someone else can "run" it and
|
|
17
|
-
|
|
19
|
+
calculate the same results or outputs.
|
|
18
20
|
This is a major step towards addressing
|
|
19
21
|
[the replication crisis](https://en.wikipedia.org/wiki/Replication_crisis)
|
|
20
22
|
and has some major benefits for both you as an individual and the research
|
|
@@ -32,7 +34,8 @@ community:
|
|
|
32
34
|
## Why another tool/platform?
|
|
33
35
|
|
|
34
36
|
Git, GitHub, DVC, Docker et al. are amazing tools/platforms, but their
|
|
35
|
-
use involves multiple fairly difficult learning curves
|
|
37
|
+
use involves multiple fairly difficult learning curves,
|
|
38
|
+
and tying them together might mean developing something new for each project.
|
|
36
39
|
Our goal is to provide a single tool and platform to unify all of these so
|
|
37
40
|
that there is a single, gentle learning curve.
|
|
38
41
|
However, it is not our goal to hide or replace these underlying components.
|
|
@@ -77,3 +77,10 @@ def list_environments():
|
|
|
77
77
|
typer.echo(name + ":")
|
|
78
78
|
for k, v in env.items():
|
|
79
79
|
typer.echo(f" {k}: {v}")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@list_app.command(name="templates")
|
|
83
|
+
def list_templates():
|
|
84
|
+
for kind, tpl_dict in calkit.templates.TEMPLATES.items():
|
|
85
|
+
for name in tpl_dict:
|
|
86
|
+
typer.echo(f"{kind}/{name}")
|
|
@@ -481,11 +481,21 @@ def run_in_env(
|
|
|
481
481
|
),
|
|
482
482
|
),
|
|
483
483
|
] = None,
|
|
484
|
+
wdir: Annotated[
|
|
485
|
+
str,
|
|
486
|
+
typer.Option(
|
|
487
|
+
"--wdir",
|
|
488
|
+
help=(
|
|
489
|
+
"Working directory. "
|
|
490
|
+
"By default will run current working directory."
|
|
491
|
+
),
|
|
492
|
+
),
|
|
493
|
+
] = None,
|
|
484
494
|
verbose: Annotated[
|
|
485
495
|
bool, typer.Option("--verbose", "-v", help="Print verbose output.")
|
|
486
496
|
] = False,
|
|
487
497
|
):
|
|
488
|
-
ck_info = calkit.load_calkit_info()
|
|
498
|
+
ck_info = calkit.load_calkit_info(process_includes=True)
|
|
489
499
|
envs = ck_info.get("environments", {})
|
|
490
500
|
if not envs:
|
|
491
501
|
raise_error("No environments defined in calkit.yaml")
|
|
@@ -507,33 +517,42 @@ def run_in_env(
|
|
|
507
517
|
if env_name is None:
|
|
508
518
|
raise_error("Environment must be specified if there are multiple")
|
|
509
519
|
env = envs[env_name]
|
|
510
|
-
|
|
520
|
+
if wdir is not None:
|
|
521
|
+
cwd = os.path.abspath(wdir)
|
|
522
|
+
else:
|
|
523
|
+
cwd = os.getcwd()
|
|
511
524
|
image_name = env.get("image", env_name)
|
|
512
|
-
|
|
525
|
+
docker_wdir = env.get("wdir", "/work")
|
|
526
|
+
shell = env.get("shell", "sh")
|
|
527
|
+
platform = env.get("platform")
|
|
513
528
|
if env["kind"] == "docker":
|
|
514
|
-
|
|
515
|
-
|
|
529
|
+
shell_cmd = " ".join(cmd)
|
|
530
|
+
docker_cmd = [
|
|
516
531
|
"docker",
|
|
517
532
|
"run",
|
|
533
|
+
]
|
|
534
|
+
if platform:
|
|
535
|
+
docker_cmd += ["--platform", platform]
|
|
536
|
+
docker_cmd += [
|
|
518
537
|
"-it" if sys.stdin.isatty() else "-i",
|
|
519
538
|
"--rm",
|
|
520
539
|
"-w",
|
|
521
|
-
|
|
540
|
+
docker_wdir,
|
|
522
541
|
"-v",
|
|
523
|
-
f"{cwd}:{
|
|
542
|
+
f"{cwd}:{docker_wdir}",
|
|
524
543
|
image_name,
|
|
525
|
-
|
|
544
|
+
shell,
|
|
526
545
|
"-c",
|
|
527
|
-
f"{
|
|
546
|
+
f"{shell_cmd}",
|
|
528
547
|
]
|
|
529
548
|
if verbose:
|
|
530
|
-
typer.echo(f"Running command: {
|
|
531
|
-
subprocess.call(
|
|
549
|
+
typer.echo(f"Running command: {docker_cmd}")
|
|
550
|
+
subprocess.call(docker_cmd, cwd=wdir)
|
|
532
551
|
elif env["kind"] == "conda":
|
|
533
552
|
cmd = ["conda", "run", "-n", env_name] + cmd
|
|
534
553
|
if verbose:
|
|
535
554
|
typer.echo(f"Running command: {cmd}")
|
|
536
|
-
subprocess.call(cmd)
|
|
555
|
+
subprocess.call(cmd, cwd=wdir)
|
|
537
556
|
else:
|
|
538
557
|
raise_error("Environment kind not supported")
|
|
539
558
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
import shutil
|
|
6
7
|
import subprocess
|
|
7
8
|
|
|
8
9
|
import git
|
|
@@ -472,3 +473,189 @@ def new_dataset(
|
|
|
472
473
|
repo.git.add("dvc.yaml")
|
|
473
474
|
if repo.git.diff("--staged"):
|
|
474
475
|
repo.git.commit(["-m", f"Add dataset {path}"])
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
@new_app.command(name="publication", help="Create a new publication.")
|
|
479
|
+
def new_publication(
|
|
480
|
+
path: Annotated[
|
|
481
|
+
str,
|
|
482
|
+
typer.Argument(
|
|
483
|
+
help=(
|
|
484
|
+
"Path for the publication. "
|
|
485
|
+
"If using a template, this could be a directory."
|
|
486
|
+
)
|
|
487
|
+
),
|
|
488
|
+
],
|
|
489
|
+
title: Annotated[
|
|
490
|
+
str, typer.Option("--title", help="The title of the publication.")
|
|
491
|
+
],
|
|
492
|
+
description: Annotated[
|
|
493
|
+
str,
|
|
494
|
+
typer.Option(
|
|
495
|
+
"--description", help="A description of the publication."
|
|
496
|
+
),
|
|
497
|
+
],
|
|
498
|
+
kind: Annotated[
|
|
499
|
+
str,
|
|
500
|
+
typer.Option(
|
|
501
|
+
"--kind", help="Kind of the publication, e.g., 'journal-article'."
|
|
502
|
+
),
|
|
503
|
+
],
|
|
504
|
+
stage_name: Annotated[
|
|
505
|
+
str,
|
|
506
|
+
typer.Option(
|
|
507
|
+
"--stage",
|
|
508
|
+
help="Name of the pipeline stage to build the output file.",
|
|
509
|
+
),
|
|
510
|
+
] = None,
|
|
511
|
+
deps: Annotated[
|
|
512
|
+
list[str], typer.Option("--dep", help="Path to stage dependency.")
|
|
513
|
+
] = [],
|
|
514
|
+
outs_from_stage: Annotated[
|
|
515
|
+
str,
|
|
516
|
+
typer.Option(
|
|
517
|
+
"--deps-from-stage-outs",
|
|
518
|
+
help="Stage name from which to add outputs as dependencies.",
|
|
519
|
+
),
|
|
520
|
+
] = None,
|
|
521
|
+
template: Annotated[
|
|
522
|
+
str,
|
|
523
|
+
typer.Option(
|
|
524
|
+
"--template",
|
|
525
|
+
help=(
|
|
526
|
+
"Template with which to create the source files. "
|
|
527
|
+
"Should be in the format {type}/{name}."
|
|
528
|
+
),
|
|
529
|
+
),
|
|
530
|
+
] = None,
|
|
531
|
+
env_name: Annotated[
|
|
532
|
+
str,
|
|
533
|
+
typer.Option(
|
|
534
|
+
"--environment",
|
|
535
|
+
help="Name of the build environment to create, if desired.",
|
|
536
|
+
),
|
|
537
|
+
] = None,
|
|
538
|
+
no_commit: Annotated[
|
|
539
|
+
bool,
|
|
540
|
+
typer.Option(
|
|
541
|
+
"--no-commit", help="Do not commit resulting changes to the repo."
|
|
542
|
+
),
|
|
543
|
+
] = False,
|
|
544
|
+
overwrite: Annotated[
|
|
545
|
+
bool,
|
|
546
|
+
typer.Option(
|
|
547
|
+
"--overwrite",
|
|
548
|
+
"-f",
|
|
549
|
+
help="Overwrite existing objects if they already exist.",
|
|
550
|
+
),
|
|
551
|
+
] = False,
|
|
552
|
+
):
|
|
553
|
+
ck_info = calkit.load_calkit_info(process_includes=False)
|
|
554
|
+
pubs = ck_info.get("publications", [])
|
|
555
|
+
envs = ck_info.get("environments", {})
|
|
556
|
+
pub_paths = [p.get("path") for p in pubs]
|
|
557
|
+
if template is not None:
|
|
558
|
+
template_type, _ = template.split("/")
|
|
559
|
+
else:
|
|
560
|
+
template_type = None
|
|
561
|
+
# Check all of our inputs
|
|
562
|
+
if template_type not in ["latex"]:
|
|
563
|
+
raise_error(f"Unknown template type '{template_type}'")
|
|
564
|
+
if env_name is not None and template_type != "latex":
|
|
565
|
+
raise_error("Environments can only be created for latex templates")
|
|
566
|
+
if env_name is not None and env_name in envs and not overwrite:
|
|
567
|
+
raise_error(f"Environment '{env_name}' already exists")
|
|
568
|
+
if template_type is not None:
|
|
569
|
+
try:
|
|
570
|
+
template_obj = calkit.templates.get_template(template)
|
|
571
|
+
except ValueError:
|
|
572
|
+
raise_error(f"Template '{template}' does not exist")
|
|
573
|
+
# Parse outs from stage if specified
|
|
574
|
+
if outs_from_stage:
|
|
575
|
+
pipeline = calkit.dvc.read_pipeline()
|
|
576
|
+
stages = pipeline.get("stages", {})
|
|
577
|
+
if outs_from_stage not in stages:
|
|
578
|
+
raise_error(f"Stage {outs_from_stage} does not exist")
|
|
579
|
+
stage = stages[outs_from_stage]
|
|
580
|
+
if "foreach" in stage:
|
|
581
|
+
for val in stage["foreach"]:
|
|
582
|
+
for out in stage.get("do", {}).get("outs", []):
|
|
583
|
+
deps.append(out.replace("${item}", val))
|
|
584
|
+
else:
|
|
585
|
+
deps += stage.get("outs", [])
|
|
586
|
+
# Create publication object
|
|
587
|
+
if template_type == "latex":
|
|
588
|
+
pub_fpath = os.path.join(
|
|
589
|
+
path, template_obj.target.removesuffix(".tex") + ".pdf"
|
|
590
|
+
)
|
|
591
|
+
else:
|
|
592
|
+
pub_fpath = path
|
|
593
|
+
if not overwrite and pub_fpath in pub_paths:
|
|
594
|
+
raise_error(f"Publication with path {pub_fpath} already exists")
|
|
595
|
+
elif overwrite and pub_fpath in pub_paths:
|
|
596
|
+
pubs = [p for p in pubs if p.get("path") != pub_fpath]
|
|
597
|
+
pub = dict(
|
|
598
|
+
path=pub_fpath,
|
|
599
|
+
kind=kind,
|
|
600
|
+
title=title,
|
|
601
|
+
description=description,
|
|
602
|
+
stage=stage_name,
|
|
603
|
+
)
|
|
604
|
+
pubs.append(pub)
|
|
605
|
+
ck_info["publications"] = pubs
|
|
606
|
+
repo = git.Repo()
|
|
607
|
+
# Create environment if applicable
|
|
608
|
+
if env_name is not None and template_type == "latex":
|
|
609
|
+
env_path = f".calkit/environments/{env_name}.yaml"
|
|
610
|
+
os.makedirs(".calkit/environments", exist_ok=True)
|
|
611
|
+
env = {"_include": env_path}
|
|
612
|
+
envs[env_name] = env
|
|
613
|
+
env_remote = dict(
|
|
614
|
+
kind="docker",
|
|
615
|
+
image="kjarosh/latex:2024.4",
|
|
616
|
+
description="TeXlive full from kjarosh.",
|
|
617
|
+
platform="linux/amd64",
|
|
618
|
+
)
|
|
619
|
+
with open(env_path, "w") as f:
|
|
620
|
+
calkit.ryaml.dump(env_remote, f)
|
|
621
|
+
ck_info["environments"] = envs
|
|
622
|
+
repo.git.add(env_path)
|
|
623
|
+
with open("calkit.yaml", "w") as f:
|
|
624
|
+
calkit.ryaml.dump(ck_info, f)
|
|
625
|
+
repo.git.add("calkit.yaml")
|
|
626
|
+
# Copy in template files if applicable
|
|
627
|
+
if template_type == "latex":
|
|
628
|
+
if overwrite and os.path.exists(path):
|
|
629
|
+
shutil.rmtree(path)
|
|
630
|
+
calkit.templates.use_template(
|
|
631
|
+
name=template, dest_dir=path, title=title
|
|
632
|
+
)
|
|
633
|
+
repo.git.add(path)
|
|
634
|
+
# Create stage if applicable
|
|
635
|
+
if stage_name is not None and template_type == "latex":
|
|
636
|
+
cmd = f"cd {path} && latexmk -pdf {template_obj.target}"
|
|
637
|
+
if env_name is not None:
|
|
638
|
+
cmd = f'calkit runenv -n {env_name} "{cmd}"'
|
|
639
|
+
target_dep = os.path.join(path, template_obj.target)
|
|
640
|
+
dvc_cmd = [
|
|
641
|
+
"dvc",
|
|
642
|
+
"stage",
|
|
643
|
+
"add",
|
|
644
|
+
"-n",
|
|
645
|
+
stage_name,
|
|
646
|
+
"-o",
|
|
647
|
+
pub_fpath,
|
|
648
|
+
"-d",
|
|
649
|
+
target_dep,
|
|
650
|
+
]
|
|
651
|
+
if env_name is not None:
|
|
652
|
+
dvc_cmd += ["-d", env_path]
|
|
653
|
+
for dep in deps:
|
|
654
|
+
dvc_cmd += ["-d", dep]
|
|
655
|
+
if overwrite:
|
|
656
|
+
dvc_cmd.append("-f")
|
|
657
|
+
dvc_cmd.append(cmd)
|
|
658
|
+
subprocess.check_call(dvc_cmd)
|
|
659
|
+
repo.git.add("dvc.yaml")
|
|
660
|
+
if not no_commit and repo.git.diff("--staged"):
|
|
661
|
+
repo.git.commit(["-m", f"Add new publication {pub_fpath}"])
|
|
@@ -46,11 +46,25 @@ 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(wdir=None) -> dict:
|
|
49
|
+
def load_calkit_info(wdir=None, process_includes=False) -> dict:
|
|
50
|
+
"""Load Calkit project information."""
|
|
51
|
+
info = {}
|
|
50
52
|
fpath = "calkit.yaml"
|
|
51
53
|
if wdir is not None:
|
|
52
54
|
fpath = os.path.join(wdir, fpath)
|
|
53
55
|
if os.path.isfile(fpath):
|
|
54
56
|
with open(fpath) as f:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
info = ryaml.load(f)
|
|
58
|
+
# Check for any includes, i.e., entities with an _include key, for which
|
|
59
|
+
# we should merge in another file
|
|
60
|
+
# Currently this is only supported with environments because they may need
|
|
61
|
+
# to be tracked as DVC dependencies
|
|
62
|
+
if process_includes:
|
|
63
|
+
if "environments" in info:
|
|
64
|
+
for env_name, env in info["environments"].items():
|
|
65
|
+
if "_include" in env:
|
|
66
|
+
include_fpath = env.pop("_include")
|
|
67
|
+
with open(include_fpath) as f:
|
|
68
|
+
include_data = ryaml.load(f)
|
|
69
|
+
info["environments"][env_name] |= include_data
|
|
70
|
+
return info
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .core import *
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Core functionality for working with templates."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from calkit.templates.latex import GITIGNORE as LATEX_GITIGNORE
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Template(BaseModel):
|
|
13
|
+
"""Model for a template.
|
|
14
|
+
|
|
15
|
+
Defines what kind of template it is, its name, and if/how we should allow
|
|
16
|
+
for replacing strings in the template in the process of copying it to a
|
|
17
|
+
project, e.g., if it's a LaTeX template and we want to automatically
|
|
18
|
+
set the title.
|
|
19
|
+
|
|
20
|
+
Attributes
|
|
21
|
+
----------
|
|
22
|
+
kind : string
|
|
23
|
+
What kind of template is this.
|
|
24
|
+
name : string
|
|
25
|
+
Kebab-case name of the template.
|
|
26
|
+
loc : string
|
|
27
|
+
Location of the template. If not provided, will be inferred from the
|
|
28
|
+
kind and name.
|
|
29
|
+
files: list of strings
|
|
30
|
+
Files to copy. If not specified, will copy all in the directory.
|
|
31
|
+
gitignore : string
|
|
32
|
+
Content to put into the ``.gitignore`` file.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
kind: Literal["latex"]
|
|
36
|
+
name: str
|
|
37
|
+
loc: str | None = None # Can be local (auto-detected by path) or URL
|
|
38
|
+
files: list[str] | None = None # Which filenames should be copied
|
|
39
|
+
gitignore: str | None = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LatexTemplate(Template):
|
|
43
|
+
kind: str = "latex"
|
|
44
|
+
target: str = "paper.tex"
|
|
45
|
+
gitignore: str = LATEX_GITIGNORE
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# A registry of available templates, keyed by their kind and subkeyed by their
|
|
49
|
+
# name
|
|
50
|
+
TEMPLATES = {
|
|
51
|
+
"latex": {
|
|
52
|
+
"article": LatexTemplate(name="article"),
|
|
53
|
+
"jfm": LatexTemplate(name="jfm"),
|
|
54
|
+
},
|
|
55
|
+
"project": {},
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_template(name: str) -> Template:
|
|
60
|
+
"""Get a template by name, which should include its namespace or type."""
|
|
61
|
+
template_type, template_name = name.split("/")
|
|
62
|
+
if template_type not in TEMPLATES:
|
|
63
|
+
raise ValueError(f"Unknown template type '{template_type}'")
|
|
64
|
+
templates = TEMPLATES[template_type]
|
|
65
|
+
if template_name not in templates:
|
|
66
|
+
raise ValueError(f"Unknown template name '{template_name}'")
|
|
67
|
+
return templates[template_name]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def use_template(name: str, dest_dir: str, **kwargs):
|
|
71
|
+
"""Copy template files into ``dest_dir``.
|
|
72
|
+
|
|
73
|
+
The destination directory must be empty if it exists.
|
|
74
|
+
"""
|
|
75
|
+
template = get_template(name)
|
|
76
|
+
if template.loc is None:
|
|
77
|
+
loc = os.path.join(
|
|
78
|
+
os.path.dirname(os.path.abspath(__file__)),
|
|
79
|
+
template.kind,
|
|
80
|
+
template.name,
|
|
81
|
+
)
|
|
82
|
+
print(loc)
|
|
83
|
+
else:
|
|
84
|
+
loc = template.loc
|
|
85
|
+
if loc.startswith("http://") or loc.startswith("https://"):
|
|
86
|
+
raise NotImplementedError("Remote template support not implemented")
|
|
87
|
+
files = template.files
|
|
88
|
+
if files is None:
|
|
89
|
+
files = os.listdir(loc)
|
|
90
|
+
if isinstance(template, LatexTemplate) and template.target not in files:
|
|
91
|
+
files.append(template.target)
|
|
92
|
+
if os.path.exists(dest_dir):
|
|
93
|
+
if os.path.isfile(dest_dir):
|
|
94
|
+
raise ValueError("Destination directory already exists as a file")
|
|
95
|
+
if os.listdir(dest_dir):
|
|
96
|
+
raise ValueError("Destination directory must be empty")
|
|
97
|
+
else:
|
|
98
|
+
os.makedirs(dest_dir)
|
|
99
|
+
# Copy files into destination
|
|
100
|
+
for fname in files:
|
|
101
|
+
fpath = os.path.join(loc, fname)
|
|
102
|
+
shutil.copy(src=fpath, dst=dest_dir)
|
|
103
|
+
# Write gitignore if applicable
|
|
104
|
+
if template.gitignore is not None:
|
|
105
|
+
with open(os.path.join(dest_dir, ".gitignore"), "w") as f:
|
|
106
|
+
f.write(template.gitignore)
|
|
107
|
+
# If there's a title in kwargs and we're using a LaTeX template,
|
|
108
|
+
# replace that line
|
|
109
|
+
if isinstance(template, LatexTemplate) and "title" in kwargs:
|
|
110
|
+
with open(os.path.join(dest_dir, template.target)) as f:
|
|
111
|
+
lines = f.readlines()
|
|
112
|
+
txt = ""
|
|
113
|
+
for line in lines:
|
|
114
|
+
if line.strip().startswith(r"\title{"):
|
|
115
|
+
line = r"\title{" + kwargs["title"] + "}\n"
|
|
116
|
+
txt += line
|
|
117
|
+
with open(os.path.join(dest_dir, template.target), "w") as f:
|
|
118
|
+
f.write(txt)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .core import *
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
% A generic article template
|
|
2
|
+
\documentclass[11pt]{article}
|
|
3
|
+
|
|
4
|
+
\usepackage{sectsty}
|
|
5
|
+
\usepackage{graphicx}
|
|
6
|
+
|
|
7
|
+
% Margins
|
|
8
|
+
\topmargin=-0.45in
|
|
9
|
+
\evensidemargin=0in
|
|
10
|
+
\oddsidemargin=0in
|
|
11
|
+
\textwidth=6.5in
|
|
12
|
+
\textheight=9.0in
|
|
13
|
+
\headsep=0.25in
|
|
14
|
+
|
|
15
|
+
\title{This is the paper}
|
|
16
|
+
\author{ The Author }
|
|
17
|
+
\date{\today}
|
|
18
|
+
|
|
19
|
+
\begin{document}
|
|
20
|
+
\maketitle
|
|
21
|
+
\pagebreak
|
|
22
|
+
|
|
23
|
+
% Optional TOC
|
|
24
|
+
% \tableofcontents
|
|
25
|
+
% \pagebreak
|
|
26
|
+
|
|
27
|
+
%--Paper--
|
|
28
|
+
|
|
29
|
+
\section{Section 1}
|
|
30
|
+
|
|
31
|
+
Lorem Ipsum
|
|
32
|
+
|
|
33
|
+
% \includegraphics{../figures/my-figure.png}
|
|
34
|
+
|
|
35
|
+
\pagebreak
|
|
36
|
+
|
|
37
|
+
\section{Section 2}
|
|
38
|
+
|
|
39
|
+
Lorem Ipsum \\
|
|
40
|
+
|
|
41
|
+
%--/Paper--
|
|
42
|
+
|
|
43
|
+
\end{document}
|