calkit-python 0.19.0__tar.gz → 0.20.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.19.0 → calkit_python-0.20.0}/PKG-INFO +3 -2
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/__init__.py +4 -1
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/main.py +10 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/new.py +447 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/config.py +9 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/dvc.py +9 -2
- calkit_python-0.20.0/calkit/github.py +70 -0
- calkit_python-0.20.0/calkit/releases.py +159 -0
- calkit_python-0.20.0/calkit/zenodo.py +82 -0
- calkit_python-0.20.0/docs/img/connect-zenodo.png +0 -0
- calkit_python-0.20.0/docs/releases.md +86 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/existing-project.md +74 -28
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/version-control.md +6 -1
- {calkit_python-0.19.0 → calkit_python-0.20.0}/mkdocs.yml +1 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/pyproject.toml +2 -1
- {calkit_python-0.19.0 → calkit_python-0.20.0}/.github/FUNDING.yml +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/.github/workflows/docs.yml +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/.github/workflows/publish-test.yml +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/.github/workflows/publish.yml +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/.gitignore +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/LICENSE +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/README.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/__main__.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/calc.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/check.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/check.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/config.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/core.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/import_.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/list.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/office.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cli/update.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/cloud.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/conda.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/core.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/datasets.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/docker.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/git.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/gui.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/jupyter.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/magics.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/models.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/office.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/ops.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/server.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/__init__.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/core.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/latex/__init__.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/latex/article/paper.tex +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/latex/core.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/latex/jfm/jfm.bst +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/latex/jfm/jfm.cls +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/latex/jfm/paper.tex +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/templates/latex/jfm/upmath.sty +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/cli/test_list.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/cli/test_main.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/cli/test_new.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/test_calc.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/test_check.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/test_conda.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/test_dvc.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/test_magics.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/calkit/tests/test_templates.py +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/CNAME +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/apps.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/calculations.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/calkit-yaml.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/cli-reference.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/cloud-integration.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/datasets.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/environments.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/examples.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/help.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/img/c-to-the-k-white.svg +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/img/calkit-no-bg.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/index.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/installation.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/local-server.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/pipeline/index.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/pipeline/manual-steps.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/references.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/adding-latex-pub-docker.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/conda-envs.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/first-project.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/push.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/chart-more-rows.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/create-project.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/excel-chart.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/excel-data.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/needs-clone.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/new-stage.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/status-more-rows.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/untracked-data.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/updated-publication.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/office/workflow-page.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/openfoam/clone.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/openfoam/create-project.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/openfoam/new-token.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/openfoam/reclone.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/img/run-proc.png +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/latex-codespaces.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/matlab.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/notebook-pipeline.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/office.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/openfoam.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/docs/tutorials/procedures.md +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/test/pipeline.ipynb +0 -0
- {calkit_python-0.19.0 → calkit_python-0.20.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: calkit-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.0
|
|
4
4
|
Summary: Reproducibility simplified.
|
|
5
5
|
Project-URL: Homepage, https://calkit.org
|
|
6
6
|
Project-URL: Issues, https://github.com/calkit/calkit/issues
|
|
@@ -12,9 +12,10 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Requires-Python: >=3.8
|
|
14
14
|
Requires-Dist: arithmeval
|
|
15
|
+
Requires-Dist: bibtexparser
|
|
15
16
|
Requires-Dist: checksumdir
|
|
16
17
|
Requires-Dist: docx2pdf
|
|
17
|
-
Requires-Dist: dvc
|
|
18
|
+
Requires-Dist: dvc>=3.59.0
|
|
18
19
|
Requires-Dist: eval-type-backport; python_version < '3.10'
|
|
19
20
|
Requires-Dist: fastapi
|
|
20
21
|
Requires-Dist: gitpython
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.20.0"
|
|
2
2
|
|
|
3
3
|
from .core import *
|
|
4
4
|
from . import git
|
|
@@ -12,3 +12,6 @@ from . import templates
|
|
|
12
12
|
from . import conda
|
|
13
13
|
from . import calc
|
|
14
14
|
from . import check
|
|
15
|
+
from . import github
|
|
16
|
+
from . import zenodo
|
|
17
|
+
from . import releases
|
|
@@ -536,6 +536,16 @@ def push(
|
|
|
536
536
|
raise_error("DVC push failed")
|
|
537
537
|
|
|
538
538
|
|
|
539
|
+
@app.command(name="sync")
|
|
540
|
+
def sync(
|
|
541
|
+
no_check_auth: Annotated[bool, typer.Option("--no-check-auth")] = False,
|
|
542
|
+
):
|
|
543
|
+
"""Sync the project repo by pulling and then pushing."""
|
|
544
|
+
# TODO: Walk users through merge conflicts if they arise
|
|
545
|
+
pull(no_check_auth=no_check_auth)
|
|
546
|
+
push(no_check_auth=no_check_auth)
|
|
547
|
+
|
|
548
|
+
|
|
539
549
|
@app.command(name="ignore")
|
|
540
550
|
def ignore(
|
|
541
551
|
path: Annotated[str, typer.Argument(help="Path to ignore.")],
|
|
@@ -6,9 +6,13 @@ import csv
|
|
|
6
6
|
import os
|
|
7
7
|
import shutil
|
|
8
8
|
import subprocess
|
|
9
|
+
import zipfile
|
|
9
10
|
from enum import Enum
|
|
10
11
|
|
|
12
|
+
import bibtexparser
|
|
13
|
+
import dotenv
|
|
11
14
|
import git
|
|
15
|
+
import requests
|
|
12
16
|
import typer
|
|
13
17
|
from git.exc import GitCommandError, InvalidGitRepositoryError
|
|
14
18
|
from typing_extensions import Annotated
|
|
@@ -1434,3 +1438,446 @@ def new_stage(
|
|
|
1434
1438
|
repo.git.commit(
|
|
1435
1439
|
["dvc.yaml", "-m", f"Add {kind.value} pipeline stage '{name}'"]
|
|
1436
1440
|
)
|
|
1441
|
+
|
|
1442
|
+
|
|
1443
|
+
@new_app.command(name="release")
|
|
1444
|
+
def new_release(
|
|
1445
|
+
name: Annotated[
|
|
1446
|
+
str,
|
|
1447
|
+
typer.Option(
|
|
1448
|
+
"--name",
|
|
1449
|
+
"-n",
|
|
1450
|
+
help=(
|
|
1451
|
+
"A name for the release, typically kebab-case. "
|
|
1452
|
+
"Will be used for the Git tag and GitHub release title."
|
|
1453
|
+
),
|
|
1454
|
+
),
|
|
1455
|
+
],
|
|
1456
|
+
release_type: Annotated[
|
|
1457
|
+
str, typer.Option("--kind", help="What kind of release to create.")
|
|
1458
|
+
] = "project",
|
|
1459
|
+
path: Annotated[
|
|
1460
|
+
str,
|
|
1461
|
+
typer.Argument(help="The path to release; '.' for a project release."),
|
|
1462
|
+
] = ".",
|
|
1463
|
+
description: Annotated[
|
|
1464
|
+
str,
|
|
1465
|
+
typer.Option(
|
|
1466
|
+
"--description",
|
|
1467
|
+
"--desc",
|
|
1468
|
+
help=(
|
|
1469
|
+
"A description of the release. "
|
|
1470
|
+
"Will be auto-generated if not provided."
|
|
1471
|
+
),
|
|
1472
|
+
),
|
|
1473
|
+
] = None,
|
|
1474
|
+
release_date: Annotated[
|
|
1475
|
+
str,
|
|
1476
|
+
typer.Option("--date", help="Release date. Will default to today."),
|
|
1477
|
+
] = None,
|
|
1478
|
+
dry_run: Annotated[
|
|
1479
|
+
bool,
|
|
1480
|
+
typer.Option(
|
|
1481
|
+
"--dry-run",
|
|
1482
|
+
help="Only print actions that would be taken but don't take them.",
|
|
1483
|
+
),
|
|
1484
|
+
] = False,
|
|
1485
|
+
no_commit: Annotated[
|
|
1486
|
+
bool,
|
|
1487
|
+
typer.Option(
|
|
1488
|
+
"--no-commit",
|
|
1489
|
+
help="Do not commit changes to Git repo.",
|
|
1490
|
+
),
|
|
1491
|
+
] = False,
|
|
1492
|
+
no_push: Annotated[
|
|
1493
|
+
bool,
|
|
1494
|
+
typer.Option(
|
|
1495
|
+
"--no-push",
|
|
1496
|
+
help="Do not push to Git remote.",
|
|
1497
|
+
),
|
|
1498
|
+
] = False,
|
|
1499
|
+
):
|
|
1500
|
+
"""Create a new release."""
|
|
1501
|
+
if release_type not in [
|
|
1502
|
+
"project",
|
|
1503
|
+
"publication",
|
|
1504
|
+
"figure",
|
|
1505
|
+
"dataset",
|
|
1506
|
+
"software",
|
|
1507
|
+
]:
|
|
1508
|
+
raise_error(f"Unknown release type '{release_type}'")
|
|
1509
|
+
# TODO: Check path is consistent with release type
|
|
1510
|
+
dotenv.load_dotenv()
|
|
1511
|
+
# First see if we have a Zenodo token
|
|
1512
|
+
typer.echo("Checking for Zenodo token")
|
|
1513
|
+
try:
|
|
1514
|
+
token = calkit.zenodo.get_token()
|
|
1515
|
+
except Exception as e:
|
|
1516
|
+
raise_error(e)
|
|
1517
|
+
ck_info = calkit.load_calkit_info()
|
|
1518
|
+
releases = ck_info.get("releases", {})
|
|
1519
|
+
# TODO: Enable resuming a release if upload failed part-way?
|
|
1520
|
+
if name in releases:
|
|
1521
|
+
raise_error(f"Release with name '{name}' already exists")
|
|
1522
|
+
repo = git.Repo()
|
|
1523
|
+
if name in repo.tags:
|
|
1524
|
+
raise_error(f"Git tag with name '{name}' already exists")
|
|
1525
|
+
release_dir = f".calkit/releases/{name}"
|
|
1526
|
+
release_files_dir = release_dir + "/files"
|
|
1527
|
+
os.makedirs(release_files_dir, exist_ok=True)
|
|
1528
|
+
# Ignore release files dir
|
|
1529
|
+
typer.echo(f"Ignoring {release_files_dir}")
|
|
1530
|
+
gitignore_path = release_dir + "/.gitignore"
|
|
1531
|
+
with open(gitignore_path, "w") as f:
|
|
1532
|
+
f.write("/files\n")
|
|
1533
|
+
if not dry_run:
|
|
1534
|
+
repo.git.add(gitignore_path)
|
|
1535
|
+
if release_date is None:
|
|
1536
|
+
release_date = str(calkit.utcnow().date())
|
|
1537
|
+
typer.echo(f"Using release date: {release_date}")
|
|
1538
|
+
# Gather up the list of files to upload
|
|
1539
|
+
if path == ".":
|
|
1540
|
+
zip_path = release_files_dir + "/archive.zip"
|
|
1541
|
+
all_paths = calkit.releases.ls_files()
|
|
1542
|
+
typer.echo(f"Adding files to {zip_path}")
|
|
1543
|
+
with zipfile.ZipFile(zip_path, "w") as zipf:
|
|
1544
|
+
for fpath in all_paths:
|
|
1545
|
+
zipf.write(fpath)
|
|
1546
|
+
if description is None:
|
|
1547
|
+
description = "An archive of all project files."
|
|
1548
|
+
title = ck_info.get("title")
|
|
1549
|
+
if title is None:
|
|
1550
|
+
warn("Project has no title")
|
|
1551
|
+
title = typer.prompt("Enter a title for the project")
|
|
1552
|
+
ck_info["title"] = title
|
|
1553
|
+
else:
|
|
1554
|
+
# TODO: Handle directories, e.g., datasets
|
|
1555
|
+
if not os.path.isfile(path):
|
|
1556
|
+
raise_error("Single artifact releases must be a single file")
|
|
1557
|
+
typer.echo(f"Copying {path} into {release_files_dir}")
|
|
1558
|
+
shutil.copy2(path, release_files_dir)
|
|
1559
|
+
if description is None:
|
|
1560
|
+
description = f"Release {release_type} at {path}."
|
|
1561
|
+
# Check that this artifact actually exists
|
|
1562
|
+
artifact_key = (
|
|
1563
|
+
release_type + "s" if release_type != "software" else release_type
|
|
1564
|
+
)
|
|
1565
|
+
artifacts = ck_info.get(artifact_key, [])
|
|
1566
|
+
title = None
|
|
1567
|
+
artifact = None
|
|
1568
|
+
for a in artifacts:
|
|
1569
|
+
if a.get("path") == path:
|
|
1570
|
+
artifact = a
|
|
1571
|
+
title = artifact.get("title")
|
|
1572
|
+
break
|
|
1573
|
+
if artifact is None:
|
|
1574
|
+
raise_error(f"{release_type} at {path} not defined in calkit.yaml")
|
|
1575
|
+
if title is None:
|
|
1576
|
+
raise_error(f"{release_type} at {path} has no title")
|
|
1577
|
+
# Save a metadata file with each DVC file's MD5 checksum
|
|
1578
|
+
dvc_md5s = calkit.releases.make_dvc_md5s(
|
|
1579
|
+
zipfile="archive.zip" if path == "." else None,
|
|
1580
|
+
paths=None if path == "." else [path],
|
|
1581
|
+
)
|
|
1582
|
+
dvc_md5s_path = release_dir + "/dvc-md5s.yaml"
|
|
1583
|
+
typer.echo(f"Saving DVC MD5 info to {dvc_md5s_path}")
|
|
1584
|
+
with open(dvc_md5s_path, "w") as f:
|
|
1585
|
+
calkit.ryaml.dump(dvc_md5s, f)
|
|
1586
|
+
if not dry_run:
|
|
1587
|
+
repo.git.add(dvc_md5s_path)
|
|
1588
|
+
# Create a README for the Zenodo release
|
|
1589
|
+
readme_txt = f"# {title}\n"
|
|
1590
|
+
git_rev = repo.git.rev_parse(["--short", "HEAD"])
|
|
1591
|
+
readme_txt += (
|
|
1592
|
+
f"\nThis is a {release_type} release ({name}) generated with "
|
|
1593
|
+
f"Calkit from Git rev {git_rev}.\n"
|
|
1594
|
+
)
|
|
1595
|
+
readme_path = release_files_dir + "/README.md"
|
|
1596
|
+
with open(readme_path, "w") as f:
|
|
1597
|
+
f.write(readme_txt)
|
|
1598
|
+
# Check size of files dir
|
|
1599
|
+
size = calkit.get_size(release_files_dir)
|
|
1600
|
+
typer.echo(f"Release size: {(size / 1e6):.1f} MB")
|
|
1601
|
+
if size >= 50e9:
|
|
1602
|
+
raise_error("Release is too large (>50 GB) to upload to Zenodo")
|
|
1603
|
+
# Upload to Zenodo
|
|
1604
|
+
# Is there already a deposition for this release, which indicates we should
|
|
1605
|
+
# create a new version?
|
|
1606
|
+
zenodo_dep_id = None
|
|
1607
|
+
project_name = calkit.git.detect_project_name()
|
|
1608
|
+
zenodo_metadata = dict(
|
|
1609
|
+
title=title,
|
|
1610
|
+
description=description,
|
|
1611
|
+
notes=f"Created from Calkit project {project_name} release {name}.",
|
|
1612
|
+
publication_date=release_date,
|
|
1613
|
+
)
|
|
1614
|
+
# Determine creators from authors, adding to project if not present
|
|
1615
|
+
authors = ck_info.get("authors", [])
|
|
1616
|
+
if not authors:
|
|
1617
|
+
warn("No authors defined for the project")
|
|
1618
|
+
still_entering_authors = True
|
|
1619
|
+
n = 0
|
|
1620
|
+
while still_entering_authors:
|
|
1621
|
+
n += 1
|
|
1622
|
+
author = dict()
|
|
1623
|
+
author["first_name"] = typer.prompt(
|
|
1624
|
+
f"Enter the first name of author {n}"
|
|
1625
|
+
)
|
|
1626
|
+
author["last_name"] = typer.prompt(
|
|
1627
|
+
f"Enter the last name of author {n}"
|
|
1628
|
+
)
|
|
1629
|
+
author["affiliation"] = typer.prompt(
|
|
1630
|
+
f"Enter the affiliation of author {n}"
|
|
1631
|
+
)
|
|
1632
|
+
has_orchid = typer.confirm(
|
|
1633
|
+
f"Does author {n} have an ORCID?", default=False
|
|
1634
|
+
)
|
|
1635
|
+
if has_orchid:
|
|
1636
|
+
author["orcid"] = typer.prompt(
|
|
1637
|
+
f"Enter the ORCID of author {n}"
|
|
1638
|
+
)
|
|
1639
|
+
authors.append(author)
|
|
1640
|
+
still_entering_authors = typer.confirm(
|
|
1641
|
+
"Are there more authors to enter?", default=True
|
|
1642
|
+
)
|
|
1643
|
+
ck_info["authors"] = authors
|
|
1644
|
+
zenodo_creators = []
|
|
1645
|
+
for author in authors:
|
|
1646
|
+
creator = dict(
|
|
1647
|
+
name=f"{author['last_name']}, {author['first_name']}",
|
|
1648
|
+
affiliation=author["affiliation"],
|
|
1649
|
+
)
|
|
1650
|
+
if "orcid" in author:
|
|
1651
|
+
creator["orcid"] = author["orcid"]
|
|
1652
|
+
zenodo_creators.append(creator)
|
|
1653
|
+
zenodo_metadata["creators"] = zenodo_creators
|
|
1654
|
+
if release_type == "project":
|
|
1655
|
+
zenodo_metadata["upload_type"] = "other"
|
|
1656
|
+
elif release_type == "publication":
|
|
1657
|
+
pubtype = artifact.get("kind")
|
|
1658
|
+
if pubtype == "journal-article":
|
|
1659
|
+
zenodo_metadata["upload_type"] = "publication"
|
|
1660
|
+
zenodo_metadata["publication_type"] = "article"
|
|
1661
|
+
elif pubtype == "presentation":
|
|
1662
|
+
zenodo_metadata["upload_type"] = "presentation"
|
|
1663
|
+
elif pubtype == "poster":
|
|
1664
|
+
zenodo_metadata["upload_type"] = "poster"
|
|
1665
|
+
else:
|
|
1666
|
+
zenodo_metadata["upload_type"] = "other"
|
|
1667
|
+
elif release_type in ["dataset", "software"]:
|
|
1668
|
+
zenodo_metadata["upload_type"] = release_type
|
|
1669
|
+
elif release_type == "figure":
|
|
1670
|
+
zenodo_metadata["upload_type"] = "image"
|
|
1671
|
+
zenodo_metadata["image_type"] = "figure"
|
|
1672
|
+
else:
|
|
1673
|
+
zenodo_metadata["upload_type"] = "other"
|
|
1674
|
+
doi = None
|
|
1675
|
+
url = None
|
|
1676
|
+
for existing_name, existing_release in releases.items():
|
|
1677
|
+
if (
|
|
1678
|
+
existing_release.get("kind") == release_type
|
|
1679
|
+
and existing_release.get("path") == path
|
|
1680
|
+
and existing_release.get("publisher") == "zenodo.org"
|
|
1681
|
+
):
|
|
1682
|
+
zenodo_dep_id = existing_release.get("zenodo_dep_id")
|
|
1683
|
+
typer.echo(
|
|
1684
|
+
f"Found existing Zenodo deposition ID {zenodo_dep_id} "
|
|
1685
|
+
f"in release {existing_name} to create new version for"
|
|
1686
|
+
)
|
|
1687
|
+
break
|
|
1688
|
+
if not dry_run:
|
|
1689
|
+
typer.echo("Uploading to Zenodo")
|
|
1690
|
+
if zenodo_dep_id is not None:
|
|
1691
|
+
# Create a new version of the existing deposit
|
|
1692
|
+
# TODO: This might fail if a new version is in progress, in which
|
|
1693
|
+
# case we should discard that
|
|
1694
|
+
zenodo_dep = calkit.zenodo.post(
|
|
1695
|
+
f"/deposit/depositions/{zenodo_dep_id}/actions/newversion",
|
|
1696
|
+
json=dict(metadata=zenodo_metadata),
|
|
1697
|
+
)
|
|
1698
|
+
typer.echo("Created new version deposition")
|
|
1699
|
+
typer.echo("Fetching latest draft")
|
|
1700
|
+
zenodo_dep = requests.get(
|
|
1701
|
+
zenodo_dep["links"]["latest_draft"],
|
|
1702
|
+
params=dict(access_token=token),
|
|
1703
|
+
).json()
|
|
1704
|
+
zenodo_dep_id = zenodo_dep["id"]
|
|
1705
|
+
typer.echo(
|
|
1706
|
+
f"Fetched latest draft with deposition ID: {zenodo_dep_id} "
|
|
1707
|
+
)
|
|
1708
|
+
# Now update that draft with the metadata
|
|
1709
|
+
typer.echo("Updating latest draft metadata")
|
|
1710
|
+
calkit.zenodo.put(
|
|
1711
|
+
f"/deposit/depositions/{zenodo_dep_id}",
|
|
1712
|
+
json=dict(metadata=zenodo_metadata),
|
|
1713
|
+
)
|
|
1714
|
+
else:
|
|
1715
|
+
zenodo_dep = calkit.zenodo.post(
|
|
1716
|
+
"/deposit/depositions", json=dict(metadata=zenodo_metadata)
|
|
1717
|
+
)
|
|
1718
|
+
zenodo_dep_id = zenodo_dep["id"]
|
|
1719
|
+
bucket_url = zenodo_dep["links"]["bucket"]
|
|
1720
|
+
files = os.listdir(release_files_dir)
|
|
1721
|
+
for filename in files:
|
|
1722
|
+
typer.echo(f"Uploading {filename}")
|
|
1723
|
+
fpath = os.path.join(release_files_dir, filename)
|
|
1724
|
+
with open(fpath, "rb") as f:
|
|
1725
|
+
resp = requests.put(
|
|
1726
|
+
f"{bucket_url}/{filename}",
|
|
1727
|
+
data=f,
|
|
1728
|
+
params={"access_token": token},
|
|
1729
|
+
)
|
|
1730
|
+
typer.echo(f"Status code: {resp.status_code}")
|
|
1731
|
+
resp.raise_for_status()
|
|
1732
|
+
# Now publish the new deposition
|
|
1733
|
+
typer.echo(f"Publishing Zenodo deposition ID {zenodo_dep_id}")
|
|
1734
|
+
zenodo_dep = calkit.zenodo.post(
|
|
1735
|
+
f"/deposit/depositions/{zenodo_dep_id}/actions/publish"
|
|
1736
|
+
)
|
|
1737
|
+
zenodo_dep_id = zenodo_dep["id"]
|
|
1738
|
+
doi = zenodo_dep["doi"]
|
|
1739
|
+
url = zenodo_dep["doi_url"]
|
|
1740
|
+
typer.echo(f"Published to Zenodo with DOI: {doi}")
|
|
1741
|
+
else:
|
|
1742
|
+
typer.echo(f"Would have posted Zenodo deposition: {zenodo_metadata}")
|
|
1743
|
+
# If this is a project release, add Zenodo badge to project README if
|
|
1744
|
+
# it doesn't exist
|
|
1745
|
+
doi_md = None
|
|
1746
|
+
if release_type == "project" and doi is not None:
|
|
1747
|
+
typer.echo("Adding DOI badge to README.md")
|
|
1748
|
+
doi_md = (
|
|
1749
|
+
f"[]"
|
|
1750
|
+
f"(https://handle.stage.datacite.org/{doi})"
|
|
1751
|
+
)
|
|
1752
|
+
if os.path.isfile("README.md"):
|
|
1753
|
+
with open("README.md") as f:
|
|
1754
|
+
readme_txt = f.read()
|
|
1755
|
+
else:
|
|
1756
|
+
readme_txt = f"# {title}\n"
|
|
1757
|
+
existing_lines = readme_txt.split("\n")
|
|
1758
|
+
new_lines = []
|
|
1759
|
+
first_content_line_index = None
|
|
1760
|
+
for n, line in enumerate(existing_lines):
|
|
1761
|
+
if line.startswith(doi_md[:6]):
|
|
1762
|
+
pass # Skip DOI lines
|
|
1763
|
+
else:
|
|
1764
|
+
if (
|
|
1765
|
+
n != 0
|
|
1766
|
+
and line.strip()
|
|
1767
|
+
and first_content_line_index is None
|
|
1768
|
+
):
|
|
1769
|
+
first_content_line_index = len(new_lines)
|
|
1770
|
+
new_lines.append(line)
|
|
1771
|
+
# Ensure first 3 lines are title, blank, DOI lines
|
|
1772
|
+
new_lines = (
|
|
1773
|
+
[new_lines[0]]
|
|
1774
|
+
+ ["", doi_md, ""]
|
|
1775
|
+
+ new_lines[first_content_line_index:]
|
|
1776
|
+
)
|
|
1777
|
+
readme_txt = "\n".join(new_lines)
|
|
1778
|
+
with open("README.md", "w") as f:
|
|
1779
|
+
f.write(readme_txt)
|
|
1780
|
+
if not dry_run:
|
|
1781
|
+
repo.git.add("README.md")
|
|
1782
|
+
# Create Git tag
|
|
1783
|
+
if not dry_run:
|
|
1784
|
+
repo.git.tag(["-a", name, "-m", description])
|
|
1785
|
+
else:
|
|
1786
|
+
typer.echo(
|
|
1787
|
+
f"Would have created Git tag {name} with message: {description}"
|
|
1788
|
+
)
|
|
1789
|
+
# Save release in Calkit info
|
|
1790
|
+
release = dict(
|
|
1791
|
+
kind=release_type,
|
|
1792
|
+
path=path,
|
|
1793
|
+
git_rev=git_rev,
|
|
1794
|
+
date=release_date,
|
|
1795
|
+
publisher="zenodo.org",
|
|
1796
|
+
zenodo_dep_id=zenodo_dep_id,
|
|
1797
|
+
doi=doi,
|
|
1798
|
+
url=url,
|
|
1799
|
+
description=description,
|
|
1800
|
+
)
|
|
1801
|
+
releases[name] = release
|
|
1802
|
+
ck_info["releases"] = releases
|
|
1803
|
+
# Create CITATION.cff file
|
|
1804
|
+
if release_type == "project":
|
|
1805
|
+
typer.echo("Writing CITATION.cff")
|
|
1806
|
+
cff = calkit.releases.create_citation_cff(
|
|
1807
|
+
ck_info=ck_info, release_name=name, release_date=release_date
|
|
1808
|
+
)
|
|
1809
|
+
with open("CITATION.cff", "w") as f:
|
|
1810
|
+
calkit.ryaml.dump(cff, f)
|
|
1811
|
+
if not dry_run:
|
|
1812
|
+
repo.git.add("CITATION.cff")
|
|
1813
|
+
# Add to references so it can be cited
|
|
1814
|
+
typer.echo("Adding BibTeX entry to references")
|
|
1815
|
+
reference_collections = ck_info.get("references", [])
|
|
1816
|
+
if len(reference_collections) > 1:
|
|
1817
|
+
warn("Multiple references collections; writing to first")
|
|
1818
|
+
if not reference_collections:
|
|
1819
|
+
references = dict(path="references.bib")
|
|
1820
|
+
ck_info["references"] = [references]
|
|
1821
|
+
else:
|
|
1822
|
+
references = reference_collections[0]
|
|
1823
|
+
ref_path = references.get("path", "references.bib")
|
|
1824
|
+
try:
|
|
1825
|
+
if os.path.isfile(ref_path):
|
|
1826
|
+
with open(ref_path) as f:
|
|
1827
|
+
reflib = bibtexparser.load(f)
|
|
1828
|
+
else:
|
|
1829
|
+
reflib = bibtexparser.bibdatabase.BibDatabase()
|
|
1830
|
+
zenodo_bibtex = calkit.releases.create_bibtex(
|
|
1831
|
+
authors=authors,
|
|
1832
|
+
release_date=release_date,
|
|
1833
|
+
title=title,
|
|
1834
|
+
doi=doi,
|
|
1835
|
+
dep_id=zenodo_dep_id,
|
|
1836
|
+
)
|
|
1837
|
+
new_entry = bibtexparser.loads(zenodo_bibtex).entries[0]
|
|
1838
|
+
# Search through entries for one with the same DOI, and replace if
|
|
1839
|
+
# there is a match
|
|
1840
|
+
existing_index = None
|
|
1841
|
+
for n, entry in enumerate(reflib.entries):
|
|
1842
|
+
if entry.get("doi") == doi:
|
|
1843
|
+
typer.echo("Found matching DOI in existing references")
|
|
1844
|
+
existing_index = n
|
|
1845
|
+
if existing_index is not None:
|
|
1846
|
+
_ = reflib.entries.pop(existing_index)
|
|
1847
|
+
reflib.entries.append(new_entry)
|
|
1848
|
+
with open(ref_path, "w") as f:
|
|
1849
|
+
bibtexparser.dump(reflib, f)
|
|
1850
|
+
if not dry_run:
|
|
1851
|
+
repo.git.add(ref_path)
|
|
1852
|
+
except Exception as e:
|
|
1853
|
+
warn(f"Failed to add to references: {e}")
|
|
1854
|
+
# Write out Calkit metadata
|
|
1855
|
+
if not dry_run:
|
|
1856
|
+
typer.echo("Writing to calkit.yaml")
|
|
1857
|
+
with open("calkit.yaml", "w") as f:
|
|
1858
|
+
calkit.ryaml.dump(ck_info, f)
|
|
1859
|
+
repo.git.add("calkit.yaml")
|
|
1860
|
+
else:
|
|
1861
|
+
typer.echo(f"Would have created release:\n{release}")
|
|
1862
|
+
# Commit with Git
|
|
1863
|
+
if not dry_run and calkit.git.get_staged_files() and not no_commit:
|
|
1864
|
+
repo.git.commit(["-m", f"Create new {release_type} release {name}"])
|
|
1865
|
+
# Push with Git
|
|
1866
|
+
if not dry_run and not no_push and not no_commit:
|
|
1867
|
+
repo.git.push(["origin", repo.active_branch.name, "--tags"])
|
|
1868
|
+
# Now create GitHub release
|
|
1869
|
+
typer.echo("Creating GitHub release")
|
|
1870
|
+
release_body = ""
|
|
1871
|
+
if doi_md is not None:
|
|
1872
|
+
release_body += doi_md + "\n\n"
|
|
1873
|
+
release_body += description
|
|
1874
|
+
resp = calkit.cloud.post(
|
|
1875
|
+
f"/projects/{project_name}/github-releases",
|
|
1876
|
+
json=dict(
|
|
1877
|
+
tag_name=name,
|
|
1878
|
+
body=release_body,
|
|
1879
|
+
),
|
|
1880
|
+
)
|
|
1881
|
+
typer.echo(f"Created GitHub release at: {resp['url']}")
|
|
1882
|
+
# TODO: Upload assets for GitHub release if they're not too big?
|
|
1883
|
+
typer.echo(f"New {release_type} release {name} successfully created")
|
|
@@ -37,6 +37,10 @@ def get_app_name() -> str:
|
|
|
37
37
|
return __package__ + get_env_suffix()
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
def get_local_config_path() -> str:
|
|
41
|
+
return os.path.join(".calkit", "config.yaml")
|
|
42
|
+
|
|
43
|
+
|
|
40
44
|
class Settings(BaseSettings):
|
|
41
45
|
model_config = SettingsConfigDict(
|
|
42
46
|
yaml_file=os.path.join(
|
|
@@ -46,12 +50,16 @@ class Settings(BaseSettings):
|
|
|
46
50
|
),
|
|
47
51
|
extra="ignore",
|
|
48
52
|
env_prefix="CALKIT" + get_env_suffix(sep="_") + "_",
|
|
53
|
+
env_file=".env",
|
|
54
|
+
env_file_encoding="utf-8",
|
|
49
55
|
)
|
|
50
56
|
username: str | None = None
|
|
51
57
|
email: str | None = None
|
|
52
58
|
token: str | None = None
|
|
53
59
|
dvc_token: str | None = None
|
|
54
60
|
dataframe_engine: Literal["pandas", "polars"] = "pandas"
|
|
61
|
+
github_token: str | None = None
|
|
62
|
+
zenodo_token: str | None = None
|
|
55
63
|
|
|
56
64
|
@classmethod
|
|
57
65
|
def settings_customise_sources(
|
|
@@ -65,6 +73,7 @@ class Settings(BaseSettings):
|
|
|
65
73
|
return (
|
|
66
74
|
init_settings,
|
|
67
75
|
env_settings,
|
|
76
|
+
dotenv_settings,
|
|
68
77
|
YamlConfigSettingsSource(settings_cls),
|
|
69
78
|
)
|
|
70
79
|
|
|
@@ -126,10 +126,17 @@ def get_remotes(wdir: str = None) -> dict[str, str]:
|
|
|
126
126
|
return resp
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
def list_paths(wdir: str = None) -> list[str]:
|
|
129
|
+
def list_paths(wdir: str = None, recursive=False) -> list[str]:
|
|
130
130
|
"""List paths tracked with DVC."""
|
|
131
|
+
return [p.get("path") for p in list_files(wdir=wdir, recursive=recursive)]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def list_files(wdir: str = None, recursive=True) -> list[dict]:
|
|
135
|
+
"""Return a list with all files in DVC, including their path and md5
|
|
136
|
+
checksum.
|
|
137
|
+
"""
|
|
131
138
|
dvc_repo = dvc.repo.Repo(wdir)
|
|
132
|
-
return
|
|
139
|
+
return dvc_repo.ls(".", dvc_only=True, recursive=recursive)
|
|
133
140
|
|
|
134
141
|
|
|
135
142
|
def get_output_revisions(path: str):
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Functionality for working with GitHub."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from functools import partial
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
import dotenv
|
|
8
|
+
import requests
|
|
9
|
+
from requests.exceptions import HTTPError
|
|
10
|
+
|
|
11
|
+
import calkit
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_token() -> str:
|
|
15
|
+
dotenv.load_dotenv()
|
|
16
|
+
token = calkit.config.read().github_token
|
|
17
|
+
if token is None:
|
|
18
|
+
token = os.getenv("GITHUB_TOKEN")
|
|
19
|
+
if token is None:
|
|
20
|
+
token = calkit.cloud.get("/user/github-token")["access_token"]
|
|
21
|
+
return token
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_base_url() -> str:
|
|
25
|
+
return "https://api.github.com"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _request(
|
|
29
|
+
kind: Literal["get", "post", "put", "patch", "delete"],
|
|
30
|
+
path: str,
|
|
31
|
+
params: dict | None = None,
|
|
32
|
+
json: dict | None = None,
|
|
33
|
+
data: dict | None = None,
|
|
34
|
+
headers: dict | None = None,
|
|
35
|
+
as_json=True,
|
|
36
|
+
**kwargs,
|
|
37
|
+
):
|
|
38
|
+
if headers is None:
|
|
39
|
+
headers = {}
|
|
40
|
+
if "Authorization" not in headers:
|
|
41
|
+
headers = headers | {"Authorization": f"Bearer {get_token()}"}
|
|
42
|
+
func = getattr(requests, kind)
|
|
43
|
+
resp = func(
|
|
44
|
+
get_base_url() + path,
|
|
45
|
+
params=params,
|
|
46
|
+
json=json,
|
|
47
|
+
data=data,
|
|
48
|
+
headers=headers,
|
|
49
|
+
**kwargs,
|
|
50
|
+
)
|
|
51
|
+
if resp.status_code >= 400:
|
|
52
|
+
resp_json = resp.json()
|
|
53
|
+
msg = f"{resp.status_code}: "
|
|
54
|
+
if "message" in resp_json:
|
|
55
|
+
msg += resp_json["message"]
|
|
56
|
+
if "errors" in resp_json:
|
|
57
|
+
msg += f"\nErrors:\n{resp_json['errors']}"
|
|
58
|
+
raise HTTPError(msg)
|
|
59
|
+
resp.raise_for_status()
|
|
60
|
+
if as_json:
|
|
61
|
+
return resp.json()
|
|
62
|
+
else:
|
|
63
|
+
return resp
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
get = partial(_request, "get")
|
|
67
|
+
post = partial(_request, "post")
|
|
68
|
+
patch = partial(_request, "patch")
|
|
69
|
+
put = partial(_request, "put")
|
|
70
|
+
delete = partial(_request, "delete")
|