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.
Files changed (126) hide show
  1. {arcsecond-3.12.0 → arcsecond-3.13.0}/.github/workflows/pythonpublish.yml +5 -7
  2. {arcsecond-3.12.0 → arcsecond-3.13.0}/.github/workflows/tests.yml +15 -23
  3. {arcsecond-3.12.0 → arcsecond-3.13.0}/PKG-INFO +10 -11
  4. {arcsecond-3.12.0 → arcsecond-3.13.0}/README.md +10 -11
  5. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/__init__.py +0 -4
  6. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/alpaca/commands.py +3 -9
  7. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/alpaca/dome_probe.py +5 -6
  8. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/main.py +0 -4
  9. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cli.py +0 -4
  10. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/__init__.py +1 -3
  11. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/resources.py +0 -35
  12. arcsecond-3.13.0/arcsecond/cloud/uploader/__init__.py +8 -0
  13. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/commands.py +1 -1
  14. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/.vitepress/config.js +1 -0
  15. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/resources.md +0 -1
  16. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/upload.md +2 -40
  17. {arcsecond-3.12.0 → arcsecond-3.13.0}/pyproject.toml +11 -1
  18. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/test_alpaca_probe.py +12 -8
  19. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/utils.py +0 -27
  20. arcsecond-3.13.0/uv.lock +2488 -0
  21. arcsecond-3.12.0/arcsecond/cloud/uploader/__init__.py +0 -13
  22. arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages/context.py +0 -62
  23. arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages/errors.py +0 -19
  24. arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages/uploader.py +0 -29
  25. arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages/utils.py +0 -54
  26. arcsecond-3.12.0/examples/example_upload_images.py +0 -24
  27. arcsecond-3.12.0/poetry.lock +0 -471
  28. arcsecond-3.12.0/tests/cloud/uploader/allskycameraimages/__init__.py +0 -0
  29. arcsecond-3.12.0/tests/cloud/uploader/allskycameraimages/test_context.py +0 -115
  30. arcsecond-3.12.0/tests/cloud/uploader/allskycameraimages/test_uploader_full_process.py +0 -85
  31. arcsecond-3.12.0/tests/cloud/uploader/datafiles/__init__.py +0 -0
  32. {arcsecond-3.12.0 → arcsecond-3.13.0}/.docker/Dockerfile_postgres +0 -0
  33. {arcsecond-3.12.0 → arcsecond-3.13.0}/.docker/Dockerfile_redis +0 -0
  34. {arcsecond-3.12.0 → arcsecond-3.13.0}/.github/dependabot.yml +0 -0
  35. {arcsecond-3.12.0 → arcsecond-3.13.0}/.github/workflows/docsdeploy.yml +0 -0
  36. {arcsecond-3.12.0 → arcsecond-3.13.0}/.gitignore +0 -0
  37. {arcsecond-3.12.0 → arcsecond-3.13.0}/LICENSE +0 -0
  38. {arcsecond-3.12.0 → arcsecond-3.13.0}/Makefile +0 -0
  39. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/__version__.py +0 -0
  40. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/alpaca/__init__.py +0 -0
  41. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/__init__.py +0 -0
  42. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/config.py +0 -0
  43. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/constants.py +0 -0
  44. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/endpoint.py +0 -0
  45. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/api/resources.py +0 -0
  46. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/auth.py +0 -0
  47. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/constants.py +0 -0
  48. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/context.py +0 -0
  49. {arcsecond-3.12.0/arcsecond/cloud/uploader/allskycameraimages → arcsecond-3.13.0/arcsecond/cloud/uploader/datafiles}/__init__.py +0 -0
  50. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/context.py +0 -0
  51. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/errors.py +0 -0
  52. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/uploader.py +0 -0
  53. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/utils.py +0 -0
  54. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/errors.py +0 -0
  55. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/logger.py +0 -0
  56. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/uploader.py +0 -0
  57. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/utils.py +0 -0
  58. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/walker.py +0 -0
  59. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/cloud/uploads.py +0 -0
  60. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/errors.py +0 -0
  61. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/__init__.py +0 -0
  62. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/backups.py +0 -0
  63. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/checks.py +0 -0
  64. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/constants.py +0 -0
  65. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/__init__.py +0 -0
  66. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/constants.py +0 -0
  67. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/containers.py +0 -0
  68. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/docker-compose.yml +0 -0
  69. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/images.py +0 -0
  70. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/utils.py +0 -0
  71. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/__init__.py +0 -0
  72. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/client.py +0 -0
  73. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/utils.py +0 -0
  74. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/local.py +0 -0
  75. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/main.py +0 -0
  76. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/postgres/init-db.sh +0 -0
  77. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/setup.py +0 -0
  78. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/utils.py +0 -0
  79. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/hosting/validation.py +0 -0
  80. {arcsecond-3.12.0/arcsecond/cloud/uploader/datafiles → arcsecond-3.13.0/arcsecond/imagesources}/__init__.py +0 -0
  81. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/proxy.py +0 -0
  82. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/registry.py +0 -0
  83. {arcsecond-3.12.0/arcsecond/imagesources → arcsecond-3.13.0/arcsecond/imagesources/sources}/__init__.py +0 -0
  84. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/base.py +0 -0
  85. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/filewatch.py +0 -0
  86. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/opencv.py +0 -0
  87. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/options.py +0 -0
  88. {arcsecond-3.12.0 → arcsecond-3.13.0}/arcsecond/targets.py +0 -0
  89. {arcsecond-3.12.0 → arcsecond-3.13.0}/deploy.sh +0 -0
  90. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/.vitepress/theme/custom.css +0 -0
  91. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/.vitepress/theme/index.js +0 -0
  92. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/api-basics.md +0 -0
  93. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/backups.md +0 -0
  94. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/img/icon-logo.png +0 -0
  95. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/index.md +0 -0
  96. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/install-windows.md +0 -0
  97. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/install.md +0 -0
  98. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/public/img/icon-logo.png +0 -0
  99. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/rotate-postgres-password.md +0 -0
  100. {arcsecond-3.12.0 → arcsecond-3.13.0}/docs/webcam.md +0 -0
  101. {arcsecond-3.12.0 → arcsecond-3.13.0}/examples/example_upload_files.py +0 -0
  102. {arcsecond-3.12.0 → arcsecond-3.13.0}/package-lock.json +0 -0
  103. {arcsecond-3.12.0 → arcsecond-3.13.0}/package.json +0 -0
  104. {arcsecond-3.12.0 → arcsecond-3.13.0}/requirements.txt +0 -0
  105. {arcsecond-3.12.0 → arcsecond-3.13.0}/setup.cfg +0 -0
  106. {arcsecond-3.12.0 → arcsecond-3.13.0}/setup.py +0 -0
  107. {arcsecond-3.12.0 → arcsecond-3.13.0}/sonar-project.properties +0 -0
  108. {arcsecond-3.12.0/arcsecond/imagesources/sources → arcsecond-3.13.0/tests}/__init__.py +0 -0
  109. {arcsecond-3.12.0/tests → arcsecond-3.13.0/tests/api}/__init__.py +0 -0
  110. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/api/test_api.py +0 -0
  111. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/api/test_api_endpoint.py +0 -0
  112. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/api/test_config.py +0 -0
  113. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/api/test_targets.py +0 -0
  114. {arcsecond-3.12.0/tests/api → arcsecond-3.13.0/tests/cloud}/__init__.py +0 -0
  115. {arcsecond-3.12.0/tests/cloud → arcsecond-3.13.0/tests/cloud/uploader}/__init__.py +0 -0
  116. {arcsecond-3.12.0/tests/cloud/uploader → arcsecond-3.13.0/tests/cloud/uploader/datafiles}/__init__.py +0 -0
  117. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_errors.py +0 -0
  118. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_full_process.py +0 -0
  119. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_init.py +0 -0
  120. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_prepare.py +0 -0
  121. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_upload.py +0 -0
  122. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/conftest.py +0 -0
  123. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/fixtures/file1.fits +0 -0
  124. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/test_cli.py +0 -0
  125. {arcsecond-3.12.0 → arcsecond-3.13.0}/tests/test_hosting_local.py +0 -0
  126. {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: Set up Python
29
- uses: actions/setup-python@v6
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: Set up Python ${{ matrix.python-version }}
20
- uses: actions/setup-python@v6
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@v5
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: Set up Python
71
- uses: actions/setup-python@v6
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.12.0
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
- To start developing the arcsecond CLI, fork the project, `git clone` it,
66
- then, in the arcsecond-cli folder, do
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
- $ virtualenv --python=pythonX.Y env
72
- $ source env/bin/activate
73
- $ pip install -e .
74
- ```
70
+ $ uv sync --extra webcam --group dev
71
+ $ uv run arcsecond --help
72
+ $ uv run pytest
73
+ ```
75
74
 
76
- The last line ensure you can call the "locally installed" version of the
77
- code of that folder. Once done one first time, only the `source
78
- env/bin/activate` is needed when you restart a debugging session.
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
- To start developing the arcsecond CLI, fork the project, `git clone` it,
15
- then, in the arcsecond-cli folder, do
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
- $ virtualenv --python=pythonX.Y env
21
- $ source env/bin/activate
22
- $ pip install -e .
23
- ```
24
-
25
- The last line ensure you can call the "locally installed" version of the
26
- code of that folder. Once done one first time, only the `source
27
- env/bin/activate` is needed when you restart a debugging session.
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", connected_outcome["ok"],
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 allskycameras, datasets, telescopes
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)
@@ -0,0 +1,8 @@
1
+ from .datafiles.context import DatasetUploadContext
2
+ from .datafiles.uploader import DatasetFileUploader
3
+ from .datafiles.utils import display_upload_datafiles_command_summary
4
+
5
+ __all__ = [
6
+ "DatasetUploadContext",
7
+ "DatasetFileUploader",
8
+ ]
@@ -120,7 +120,7 @@ def _add_options(options):
120
120
  # ---------------------------------------------------------------------------
121
121
 
122
122
 
123
- @click.group(help="Manage the native live-image proxy (USB webcams + all-sky).")
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
  ]
@@ -30,7 +30,6 @@ These methods are available on resources such as:
30
30
  - `api.datafiles`
31
31
  - `api.datapackages`
32
32
  - `api.observingsites`
33
- - `api.allskycameras`
34
33
 
35
34
  ## Generic CRUD Example
36
35
 
@@ -4,8 +4,8 @@ sidebar: true
4
4
 
5
5
  # Data Upload
6
6
 
7
- Arcsecond CLI makes it easy to upload dataset files and all-sky camera images to
8
- your account or to an observatory portal.
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.12.0"
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 ("environment", "device_metadata", "supported_actions", "command_passthrough"):
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 dome.command_blind_calls, "CommandBlind should have been invoked under --allow-active"
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
- data["command_passthrough"]["command_blind_skipped_reason"]
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)