arcsecond 3.12.0__tar.gz → 3.13.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.
- {arcsecond-3.12.0 → arcsecond-3.13.0}/.github/workflows/pythonpublish.yml +5 -7
- {arcsecond-3.12.0 → arcsecond-3.13.0}/.github/workflows/tests.yml +15 -23
- {arcsecond-3.12.0 → arcsecond-3.13.0}/PKG-INFO +10 -11
- {arcsecond-3.12.0 → arcsecond-3.13.0}/README.md +10 -11
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/__init__.py +0 -4
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/alpaca/commands.py +3 -9
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/alpaca/dome_probe.py +5 -6
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/main.py +0 -4
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cli.py +0 -4
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/__init__.py +1 -3
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/resources.py +0 -35
- arcsecond-3.13.0/arcsecond/cloud/uploader/__init__.py +8 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/commands.py +1 -1
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/.vitepress/config.js +1 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/resources.md +0 -1
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/upload.md +2 -40
- {arcsecond-3.12.0 → arcsecond-3.13.0}/pyproject.toml +11 -1
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/test_alpaca_probe.py +12 -8
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/utils.py +0 -27
- arcsecond-3.13.0/uv.lock +2488 -0
- arcsecond-3.12.0/arcsecond/cloud/uploader/__init__.py +0 -13
- arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages/context.py +0 -62
- arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages/errors.py +0 -19
- arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages/uploader.py +0 -29
- arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages/utils.py +0 -54
- arcsecond-3.12.0/examples/example_upload_images.py +0 -24
- arcsecond-3.12.0/poetry.lock +0 -471
- arcsecond-3.12.0/tests/cloud/uploader/allskycameraimages/__init__.py +0 -0
- arcsecond-3.12.0/tests/cloud/uploader/allskycameraimages/test_context.py +0 -115
- arcsecond-3.12.0/tests/cloud/uploader/allskycameraimages/test_uploader_full_process.py +0 -85
- arcsecond-3.12.0/tests/cloud/uploader/datafiles/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/.docker/Dockerfile_postgres +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/.docker/Dockerfile_redis +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/.github/dependabot.yml +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/.github/workflows/docsdeploy.yml +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/.gitignore +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/LICENSE +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/Makefile +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/__version__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/alpaca/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/config.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/constants.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/endpoint.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/resources.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/auth.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/constants.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/context.py +0 -0
- {arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages → arcsecond-3.13.0/arcsecond/cloud/uploader/datafiles}/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/context.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/errors.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/uploader.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/utils.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/errors.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/logger.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/uploader.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/utils.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/walker.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploads.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/errors.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/backups.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/checks.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/constants.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/constants.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/containers.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/docker-compose.yml +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/images.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/utils.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/client.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/utils.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/local.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/main.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/postgres/init-db.sh +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/setup.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/utils.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/validation.py +0 -0
- {arcsecond-3.12.0/arcsecond/cloud/uploader/datafiles → arcsecond-3.13.0/arcsecond/imagesources}/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/proxy.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/registry.py +0 -0
- {arcsecond-3.12.0/arcsecond/imagesources → arcsecond-3.13.0/arcsecond/imagesources/sources}/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/base.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/filewatch.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/opencv.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/options.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/targets.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/deploy.sh +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/.vitepress/theme/custom.css +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/.vitepress/theme/index.js +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/api-basics.md +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/backups.md +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/img/icon-logo.png +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/index.md +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/install-windows.md +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/install.md +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/public/img/icon-logo.png +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/rotate-postgres-password.md +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/webcam.md +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/examples/example_upload_files.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/package-lock.json +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/package.json +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/requirements.txt +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/setup.cfg +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/setup.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/sonar-project.properties +0 -0
- {arcsecond-3.12.0/arcsecond/imagesources/sources → arcsecond-3.13.0/tests}/__init__.py +0 -0
- {arcsecond-3.12.0/tests → arcsecond-3.13.0/tests/api}/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/api/test_api.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/api/test_api_endpoint.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/api/test_config.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/api/test_targets.py +0 -0
- {arcsecond-3.12.0/tests/api → arcsecond-3.13.0/tests/cloud}/__init__.py +0 -0
- {arcsecond-3.12.0/tests/cloud → arcsecond-3.13.0/tests/cloud/uploader}/__init__.py +0 -0
- {arcsecond-3.12.0/tests/cloud/uploader → arcsecond-3.13.0/tests/cloud/uploader/datafiles}/__init__.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_errors.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_full_process.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_init.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_prepare.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_upload.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/conftest.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/fixtures/file1.fits +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/test_cli.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/test_hosting_local.py +0 -0
- {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/test_targets_planning.py +0 -0
|
@@ -25,14 +25,15 @@ jobs:
|
|
|
25
25
|
|
|
26
26
|
- uses: actions/checkout@v5
|
|
27
27
|
|
|
28
|
-
- name:
|
|
29
|
-
uses:
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v6
|
|
30
30
|
with:
|
|
31
31
|
python-version: '3.12'
|
|
32
|
+
enable-cache: true
|
|
32
33
|
|
|
33
34
|
- name: Validate pyproject.toml version matches tag
|
|
34
35
|
run: |
|
|
35
|
-
python - <<'PY'
|
|
36
|
+
uv run python - <<'PY'
|
|
36
37
|
import os, pathlib, tomllib
|
|
37
38
|
|
|
38
39
|
tag = os.environ["GITHUB_REF_NAME"]
|
|
@@ -45,10 +46,7 @@ jobs:
|
|
|
45
46
|
PY
|
|
46
47
|
|
|
47
48
|
- name: Build distributions
|
|
48
|
-
run:
|
|
49
|
-
python -m pip install --upgrade pip
|
|
50
|
-
pip install build
|
|
51
|
-
python -m build
|
|
49
|
+
run: uv build
|
|
52
50
|
|
|
53
51
|
- name: Publish to PyPI (OIDC)
|
|
54
52
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -16,26 +16,22 @@ jobs:
|
|
|
16
16
|
steps:
|
|
17
17
|
- uses: actions/checkout@v5
|
|
18
18
|
|
|
19
|
-
- name:
|
|
20
|
-
uses:
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v6
|
|
21
21
|
with:
|
|
22
22
|
python-version: ${{ matrix.python-version }}
|
|
23
|
+
enable-cache: true
|
|
23
24
|
|
|
24
25
|
- name: Install dependencies
|
|
25
|
-
run:
|
|
26
|
-
python -m pip install --upgrade pip
|
|
27
|
-
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
28
|
-
pip install pytest pytest-cov respx
|
|
29
|
-
pip install -e .
|
|
26
|
+
run: uv sync --extra webcam --group dev
|
|
30
27
|
|
|
31
28
|
- name: Test with coverage (only on 3.12)
|
|
32
29
|
if: matrix.python-version == '3.12'
|
|
33
|
-
run:
|
|
34
|
-
pytest -vvs tests/ --cov=arcsecond --cov-report=xml:coverage.xml
|
|
30
|
+
run: uv run pytest -vvs tests/ --cov=arcsecond --cov-report=xml:coverage.xml
|
|
35
31
|
|
|
36
32
|
- name: Test without coverage
|
|
37
33
|
if: matrix.python-version != '3.12'
|
|
38
|
-
run: pytest -vvs tests/
|
|
34
|
+
run: uv run pytest -vvs tests/
|
|
39
35
|
|
|
40
36
|
- name: Upload coverage artifact (only on 3.12)
|
|
41
37
|
if: matrix.python-version == '3.12'
|
|
@@ -58,7 +54,7 @@ jobs:
|
|
|
58
54
|
path: .
|
|
59
55
|
|
|
60
56
|
- name: SonarQube Scan
|
|
61
|
-
uses: SonarSource/sonarqube-scan-action@
|
|
57
|
+
uses: SonarSource/sonarqube-scan-action@v6
|
|
62
58
|
env:
|
|
63
59
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
64
60
|
|
|
@@ -67,24 +63,20 @@ jobs:
|
|
|
67
63
|
steps:
|
|
68
64
|
- uses: actions/checkout@v5
|
|
69
65
|
|
|
70
|
-
- name:
|
|
71
|
-
uses:
|
|
66
|
+
- name: Install uv
|
|
67
|
+
uses: astral-sh/setup-uv@v6
|
|
72
68
|
with:
|
|
73
69
|
python-version: '3.12'
|
|
70
|
+
enable-cache: true
|
|
74
71
|
|
|
75
|
-
- name: Install dependencies
|
|
76
|
-
run:
|
|
77
|
-
python -m pip install --upgrade pip
|
|
78
|
-
pip install flake8 black isort
|
|
72
|
+
- name: Install dev dependencies
|
|
73
|
+
run: uv sync --group dev
|
|
79
74
|
|
|
80
75
|
- name: Lint with flake8
|
|
81
|
-
run:
|
|
82
|
-
flake8 arcsecond tests --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
76
|
+
run: uv run flake8 arcsecond tests --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
83
77
|
|
|
84
78
|
- name: Check formatting with black
|
|
85
|
-
run:
|
|
86
|
-
black --check arcsecond tests
|
|
79
|
+
run: uv run black --check arcsecond tests
|
|
87
80
|
|
|
88
81
|
- name: Check imports with isort
|
|
89
|
-
run:
|
|
90
|
-
isort --check-only --profile black arcsecond tests
|
|
82
|
+
run: uv run isort --check-only --profile black arcsecond tests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arcsecond
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.13.0
|
|
4
4
|
Summary: CLI for arcsecond.io
|
|
5
5
|
Project-URL: Homepage, https://github.com/arcsecond-io/cli
|
|
6
6
|
Project-URL: Issues, https://github.com/arcsecond-io/cli/issues
|
|
@@ -62,20 +62,19 @@ public and private ones.
|
|
|
62
62
|
|
|
63
63
|
# Development
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
(assuming [virtualenv](https://virtualenv.pypa.io/en/stable/) is installed):
|
|
65
|
+
The project uses [uv](https://docs.astral.sh/uv/) for environment and
|
|
66
|
+
dependency management. After forking and cloning:
|
|
68
67
|
|
|
69
68
|
```bash
|
|
70
69
|
$ cd ~/arcsecond-cli
|
|
71
|
-
$
|
|
72
|
-
$
|
|
73
|
-
$
|
|
74
|
-
```
|
|
70
|
+
$ uv sync --extra webcam --group dev
|
|
71
|
+
$ uv run arcsecond --help
|
|
72
|
+
$ uv run pytest
|
|
73
|
+
```
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
`uv sync` creates `.venv/` and installs the project with its `webcam` extra
|
|
76
|
+
and the `dev` dependency group (pytest, black, flake8, isort). Prefix
|
|
77
|
+
commands with `uv run` or activate the venv with `source .venv/bin/activate`.
|
|
79
78
|
|
|
80
79
|
|
|
81
80
|
## License
|
|
@@ -11,20 +11,19 @@ public and private ones.
|
|
|
11
11
|
|
|
12
12
|
# Development
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
(assuming [virtualenv](https://virtualenv.pypa.io/en/stable/) is installed):
|
|
14
|
+
The project uses [uv](https://docs.astral.sh/uv/) for environment and
|
|
15
|
+
dependency management. After forking and cloning:
|
|
17
16
|
|
|
18
17
|
```bash
|
|
19
18
|
$ cd ~/arcsecond-cli
|
|
20
|
-
$
|
|
21
|
-
$
|
|
22
|
-
$
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
$ uv sync --extra webcam --group dev
|
|
20
|
+
$ uv run arcsecond --help
|
|
21
|
+
$ uv run pytest
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`uv sync` creates `.venv/` and installs the project with its `webcam` extra
|
|
25
|
+
and the `dev` dependency group (pytest, black, flake8, isort). Prefix
|
|
26
|
+
commands with `uv run` or activate the venv with `source .venv/bin/activate`.
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
## License
|
|
@@ -5,8 +5,6 @@ from .api import (
|
|
|
5
5
|
ArcsecondTargetListsResource,
|
|
6
6
|
)
|
|
7
7
|
from .cloud.uploader import (
|
|
8
|
-
AllSkyCameraImageFileUploader,
|
|
9
|
-
AllSkyCameraImageUploadContext,
|
|
10
8
|
DatasetFileUploader,
|
|
11
9
|
DatasetUploadContext,
|
|
12
10
|
)
|
|
@@ -25,8 +23,6 @@ __all__ = [
|
|
|
25
23
|
"ArcsecondTargetPayloadPlan",
|
|
26
24
|
"DatasetUploadContext",
|
|
27
25
|
"DatasetFileUploader",
|
|
28
|
-
"AllSkyCameraImageFileUploader",
|
|
29
|
-
"AllSkyCameraImageUploadContext",
|
|
30
26
|
"plan_target_payload",
|
|
31
27
|
"walk_folder_and_upload_files",
|
|
32
28
|
]
|
|
@@ -167,20 +167,14 @@ def probe_dome_cmd(
|
|
|
167
167
|
json.dump(result.report, fp, indent=2, sort_keys=False)
|
|
168
168
|
fp.write("\n")
|
|
169
169
|
except OSError as exc:
|
|
170
|
-
raise ArcsecondError(
|
|
171
|
-
f"Could not write report to {output_path}: {exc}"
|
|
172
|
-
) from exc
|
|
170
|
+
raise ArcsecondError(f"Could not write report to {output_path}: {exc}") from exc
|
|
173
171
|
|
|
174
172
|
counts = result.counts
|
|
175
173
|
supported = result.report.get("supported_actions", {})
|
|
176
|
-
supported_n = (
|
|
177
|
-
len(supported.get("value") or []) if supported.get("ok") else 0
|
|
178
|
-
)
|
|
174
|
+
supported_n = len(supported.get("value") or []) if supported.get("ok") else 0
|
|
179
175
|
|
|
180
176
|
click.echo("")
|
|
181
177
|
click.echo(click.style("Summary", bold=True))
|
|
182
178
|
click.echo(f" Probes: {counts.ok}/{counts.total} OK")
|
|
183
179
|
click.echo(f" SupportedActions: {supported_n} entries")
|
|
184
|
-
click.echo(
|
|
185
|
-
f" Report written to {click.style(os.fspath(output_path), fg='cyan')}"
|
|
186
|
-
)
|
|
180
|
+
click.echo(f" Report written to {click.style(os.fspath(output_path), fg='cyan')}")
|
|
@@ -71,9 +71,7 @@ _READ_ONLY_COMMANDS: tuple[str, ...] = (
|
|
|
71
71
|
# Active commands: kept separate and only sent when allow_active=True.
|
|
72
72
|
# ``XQ#STATUS`` jumps to a label called STATUS if it exists; whether that
|
|
73
73
|
# label has side effects depends on the driver, hence "active".
|
|
74
|
-
_ACTIVE_COMMANDS: tuple[str, ...] = (
|
|
75
|
-
"XQ#STATUS",
|
|
76
|
-
)
|
|
74
|
+
_ACTIVE_COMMANDS: tuple[str, ...] = ("XQ#STATUS",)
|
|
77
75
|
|
|
78
76
|
_RESPONSE_TRUNCATE = 2000
|
|
79
77
|
|
|
@@ -116,6 +114,7 @@ def _coerce_jsonable(value: Any) -> Any:
|
|
|
116
114
|
# IntEnum is a subclass of int, so check it *before* the primitive branch.
|
|
117
115
|
# We want {"name": "shutterClosed", "value": 1} not just 1.
|
|
118
116
|
from enum import Enum
|
|
117
|
+
|
|
119
118
|
if isinstance(value, Enum):
|
|
120
119
|
return {"name": value.name, "value": value.value}
|
|
121
120
|
if isinstance(value, (str, int, float, bool)) or value is None:
|
|
@@ -384,15 +383,15 @@ def probe_dome(
|
|
|
384
383
|
raise RuntimeError(
|
|
385
384
|
f"Could not construct Alpaca Dome client for "
|
|
386
385
|
f"{protocol}://{address} (device {device_number}): "
|
|
387
|
-
f"{type(exc).__name__}: {exc}\n"
|
|
388
|
-
+ traceback.format_exc()
|
|
386
|
+
f"{type(exc).__name__}: {exc}\n" + traceback.format_exc()
|
|
389
387
|
) from exc
|
|
390
388
|
|
|
391
389
|
connected_outcome = _capture(lambda: dome.Connected)
|
|
392
390
|
counts.total += 1
|
|
393
391
|
counts.ok += 1 if connected_outcome["ok"] else 0
|
|
394
392
|
progress.emit(
|
|
395
|
-
"Connected",
|
|
393
|
+
"Connected",
|
|
394
|
+
connected_outcome["ok"],
|
|
396
395
|
connected_outcome.get("error") or str(connected_outcome.get("value")),
|
|
397
396
|
)
|
|
398
397
|
|
|
@@ -52,10 +52,6 @@ class ArcsecondAPI(object):
|
|
|
52
52
|
self.datasets = ArcsecondAPIEndpoint(self.config, "datasets", self.subdomain)
|
|
53
53
|
self.datafiles = ArcsecondAPIEndpoint(self.config, "datafiles", self.subdomain)
|
|
54
54
|
|
|
55
|
-
self.allskycameras = ArcsecondAPIEndpoint(
|
|
56
|
-
self.config, "allskycameras", self.subdomain
|
|
57
|
-
)
|
|
58
|
-
|
|
59
55
|
def login(self, username, access_key=None, upload_key=None):
|
|
60
56
|
assert access_key or upload_key
|
|
61
57
|
assert not (access_key and upload_key)
|
|
@@ -2,7 +2,6 @@ import click
|
|
|
2
2
|
|
|
3
3
|
from arcsecond.alpaca.commands import alpaca_group
|
|
4
4
|
from arcsecond.cloud import (
|
|
5
|
-
allskycameras,
|
|
6
5
|
api,
|
|
7
6
|
datasets,
|
|
8
7
|
login,
|
|
@@ -53,9 +52,6 @@ main.add_command(datasets)
|
|
|
53
52
|
# Read the list of existing telescopes (for upload purposes).
|
|
54
53
|
main.add_command(telescopes)
|
|
55
54
|
|
|
56
|
-
# Read the list of existing allskycameras (for upload purposes).
|
|
57
|
-
main.add_command(allskycameras)
|
|
58
|
-
|
|
59
55
|
# Upload a folder of files to a given dataset.
|
|
60
56
|
main.add_command(upload_data)
|
|
61
57
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from .auth import api, login, me
|
|
2
|
-
from .resources import
|
|
2
|
+
from .resources import datasets, telescopes
|
|
3
3
|
from .uploader import (
|
|
4
|
-
AllSkyCameraImageFileUploader,
|
|
5
|
-
AllSkyCameraImageUploadContext,
|
|
6
4
|
DatasetFileUploader,
|
|
7
5
|
DatasetUploadContext,
|
|
8
6
|
)
|
|
@@ -78,38 +78,3 @@ def telescopes(state, portal=None):
|
|
|
78
78
|
s += f"(uuid: {telescope_dict['uuid']}) "
|
|
79
79
|
s += f"[ObservingSite UUID: {telescope_dict['observing_site']}]"
|
|
80
80
|
click.echo(s)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
@click.command(help="Display the list of (portal) all-sky cameras.")
|
|
84
|
-
@click.option(
|
|
85
|
-
"-p",
|
|
86
|
-
"--portal",
|
|
87
|
-
required=False,
|
|
88
|
-
nargs=1,
|
|
89
|
-
help="The portal subdomain, if uploading for an Observatory Portal.",
|
|
90
|
-
)
|
|
91
|
-
@basic_options
|
|
92
|
-
@pass_state
|
|
93
|
-
def allskycameras(state, portal=None):
|
|
94
|
-
org_subdomain = portal or ""
|
|
95
|
-
if org_subdomain:
|
|
96
|
-
click.echo(f" • Fetching allskycameras for portal '{org_subdomain}'...")
|
|
97
|
-
else:
|
|
98
|
-
click.echo(" • Fetching allskycameras...")
|
|
99
|
-
|
|
100
|
-
cameras_list, error = ArcsecondAPI(
|
|
101
|
-
ArcsecondConfig.from_state(state), org_subdomain
|
|
102
|
-
).allskycameras.list()
|
|
103
|
-
if error is not None:
|
|
104
|
-
raise ArcsecondError(str(error))
|
|
105
|
-
|
|
106
|
-
if isinstance(cameras_list, dict) and "results" in cameras_list.keys():
|
|
107
|
-
cameras_list = cameras_list["results"]
|
|
108
|
-
|
|
109
|
-
click.echo(
|
|
110
|
-
f" • Found {len(cameras_list)} all-sky camera{'s' if len(cameras_list) > 1 else ''}."
|
|
111
|
-
)
|
|
112
|
-
for camera_list in cameras_list:
|
|
113
|
-
s = f" 💾 \"{camera_list['name']}\" "
|
|
114
|
-
s += f"(uuid: {camera_list['uuid']}) "
|
|
115
|
-
click.echo(s)
|
|
@@ -120,7 +120,7 @@ def _add_options(options):
|
|
|
120
120
|
# ---------------------------------------------------------------------------
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
@click.group(help="Manage the native live-image proxy
|
|
123
|
+
@click.group(help="Manage the native live-image proxy for USB webcams.")
|
|
124
124
|
def webcam():
|
|
125
125
|
pass
|
|
126
126
|
|
|
@@ -20,6 +20,7 @@ module.exports = {
|
|
|
20
20
|
text: 'Self-Hosting',
|
|
21
21
|
items: [
|
|
22
22
|
{ text: 'Live-Image Proxy', link: '/webcam' },
|
|
23
|
+
{ text: 'All-Sky Cameras', link: '/webcam#all-sky-cameras' },
|
|
23
24
|
{ text: 'Backups', link: '/backups' },
|
|
24
25
|
{ text: 'Rotate Postgres Password', link: '/rotate-postgres-password' }
|
|
25
26
|
]
|
|
@@ -4,8 +4,8 @@ sidebar: true
|
|
|
4
4
|
|
|
5
5
|
# Data Upload
|
|
6
6
|
|
|
7
|
-
Arcsecond CLI makes it easy to upload dataset files
|
|
8
|
-
|
|
7
|
+
Arcsecond CLI makes it easy to upload dataset files to your account or to an
|
|
8
|
+
observatory portal.
|
|
9
9
|
|
|
10
10
|
All non-hidden files are uploaded. Choose folders carefully so you only send the
|
|
11
11
|
data you actually want in Arcsecond Cloud Storage.
|
|
@@ -33,7 +33,6 @@ Useful discovery commands:
|
|
|
33
33
|
|
|
34
34
|
- `arcsecond datasets`
|
|
35
35
|
- `arcsecond telescopes`
|
|
36
|
-
- `arcsecond allskycameras`
|
|
37
36
|
|
|
38
37
|
## Upload Dataset Files With Python
|
|
39
38
|
|
|
@@ -89,40 +88,3 @@ for file_path in Path("/folder/path").glob("**/*"):
|
|
|
89
88
|
if error:
|
|
90
89
|
raise error
|
|
91
90
|
```
|
|
92
|
-
|
|
93
|
-
## Upload All-Sky Camera Images With Python
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
from datetime import datetime, timezone
|
|
97
|
-
from pathlib import Path
|
|
98
|
-
|
|
99
|
-
from arcsecond import (
|
|
100
|
-
AllSkyCameraImageFileUploader,
|
|
101
|
-
AllSkyCameraImageUploadContext,
|
|
102
|
-
ArcsecondConfig,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
config = ArcsecondConfig()
|
|
106
|
-
context = AllSkyCameraImageUploadContext(
|
|
107
|
-
config,
|
|
108
|
-
input_camera_uuid="camera-uuid",
|
|
109
|
-
org_subdomain="my-portal",
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
context.validate()
|
|
113
|
-
|
|
114
|
-
for file_path in Path("/folder/path").glob("*"):
|
|
115
|
-
if not file_path.is_file():
|
|
116
|
-
continue
|
|
117
|
-
|
|
118
|
-
uploader = AllSkyCameraImageFileUploader(
|
|
119
|
-
context,
|
|
120
|
-
str(file_path),
|
|
121
|
-
display_progress=False,
|
|
122
|
-
)
|
|
123
|
-
timestamp = datetime.now(timezone.utc).timestamp()
|
|
124
|
-
status, substatus, error = uploader.upload_file(utc_timestamp=timestamp)
|
|
125
|
-
|
|
126
|
-
if error:
|
|
127
|
-
raise error
|
|
128
|
-
```
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arcsecond"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.13.0"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "Cedric Foellmi", email = "cedric@arcsecond.io" },
|
|
10
10
|
]
|
|
@@ -42,6 +42,16 @@ webcam = [
|
|
|
42
42
|
'opencv-python-headless>=4.10,<5',
|
|
43
43
|
]
|
|
44
44
|
|
|
45
|
+
[dependency-groups]
|
|
46
|
+
dev = [
|
|
47
|
+
"pytest",
|
|
48
|
+
"pytest-cov",
|
|
49
|
+
"respx",
|
|
50
|
+
"flake8",
|
|
51
|
+
"black",
|
|
52
|
+
"isort",
|
|
53
|
+
]
|
|
54
|
+
|
|
45
55
|
[project.scripts]
|
|
46
56
|
arcsecond = "arcsecond.cli:main"
|
|
47
57
|
|
|
@@ -200,7 +200,12 @@ def test_probe_dome_function_default_is_readonly(fake_dome_factory):
|
|
|
200
200
|
dome = registry[0]
|
|
201
201
|
|
|
202
202
|
# Top-level shape.
|
|
203
|
-
for key in (
|
|
203
|
+
for key in (
|
|
204
|
+
"environment",
|
|
205
|
+
"device_metadata",
|
|
206
|
+
"supported_actions",
|
|
207
|
+
"command_passthrough",
|
|
208
|
+
):
|
|
204
209
|
assert key in result.report
|
|
205
210
|
assert "host_hints" not in result.report
|
|
206
211
|
|
|
@@ -255,7 +260,9 @@ def test_probe_dome_function_allow_active_sends_blind(fake_dome_factory):
|
|
|
255
260
|
)
|
|
256
261
|
|
|
257
262
|
dome = registry[0]
|
|
258
|
-
assert
|
|
263
|
+
assert (
|
|
264
|
+
dome.command_blind_calls
|
|
265
|
+
), "CommandBlind should have been invoked under --allow-active"
|
|
259
266
|
blind_commands = {cmd for cmd, _ in dome.command_blind_calls}
|
|
260
267
|
assert "XQ#STATUS" in blind_commands
|
|
261
268
|
|
|
@@ -303,9 +310,8 @@ def test_cli_writes_json_report(tmp_path):
|
|
|
303
310
|
# Default invocation: no host_hints, no CommandBlind block populated.
|
|
304
311
|
assert "host_hints" not in data
|
|
305
312
|
assert data["command_passthrough"]["command_blind"] == []
|
|
306
|
-
assert (
|
|
307
|
-
|
|
308
|
-
.startswith("CommandBlind is fire-and-forget")
|
|
313
|
+
assert data["command_passthrough"]["command_blind_skipped_reason"].startswith(
|
|
314
|
+
"CommandBlind is fire-and-forget"
|
|
309
315
|
)
|
|
310
316
|
|
|
311
317
|
|
|
@@ -353,9 +359,7 @@ def test_cli_progress_lines_emitted(tmp_path):
|
|
|
353
359
|
assert "Summary" in result.output
|
|
354
360
|
|
|
355
361
|
|
|
356
|
-
def test_cli_fatal_connection_error_becomes_arcsecond_error(
|
|
357
|
-
monkeypatch, tmp_path
|
|
358
|
-
):
|
|
362
|
+
def test_cli_fatal_connection_error_becomes_arcsecond_error(monkeypatch, tmp_path):
|
|
359
363
|
"""
|
|
360
364
|
Construction failures (e.g. unreachable host) must surface as
|
|
361
365
|
``ArcsecondError`` — clean message, non-zero exit, no stack trace.
|
|
@@ -101,33 +101,6 @@ def prepare_upload_files(config, dataset_uuid, telescope_uuid, org_subdomain="")
|
|
|
101
101
|
)
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
def prepare_upload_allskyimages(config, camera_uuid, org_subdomain=""):
|
|
105
|
-
respx.get(
|
|
106
|
-
"/".join(
|
|
107
|
-
[
|
|
108
|
-
part
|
|
109
|
-
for part in [
|
|
110
|
-
config.api_server,
|
|
111
|
-
org_subdomain,
|
|
112
|
-
"allskycameras",
|
|
113
|
-
camera_uuid,
|
|
114
|
-
]
|
|
115
|
-
if part
|
|
116
|
-
]
|
|
117
|
-
)
|
|
118
|
-
+ "/"
|
|
119
|
-
).mock(return_value=Response(201, json={"status": "success", "uuid": camera_uuid}))
|
|
120
|
-
if org_subdomain:
|
|
121
|
-
respx.get(
|
|
122
|
-
"/".join([config.api_server, "organisations", org_subdomain]) + "/"
|
|
123
|
-
).mock(
|
|
124
|
-
return_value=Response(
|
|
125
|
-
200,
|
|
126
|
-
json={"subdomain": org_subdomain, "name": "dummy org"},
|
|
127
|
-
)
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
|
|
131
104
|
def save_test_credentials(api_name, username, memberships=None):
|
|
132
105
|
config = ArcsecondConfig(api_name=api_name)
|
|
133
106
|
config.save(username=username)
|