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.
Files changed (50) hide show
  1. {calkit_python-0.3.3 → calkit_python-0.4.0}/PKG-INFO +10 -7
  2. {calkit_python-0.3.3 → calkit_python-0.4.0}/README.md +9 -6
  3. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/__init__.py +2 -1
  4. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/list.py +7 -0
  5. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/main.py +31 -12
  6. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/new.py +187 -0
  7. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/core.py +17 -3
  8. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/models.py +2 -0
  9. calkit_python-0.4.0/calkit/templates/__init__.py +1 -0
  10. calkit_python-0.4.0/calkit/templates/core.py +118 -0
  11. calkit_python-0.4.0/calkit/templates/latex/__init__.py +1 -0
  12. calkit_python-0.4.0/calkit/templates/latex/article/paper.tex +43 -0
  13. calkit_python-0.4.0/calkit/templates/latex/core.py +11 -0
  14. calkit_python-0.4.0/calkit/templates/latex/jfm/jfm.bst +1659 -0
  15. calkit_python-0.4.0/calkit/templates/latex/jfm/jfm.cls +1518 -0
  16. calkit_python-0.4.0/calkit/templates/latex/jfm/lineno-FLM.sty +113 -0
  17. calkit_python-0.4.0/calkit/templates/latex/jfm/paper.tex +468 -0
  18. calkit_python-0.4.0/calkit/templates/latex/jfm/upmath.sty +158 -0
  19. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/cli/test_list.py +4 -0
  20. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/cli/test_new.py +39 -0
  21. calkit_python-0.4.0/calkit/tests/test_templates.py +14 -0
  22. calkit_python-0.4.0/docs/tutorials/adding-latex-pub-docker.md +47 -0
  23. {calkit_python-0.3.3 → calkit_python-0.4.0}/.github/FUNDING.yml +0 -0
  24. {calkit_python-0.3.3 → calkit_python-0.4.0}/.github/workflows/publish-test.yml +0 -0
  25. {calkit_python-0.3.3 → calkit_python-0.4.0}/.github/workflows/publish.yml +0 -0
  26. {calkit_python-0.3.3 → calkit_python-0.4.0}/.gitignore +0 -0
  27. {calkit_python-0.3.3 → calkit_python-0.4.0}/LICENSE +0 -0
  28. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/__init__.py +0 -0
  29. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/config.py +0 -0
  30. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/core.py +0 -0
  31. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/import_.py +0 -0
  32. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/notebooks.py +0 -0
  33. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cli/office.py +0 -0
  34. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/cloud.py +0 -0
  35. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/config.py +0 -0
  36. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/data.py +0 -0
  37. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/docker.py +0 -0
  38. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/dvc.py +0 -0
  39. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/git.py +0 -0
  40. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/gui.py +0 -0
  41. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/jupyter.py +0 -0
  42. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/office.py +0 -0
  43. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/server.py +0 -0
  44. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/__init__.py +0 -0
  45. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/cli/__init__.py +0 -0
  46. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/cli/test_main.py +0 -0
  47. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/test_core.py +0 -0
  48. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/test_dvc.py +0 -0
  49. {calkit_python-0.3.3 → calkit_python-0.4.0}/calkit/tests/test_jupyter.py +0 -0
  50. {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.3
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) simplifies reproducibility,
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), and more,
38
- such that all all aspects of the research process can be fully described in a
39
- single repository.
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
- get the same results or outputs.
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) simplifies reproducibility,
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), and more,
7
- such that all all aspects of the research process can be fully described in a
8
- single repository.
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
- get the same results or outputs.
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.
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.3"
1
+ __version__ = "0.4.0"
2
2
 
3
3
  from .core import *
4
4
  from . import git
@@ -8,3 +8,4 @@ from . import jupyter
8
8
  from . import config
9
9
  from . import models
10
10
  from . import office
11
+ from . import templates
@@ -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
- cwd = os.getcwd()
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
- wdir = env.get("wdir", "/work")
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
- cmd = " ".join(cmd)
515
- cmd = [
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
- wdir,
540
+ docker_wdir,
522
541
  "-v",
523
- f"{cwd}:{wdir}",
542
+ f"{cwd}:{docker_wdir}",
524
543
  image_name,
525
- "bash",
544
+ shell,
526
545
  "-c",
527
- f"{cmd}",
546
+ f"{shell_cmd}",
528
547
  ]
529
548
  if verbose:
530
- typer.echo(f"Running command: {cmd}")
531
- subprocess.call(cmd)
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
- return ryaml.load(f)
56
- return {}
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
@@ -73,6 +73,8 @@ class DockerEnvironment(Environment):
73
73
  kind: str = "docker"
74
74
  image: str
75
75
  layers: list[str] | None = None
76
+ shell: Literal["bash", "sh"] = "sh"
77
+ platform: str | None = None
76
78
 
77
79
 
78
80
  class Software(BaseModel):
@@ -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}
@@ -0,0 +1,11 @@
1
+ """Core functionality for working with LaTeX templates."""
2
+
3
+ GITIGNORE = """
4
+ *.aux
5
+ *.fdb_latexmk
6
+ *.fls
7
+ *.log
8
+ *.pdf
9
+ *.out
10
+ *.DS_Store
11
+ """