arcsecond 3.7.2__tar.gz → 3.8.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.8.0/.docker/Dockerfile_postgres +2 -0
- arcsecond-3.8.0/.docker/Dockerfile_redis +2 -0
- arcsecond-3.8.0/.github/dependabot.yml +11 -0
- arcsecond-3.8.0/.github/workflows/docsdeploy.yml +30 -0
- arcsecond-3.8.0/.github/workflows/pythonpublish.yml +57 -0
- arcsecond-3.8.0/.github/workflows/tests.yml +90 -0
- arcsecond-3.8.0/.gitignore +22 -0
- arcsecond-3.8.0/Makefile +63 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/PKG-INFO +10 -16
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/__init__.py +10 -1
- arcsecond-3.8.0/arcsecond/__version__.py +11 -0
- arcsecond-3.8.0/arcsecond/api/__init__.py +11 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/api/endpoint.py +77 -4
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/api/main.py +5 -0
- arcsecond-3.8.0/arcsecond/api/resources.py +114 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cli.py +4 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/docker/docker-compose.yml +6 -0
- arcsecond-3.8.0/arcsecond/hosting/postgres/init-db.sh +8 -0
- arcsecond-3.8.0/arcsecond/targets.py +211 -0
- arcsecond-3.8.0/arcsecond/webcam/commands.py +101 -0
- arcsecond-3.8.0/arcsecond/webcam/proxy.py +135 -0
- arcsecond-3.8.0/deploy.sh +30 -0
- arcsecond-3.8.0/docs/.vitepress/config.js +21 -0
- arcsecond-3.8.0/docs/.vitepress/theme/custom.css +4 -0
- arcsecond-3.8.0/docs/.vitepress/theme/index.js +4 -0
- arcsecond-3.8.0/docs/api-basics.md +91 -0
- arcsecond-3.8.0/docs/img/logo-circle.png +0 -0
- arcsecond-3.8.0/docs/index.md +156 -0
- arcsecond-3.8.0/docs/install.md +67 -0
- arcsecond-3.8.0/docs/resources.md +335 -0
- arcsecond-3.8.0/examples/example_upload_files.py +25 -0
- arcsecond-3.8.0/examples/example_upload_images.py +24 -0
- arcsecond-3.8.0/package-lock.json +2627 -0
- arcsecond-3.8.0/package.json +27 -0
- arcsecond-3.8.0/poetry.lock +471 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/pyproject.toml +6 -1
- arcsecond-3.8.0/requirements.txt +3 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/setup.cfg +0 -5
- arcsecond-3.8.0/sonar-project.properties +23 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/api/test_api_endpoint.py +50 -0
- arcsecond-3.8.0/tests/api/test_targets.py +196 -0
- arcsecond-3.8.0/tests/cloud/uploader/allskycameraimages/__init__.py +0 -0
- arcsecond-3.8.0/tests/cloud/uploader/datafiles/__init__.py +0 -0
- arcsecond-3.8.0/tests/conftest.py +54 -0
- arcsecond-3.8.0/tests/fixtures/file1.fits +0 -0
- arcsecond-3.8.0/tests/test_targets_planning.py +86 -0
- arcsecond-3.8.0/tests/utils.py +141 -0
- arcsecond-3.7.2/arcsecond/__version__.py +0 -2
- arcsecond-3.7.2/arcsecond/api/__init__.py +0 -5
- arcsecond-3.7.2/arcsecond.egg-info/PKG-INFO +0 -86
- arcsecond-3.7.2/arcsecond.egg-info/SOURCES.txt +0 -78
- arcsecond-3.7.2/arcsecond.egg-info/dependency_links.txt +0 -1
- arcsecond-3.7.2/arcsecond.egg-info/entry_points.txt +0 -2
- arcsecond-3.7.2/arcsecond.egg-info/not-zip-safe +0 -1
- arcsecond-3.7.2/arcsecond.egg-info/requires.txt +0 -7
- arcsecond-3.7.2/arcsecond.egg-info/top_level.txt +0 -2
- {arcsecond-3.7.2 → arcsecond-3.8.0}/LICENSE +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/README.md +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/api/config.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/api/constants.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/auth.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/resources.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/allskycameraimages/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/allskycameraimages/context.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/allskycameraimages/errors.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/allskycameraimages/uploader.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/allskycameraimages/utils.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/constants.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/context.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/datafiles/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/datafiles/context.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/datafiles/errors.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/datafiles/uploader.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/datafiles/utils.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/errors.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/logger.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/uploader.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/utils.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploader/walker.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/cloud/uploads.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/errors.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/checks.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/constants.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/docker/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/docker/constants.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/docker/containers.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/docker/images.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/docker/utils.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/keygen/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/keygen/client.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/keygen/utils.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/local.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/main.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/setup.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/utils.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/hosting/validation.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/arcsecond/options.py +0 -0
- {arcsecond-3.7.2/tests/api → arcsecond-3.8.0/arcsecond/webcam}/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/setup.py +0 -0
- {arcsecond-3.7.2/tests/cloud → arcsecond-3.8.0/tests}/__init__.py +0 -0
- {arcsecond-3.7.2/tests/cloud/uploader → arcsecond-3.8.0/tests/api}/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/api/test_api.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/api/test_config.py +0 -0
- {arcsecond-3.7.2/tests/cloud/uploader/allskycameraimages → arcsecond-3.8.0/tests/cloud}/__init__.py +0 -0
- {arcsecond-3.7.2/tests/cloud/uploader/datafiles → arcsecond-3.8.0/tests/cloud/uploader}/__init__.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/cloud/uploader/allskycameraimages/test_context.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/cloud/uploader/allskycameraimages/test_uploader_full_process.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/cloud/uploader/datafiles/test_uploader_errors.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/cloud/uploader/datafiles/test_uploader_full_process.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/cloud/uploader/datafiles/test_uploader_init.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/cloud/uploader/datafiles/test_uploader_prepare.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/cloud/uploader/datafiles/test_uploader_upload.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/test_cli.py +0 -0
- {arcsecond-3.7.2 → arcsecond-3.8.0}/tests/test_hosting_local.py +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "pip"
|
|
9
|
+
directory: "/" # Location of package manifests
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Deploy Docs
|
|
2
|
+
|
|
3
|
+
on: [push]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v4
|
|
10
|
+
- name: Setup node
|
|
11
|
+
uses: actions/setup-node@v4
|
|
12
|
+
with:
|
|
13
|
+
node-version: '20.x'
|
|
14
|
+
- name: Cache node modules
|
|
15
|
+
uses: actions/cache@v4
|
|
16
|
+
with:
|
|
17
|
+
path: ~/.npm
|
|
18
|
+
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
|
19
|
+
restore-keys: |
|
|
20
|
+
${{ runner.os }}-node-
|
|
21
|
+
- run: npm ci
|
|
22
|
+
env:
|
|
23
|
+
NODE_AUTH_TOKEN: ${{ secrets.READ_PACKAGE_TOKEN }}
|
|
24
|
+
CI: true
|
|
25
|
+
node-version: '20.x'
|
|
26
|
+
cache: 'npm'
|
|
27
|
+
- name: Deploy docs
|
|
28
|
+
env:
|
|
29
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
30
|
+
run: ./deploy.sh
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Upload Python Package to Pypi
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [ published ]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
deploy:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
id-token: write
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Validate release tag and version (X.Y.Z and matches pyproject)
|
|
16
|
+
shell: bash
|
|
17
|
+
run: |
|
|
18
|
+
TAG="${{ github.event.release.tag_name }}"
|
|
19
|
+
echo "Release tag: $TAG"
|
|
20
|
+
if ! [[ "$TAG" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
21
|
+
echo "Tag must be X.Y.Z (e.g. 1.2.3). Got: $TAG"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Validate pyproject.toml version matches tag
|
|
28
|
+
run: |
|
|
29
|
+
python - <<'PY'
|
|
30
|
+
import pathlib
|
|
31
|
+
import tomllib
|
|
32
|
+
|
|
33
|
+
tag = "${{ github.event.release.tag_name }}"
|
|
34
|
+
|
|
35
|
+
data = tomllib.loads(pathlib.Path("pyproject.toml").read_text(encoding="utf-8"))
|
|
36
|
+
py_ver = data["project"]["version"]
|
|
37
|
+
|
|
38
|
+
print(f"pyproject.toml version: {py_ver}")
|
|
39
|
+
if py_ver != tag:
|
|
40
|
+
raise SystemExit(f"Version mismatch: tag={tag} but pyproject.toml={py_ver}")
|
|
41
|
+
PY
|
|
42
|
+
|
|
43
|
+
- name: Set up Python
|
|
44
|
+
uses: actions/setup-python@v5
|
|
45
|
+
with:
|
|
46
|
+
python-version: '3.12'
|
|
47
|
+
|
|
48
|
+
- name: Build distributions
|
|
49
|
+
run: |
|
|
50
|
+
python -m pip install --upgrade pip
|
|
51
|
+
pip install build
|
|
52
|
+
python -m build
|
|
53
|
+
|
|
54
|
+
- name: Publish to PyPI (OIDC)
|
|
55
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
56
|
+
with:
|
|
57
|
+
packages-dir: dist
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
name: Run Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ '*' ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ '*' ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
tests:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: [ '3.10', '3.11', '3.12', '3.13' ]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v4
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- 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 .
|
|
30
|
+
|
|
31
|
+
- name: Test with coverage (only on 3.12)
|
|
32
|
+
if: matrix.python-version == '3.12'
|
|
33
|
+
run: |
|
|
34
|
+
pytest -vvs tests/ --cov=arcsecond --cov-report=xml:coverage.xml
|
|
35
|
+
|
|
36
|
+
- name: Test without coverage
|
|
37
|
+
if: matrix.python-version != '3.12'
|
|
38
|
+
run: pytest -vvs tests/
|
|
39
|
+
|
|
40
|
+
- name: Upload coverage artifact (only on 3.12)
|
|
41
|
+
if: matrix.python-version == '3.12'
|
|
42
|
+
uses: actions/upload-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: coverage
|
|
45
|
+
path: coverage.xml
|
|
46
|
+
|
|
47
|
+
scan:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
needs: tests
|
|
50
|
+
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
|
|
54
|
+
- name: Download coverage artifact
|
|
55
|
+
uses: actions/download-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: coverage
|
|
58
|
+
path: .
|
|
59
|
+
|
|
60
|
+
- name: SonarQube Scan
|
|
61
|
+
uses: SonarSource/sonarcloud-github-action@master
|
|
62
|
+
env:
|
|
63
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
64
|
+
|
|
65
|
+
lint:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v4
|
|
69
|
+
|
|
70
|
+
- name: Set up Python
|
|
71
|
+
uses: actions/setup-python@v4
|
|
72
|
+
with:
|
|
73
|
+
python-version: '3.12'
|
|
74
|
+
|
|
75
|
+
- name: Install dependencies
|
|
76
|
+
run: |
|
|
77
|
+
python -m pip install --upgrade pip
|
|
78
|
+
pip install flake8 black isort
|
|
79
|
+
|
|
80
|
+
- name: Lint with flake8
|
|
81
|
+
run: |
|
|
82
|
+
flake8 arcsecond tests --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
83
|
+
|
|
84
|
+
- name: Check formatting with black
|
|
85
|
+
run: |
|
|
86
|
+
black --check arcsecond tests
|
|
87
|
+
|
|
88
|
+
- name: Check imports with isort
|
|
89
|
+
run: |
|
|
90
|
+
isort --check-only --profile black arcsecond tests
|
arcsecond-3.8.0/Makefile
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
ARCH ?= amd64
|
|
2
|
+
TAG ?= latest
|
|
3
|
+
|
|
4
|
+
IMAGE_API = arcsecond-api-linux-$(ARCH)
|
|
5
|
+
IMAGE_WEB = arcsecond-web-linux-$(ARCH)
|
|
6
|
+
IMAGE_REDIS = arcsecond-redis-linux-$(ARCH)
|
|
7
|
+
IMAGE_POSTGRES = arcsecond-postgres-linux-$(ARCH)
|
|
8
|
+
|
|
9
|
+
IMAGE_API_OCI = arcsecond-api
|
|
10
|
+
IMAGE_WEB_OCI = arcsecond-web
|
|
11
|
+
IMAGE_REDIS_OCI = arcsecond-redis
|
|
12
|
+
IMAGE_POSTGRES_OCI = arcsecond-postgres
|
|
13
|
+
|
|
14
|
+
# Paths
|
|
15
|
+
DOCKERFILE_API = ../arcsecond-back/.docker/Dockerfile
|
|
16
|
+
CONTEXT_API = ../arcsecond-back
|
|
17
|
+
|
|
18
|
+
DOCKERFILE_WEB = ../arcsecond-front/.docker/Dockerfile
|
|
19
|
+
CONTEXT_WEB = ../arcsecond-front
|
|
20
|
+
|
|
21
|
+
DOCKERFILE_REDIS = ./.docker/Dockerfile_redis
|
|
22
|
+
DOCKERFILE_POSTGRES = ./.docker/Dockerfile_postgres
|
|
23
|
+
|
|
24
|
+
# Build and save each image
|
|
25
|
+
|
|
26
|
+
arcsecond-api-linux:
|
|
27
|
+
docker build --platform linux/$(ARCH) -t $(IMAGE_API):$(TAG) -f $(DOCKERFILE_API) $(CONTEXT_API)
|
|
28
|
+
docker save $(IMAGE_API):$(TAG) -o tars/$(IMAGE_API)_$(TAG).tar
|
|
29
|
+
|
|
30
|
+
arcsecond-web-linux:
|
|
31
|
+
docker build --platform linux/$(ARCH) -t $(IMAGE_WEB):$(TAG) -f $(DOCKERFILE_WEB) $(CONTEXT_WEB)
|
|
32
|
+
docker save $(IMAGE_WEB):$(TAG) -o tars/$(IMAGE_WEB)_$(TAG).tar
|
|
33
|
+
|
|
34
|
+
arcsecond-redis-linux:
|
|
35
|
+
docker build --platform linux/$(ARCH) -t $(IMAGE_REDIS):7.4 -f $(DOCKERFILE_REDIS) .
|
|
36
|
+
docker save $(IMAGE_REDIS):7.4 -o tars/$(IMAGE_REDIS)_7.4.tar
|
|
37
|
+
|
|
38
|
+
arcsecond-postgres-linux:
|
|
39
|
+
docker build --platform linux/$(ARCH) -t $(IMAGE_POSTGRES):16 -f $(DOCKERFILE_POSTGRES) .
|
|
40
|
+
docker save $(IMAGE_POSTGRES):16 -o tars/$(IMAGE_POSTGRES)_16.tar
|
|
41
|
+
|
|
42
|
+
arcsecond-api-oci:
|
|
43
|
+
docker buildx build --platform 'linux/amd64,linux/arm64' -o 'type=oci,dest=-' -t $(IMAGE_API_OCI):$(TAG) -f $(DOCKERFILE_API) $(CONTEXT_API) > tars/$(IMAGE_API_OCI)_$(TAG).tar
|
|
44
|
+
|
|
45
|
+
arcsecond-web-oci:
|
|
46
|
+
docker buildx build --platform 'linux/amd64,linux/arm64' -o 'type=oci,dest=-' -t $(IMAGE_WEB_OCI):$(TAG) -f $(DOCKERFILE_WEB) $(CONTEXT_WEB) > tars/$(IMAGE_WEB_OCI)_$(TAG).tar
|
|
47
|
+
|
|
48
|
+
arcsecond-redis-oci:
|
|
49
|
+
docker buildx build --platform 'linux/amd64,linux/arm64' -o 'type=oci,dest=-' -t $(IMAGE_REDIS_OCI):$(TAG) -f $(DOCKERFILE_REDIS) . > tars/$(IMAGE_REDIS_OCI)_$(TAG).tar
|
|
50
|
+
|
|
51
|
+
arcsecond-postgres-oci:
|
|
52
|
+
docker buildx build --platform 'linux/amd64,linux/arm64' -o 'type=oci,dest=-' -t $(IMAGE_POSTGRES_OCI):$(TAG) -f $(DOCKERFILE_POSTGRES) . > tars/$(IMAGE_POSTGRES_OCI)_$(TAG).tar
|
|
53
|
+
|
|
54
|
+
.PHONY: core aux clean
|
|
55
|
+
|
|
56
|
+
core: arcsecond-api-linux arcsecond-web-linux
|
|
57
|
+
|
|
58
|
+
aux: arcsecond-redis-linux arcsecond-postgres-linux
|
|
59
|
+
|
|
60
|
+
oci: arcsecond-api-oci arcsecond-web-oci arcsecond-redis-oci arcsecond-postgres-oci
|
|
61
|
+
|
|
62
|
+
clean:
|
|
63
|
+
rm -f arcsecond-*.tar tars/*
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arcsecond
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.8.0
|
|
4
4
|
Summary: CLI for arcsecond.io
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Project-URL: Homepage, https://github.com/arcsecond-io/cli
|
|
6
|
+
Project-URL: Issues, https://github.com/arcsecond-io/cli/issues
|
|
7
|
+
Project-URL: Documentation, https://docs.arcsecond.io
|
|
7
8
|
Author-email: Cedric Foellmi <cedric@arcsecond.io>
|
|
8
9
|
License: MIT License
|
|
9
10
|
|
|
@@ -26,11 +27,7 @@ License: MIT License
|
|
|
26
27
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
28
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
29
|
SOFTWARE.
|
|
29
|
-
|
|
30
|
-
Project-URL: Homepage, https://github.com/arcsecond-io/cli
|
|
31
|
-
Project-URL: Issues, https://github.com/arcsecond-io/cli/issues
|
|
32
|
-
Project-URL: Documentation, https://docs.arcsecond.io
|
|
33
|
-
Platform: any
|
|
30
|
+
License-File: LICENSE
|
|
34
31
|
Classifier: Development Status :: 5 - Production/Stable
|
|
35
32
|
Classifier: Environment :: Console
|
|
36
33
|
Classifier: Intended Audience :: Developers
|
|
@@ -39,19 +36,16 @@ Classifier: Operating System :: OS Independent
|
|
|
39
36
|
Classifier: Programming Language :: Python :: 3
|
|
40
37
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
41
38
|
Requires-Python: >=3.9
|
|
42
|
-
|
|
43
|
-
License-File: LICENSE
|
|
39
|
+
Requires-Dist: aiohttp>=3.9
|
|
44
40
|
Requires-Dist: click>=8
|
|
45
41
|
Requires-Dist: configparser
|
|
46
42
|
Requires-Dist: docker
|
|
47
43
|
Requires-Dist: httpx
|
|
48
|
-
Requires-Dist:
|
|
44
|
+
Requires-Dist: opencv-python-headless<5,>=4.10
|
|
49
45
|
Requires-Dist: py-machineid
|
|
46
|
+
Requires-Dist: tqdm<5.0.0,>=4.67.1
|
|
50
47
|
Requires-Dist: wait-for-it
|
|
51
|
-
|
|
52
|
-
Dynamic: home-page
|
|
53
|
-
Dynamic: license-file
|
|
54
|
-
Dynamic: platform
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
55
49
|
|
|
56
50
|
[](https://github.com/arcsecond-io/cli/actions/workflows/docsdeploy.yml) [](http://pepy.tech/project/arcsecond)
|
|
57
51
|
[](https://app.fossa.com/projects/git%2Bgithub.com%2Farcsecond-io%2Fcli?ref=badge_shield)
|
|
@@ -83,4 +77,4 @@ env/bin/activate` is needed when you restart a debugging session.
|
|
|
83
77
|
|
|
84
78
|
|
|
85
79
|
## License
|
|
86
|
-
[](https://app.fossa.com/projects/git%2Bgithub.com%2Farcsecond-io%2Fcli?ref=badge_large)
|
|
80
|
+
[](https://app.fossa.com/projects/git%2Bgithub.com%2Farcsecond-io%2Fcli?ref=badge_large)
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
from .api import
|
|
1
|
+
from .api import (
|
|
2
|
+
ArcsecondAPI,
|
|
3
|
+
ArcsecondAPIEndpoint,
|
|
4
|
+
ArcsecondConfig,
|
|
5
|
+
ArcsecondTargetListsResource,
|
|
6
|
+
)
|
|
2
7
|
from .cloud.uploader import (
|
|
3
8
|
AllSkyCameraImageFileUploader,
|
|
4
9
|
AllSkyCameraImageUploadContext,
|
|
@@ -7,6 +12,7 @@ from .cloud.uploader import (
|
|
|
7
12
|
)
|
|
8
13
|
from .cloud.uploader.walker import walk_folder_and_upload_files
|
|
9
14
|
from .errors import ArcsecondError
|
|
15
|
+
from .targets import ArcsecondTargetPayloadPlan, plan_target_payload
|
|
10
16
|
|
|
11
17
|
name = "arcsecond"
|
|
12
18
|
|
|
@@ -15,9 +21,12 @@ __all__ = [
|
|
|
15
21
|
"ArcsecondError",
|
|
16
22
|
"ArcsecondConfig",
|
|
17
23
|
"ArcsecondAPIEndpoint",
|
|
24
|
+
"ArcsecondTargetListsResource",
|
|
25
|
+
"ArcsecondTargetPayloadPlan",
|
|
18
26
|
"DatasetUploadContext",
|
|
19
27
|
"DatasetFileUploader",
|
|
20
28
|
"AllSkyCameraImageFileUploader",
|
|
21
29
|
"AllSkyCameraImageUploadContext",
|
|
30
|
+
"plan_target_payload",
|
|
22
31
|
"walk_folder_and_upload_files",
|
|
23
32
|
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Version is defined in pyproject.toml (project.version).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
__version__ = version("arcsecond")
|
|
9
|
+
except PackageNotFoundError:
|
|
10
|
+
# Fallback for running from a source checkout without installing.
|
|
11
|
+
__version__ = "0.0.0"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .config import ArcsecondConfig
|
|
2
|
+
from .endpoint import ArcsecondAPIEndpoint
|
|
3
|
+
from .main import ArcsecondAPI
|
|
4
|
+
from .resources import ArcsecondTargetListsResource
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"ArcsecondAPI",
|
|
8
|
+
"ArcsecondConfig",
|
|
9
|
+
"ArcsecondAPIEndpoint",
|
|
10
|
+
"ArcsecondTargetListsResource",
|
|
11
|
+
]
|
|
@@ -12,6 +12,13 @@ WRITABLE_MEMBERSHIPS = ["superadmin", "admin", "member"]
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class ArcsecondAPIEndpoint(object):
|
|
15
|
+
"""
|
|
16
|
+
Generic REST endpoint wrapper for Arcsecond resources.
|
|
17
|
+
|
|
18
|
+
It owns transport-level CRUD plus resource-agnostic conveniences such as
|
|
19
|
+
payload merging, `find_one()`, and `upsert()`.
|
|
20
|
+
"""
|
|
21
|
+
|
|
15
22
|
def __init__(
|
|
16
23
|
self,
|
|
17
24
|
config: ArcsecondConfig,
|
|
@@ -57,6 +64,30 @@ class ArcsecondAPIEndpoint(object):
|
|
|
57
64
|
def _detail_url(self, uuid_or_id):
|
|
58
65
|
return self._build_url(self.__path, str(uuid_or_id))
|
|
59
66
|
|
|
67
|
+
def _build_payload(self, json=None, **fields):
|
|
68
|
+
payload = {}
|
|
69
|
+
if json:
|
|
70
|
+
payload.update(json)
|
|
71
|
+
payload.update({key: value for key, value in fields.items() if value is not None})
|
|
72
|
+
return payload or None
|
|
73
|
+
|
|
74
|
+
def _extract_results(self, response):
|
|
75
|
+
if isinstance(response, dict):
|
|
76
|
+
if isinstance(response.get("results"), list):
|
|
77
|
+
return response["results"]
|
|
78
|
+
if response:
|
|
79
|
+
return [response]
|
|
80
|
+
elif isinstance(response, list):
|
|
81
|
+
return response
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
def _extract_identifier(self, resource, identifier_fields=("uuid", "id", "pk")):
|
|
85
|
+
for key in identifier_fields:
|
|
86
|
+
value = resource.get(key)
|
|
87
|
+
if value is not None:
|
|
88
|
+
return value
|
|
89
|
+
return None
|
|
90
|
+
|
|
60
91
|
def list(self, **filters):
|
|
61
92
|
return self._perform_request(self._list_url(**filters), "get")
|
|
62
93
|
|
|
@@ -65,16 +96,20 @@ class ArcsecondAPIEndpoint(object):
|
|
|
65
96
|
self._detail_url(id_name_uuid), "get", headers=headers
|
|
66
97
|
)
|
|
67
98
|
|
|
68
|
-
def create(self, json=None, files=None, headers=None):
|
|
99
|
+
def create(self, json=None, files=None, headers=None, **fields):
|
|
69
100
|
return self._perform_request(
|
|
70
|
-
self._list_url(),
|
|
101
|
+
self._list_url(),
|
|
102
|
+
"post",
|
|
103
|
+
json=self._build_payload(json=json, **fields),
|
|
104
|
+
files=files,
|
|
105
|
+
headers=headers,
|
|
71
106
|
)
|
|
72
107
|
|
|
73
|
-
def update(self, id_name_uuid, json=None, files=None, headers=None):
|
|
108
|
+
def update(self, id_name_uuid, json=None, files=None, headers=None, **fields):
|
|
74
109
|
return self._perform_request(
|
|
75
110
|
self._detail_url(id_name_uuid),
|
|
76
111
|
"patch",
|
|
77
|
-
json=json,
|
|
112
|
+
json=self._build_payload(json=json, **fields),
|
|
78
113
|
files=files,
|
|
79
114
|
headers=headers,
|
|
80
115
|
)
|
|
@@ -82,6 +117,44 @@ class ArcsecondAPIEndpoint(object):
|
|
|
82
117
|
def delete(self, id_name_uuid):
|
|
83
118
|
return self._perform_request(self._detail_url(id_name_uuid), "delete")
|
|
84
119
|
|
|
120
|
+
def find_one(self, **filters):
|
|
121
|
+
response, error = self.list(**filters)
|
|
122
|
+
if error:
|
|
123
|
+
return None, error
|
|
124
|
+
|
|
125
|
+
results = self._extract_results(response)
|
|
126
|
+
if len(results) == 0:
|
|
127
|
+
return None, None
|
|
128
|
+
if len(results) > 1:
|
|
129
|
+
return (
|
|
130
|
+
None,
|
|
131
|
+
ArcsecondError(
|
|
132
|
+
f"Expected one '{self.path}' match for filters {filters}, got {len(results)}."
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
return results[0], None
|
|
136
|
+
|
|
137
|
+
def upsert(self, match_field="name", json=None, **fields):
|
|
138
|
+
payload = self._build_payload(json=json, **fields)
|
|
139
|
+
if payload is None:
|
|
140
|
+
return None, ArcsecondError("Cannot upsert an empty payload.")
|
|
141
|
+
|
|
142
|
+
match_value = payload.get(match_field)
|
|
143
|
+
if match_value in (None, ""):
|
|
144
|
+
return self.create(json=payload)
|
|
145
|
+
|
|
146
|
+
existing, error = self.find_one(**{match_field: match_value})
|
|
147
|
+
if error:
|
|
148
|
+
return None, error
|
|
149
|
+
if existing is None:
|
|
150
|
+
return self.create(json=payload)
|
|
151
|
+
|
|
152
|
+
identifier = self._extract_identifier(existing)
|
|
153
|
+
if identifier is None:
|
|
154
|
+
return None, ArcsecondError(f"Could not find an identifier for '{match_value}'.")
|
|
155
|
+
|
|
156
|
+
return self.update(identifier, json=payload)
|
|
157
|
+
|
|
85
158
|
def _perform_request(self, url, method_name, json=None, files=None, headers=None):
|
|
86
159
|
if self.__config.verbose:
|
|
87
160
|
click.echo(f"Sending {method_name} request to {url}")
|
|
@@ -8,6 +8,7 @@ from arcsecond.options import State
|
|
|
8
8
|
from .config import ArcsecondConfig
|
|
9
9
|
from .constants import API_AUTH_PATH_VERIFY
|
|
10
10
|
from .endpoint import ArcsecondAPIEndpoint
|
|
11
|
+
from .resources import ArcsecondTargetListsResource
|
|
11
12
|
|
|
12
13
|
__all__ = [
|
|
13
14
|
"ArcsecondAPI",
|
|
@@ -40,6 +41,10 @@ class ArcsecondAPI(object):
|
|
|
40
41
|
self.calibrations = ArcsecondAPIEndpoint(
|
|
41
42
|
self.config, "calibrations", self.subdomain
|
|
42
43
|
)
|
|
44
|
+
self.targets = ArcsecondAPIEndpoint(self.config, "targets", self.subdomain)
|
|
45
|
+
self.targetlists = ArcsecondTargetListsResource(
|
|
46
|
+
self.config, "targetlists", self.subdomain
|
|
47
|
+
)
|
|
43
48
|
|
|
44
49
|
self.datapackages = ArcsecondAPIEndpoint(
|
|
45
50
|
self.config, "datapackages", self.subdomain
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from arcsecond.errors import ArcsecondError
|
|
2
|
+
|
|
3
|
+
from .endpoint import ArcsecondAPIEndpoint
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ArcsecondTargetListsResource(ArcsecondAPIEndpoint):
|
|
7
|
+
"""Target-list specific helpers built on top of the generic endpoint contract."""
|
|
8
|
+
|
|
9
|
+
target_relation_keys = ("targets", "target_uuids", "target_ids")
|
|
10
|
+
|
|
11
|
+
def _ensure_iterable(self, values):
|
|
12
|
+
if values is None:
|
|
13
|
+
return None
|
|
14
|
+
if isinstance(values, (str, int)):
|
|
15
|
+
return [values]
|
|
16
|
+
return list(values)
|
|
17
|
+
|
|
18
|
+
def _normalise_target_references(self, targets):
|
|
19
|
+
values = self._ensure_iterable(targets)
|
|
20
|
+
if values is None:
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
refs = []
|
|
24
|
+
for target in values:
|
|
25
|
+
if isinstance(target, dict):
|
|
26
|
+
ref = (
|
|
27
|
+
target.get("uuid")
|
|
28
|
+
or target.get("id")
|
|
29
|
+
or target.get("pk")
|
|
30
|
+
or target.get("name")
|
|
31
|
+
)
|
|
32
|
+
if ref is None:
|
|
33
|
+
raise ArcsecondError(
|
|
34
|
+
"Target dictionaries must include one of: uuid, id, pk or name."
|
|
35
|
+
)
|
|
36
|
+
refs.append(ref)
|
|
37
|
+
else:
|
|
38
|
+
refs.append(target)
|
|
39
|
+
return refs
|
|
40
|
+
|
|
41
|
+
def _target_key_from_payload(self, payload, target_key=None):
|
|
42
|
+
if target_key:
|
|
43
|
+
return target_key
|
|
44
|
+
for key in self.target_relation_keys:
|
|
45
|
+
if payload and key in payload:
|
|
46
|
+
return key
|
|
47
|
+
return self.target_relation_keys[0]
|
|
48
|
+
|
|
49
|
+
def _build_payload(self, json=None, targets=None, target_key=None, **fields):
|
|
50
|
+
payload = super()._build_payload(json=json, **fields) or {}
|
|
51
|
+
normalised_targets = self._normalise_target_references(targets)
|
|
52
|
+
if normalised_targets is not None:
|
|
53
|
+
payload[self._target_key_from_payload(payload, target_key=target_key)] = (
|
|
54
|
+
normalised_targets
|
|
55
|
+
)
|
|
56
|
+
return payload or None
|
|
57
|
+
|
|
58
|
+
def create(self, json=None, targets=None, target_key=None, **fields):
|
|
59
|
+
payload = self._build_payload(
|
|
60
|
+
json=json, targets=targets, target_key=target_key, **fields
|
|
61
|
+
)
|
|
62
|
+
return ArcsecondAPIEndpoint.create(self, json=payload)
|
|
63
|
+
|
|
64
|
+
def update(self, id_name_uuid, json=None, targets=None, target_key=None, **fields):
|
|
65
|
+
payload = self._build_payload(
|
|
66
|
+
json=json, targets=targets, target_key=target_key, **fields
|
|
67
|
+
)
|
|
68
|
+
return ArcsecondAPIEndpoint.update(self, id_name_uuid, json=payload)
|
|
69
|
+
|
|
70
|
+
def upsert(self, match_field="name", json=None, targets=None, target_key=None, **fields):
|
|
71
|
+
payload = self._build_payload(
|
|
72
|
+
json=json, targets=targets, target_key=target_key, **fields
|
|
73
|
+
)
|
|
74
|
+
return super().upsert(match_field=match_field, json=payload)
|
|
75
|
+
|
|
76
|
+
def _read_target_refs(self, target_list, target_key=None):
|
|
77
|
+
key = self._target_key_from_payload(target_list or {}, target_key=target_key)
|
|
78
|
+
raw_targets = (target_list or {}).get(key, [])
|
|
79
|
+
refs = self._normalise_target_references(raw_targets) or []
|
|
80
|
+
return key, refs
|
|
81
|
+
|
|
82
|
+
def set_targets(self, id_name_uuid, targets, target_key=None):
|
|
83
|
+
target_key = self._target_key_from_payload({}, target_key=target_key)
|
|
84
|
+
return self.update(id_name_uuid, **{target_key: self._normalise_target_references(targets)})
|
|
85
|
+
|
|
86
|
+
def clear_targets(self, id_name_uuid, target_key=None):
|
|
87
|
+
return self.set_targets(id_name_uuid, [], target_key=target_key)
|
|
88
|
+
|
|
89
|
+
def add_targets(self, id_name_uuid, targets, target_key=None):
|
|
90
|
+
target_list, error = self.read(id_name_uuid)
|
|
91
|
+
if error:
|
|
92
|
+
return None, error
|
|
93
|
+
|
|
94
|
+
key, current_refs = self._read_target_refs(target_list, target_key=target_key)
|
|
95
|
+
for ref in self._normalise_target_references(targets) or []:
|
|
96
|
+
if ref not in current_refs:
|
|
97
|
+
current_refs.append(ref)
|
|
98
|
+
return self.update(id_name_uuid, **{key: current_refs})
|
|
99
|
+
|
|
100
|
+
def remove_targets(self, id_name_uuid, targets, target_key=None):
|
|
101
|
+
target_list, error = self.read(id_name_uuid)
|
|
102
|
+
if error:
|
|
103
|
+
return None, error
|
|
104
|
+
|
|
105
|
+
key, current_refs = self._read_target_refs(target_list, target_key=target_key)
|
|
106
|
+
refs_to_remove = set(self._normalise_target_references(targets) or [])
|
|
107
|
+
remaining_refs = [ref for ref in current_refs if ref not in refs_to_remove]
|
|
108
|
+
return self.update(id_name_uuid, **{key: remaining_refs})
|
|
109
|
+
|
|
110
|
+
def add_target(self, id_name_uuid, target, target_key=None):
|
|
111
|
+
return self.add_targets(id_name_uuid, [target], target_key=target_key)
|
|
112
|
+
|
|
113
|
+
def remove_target(self, id_name_uuid, target, target_key=None):
|
|
114
|
+
return self.remove_targets(id_name_uuid, [target], target_key=target_key)
|
|
@@ -10,6 +10,7 @@ from arcsecond.cloud import (
|
|
|
10
10
|
upload_data,
|
|
11
11
|
)
|
|
12
12
|
from arcsecond.hosting import setup
|
|
13
|
+
from arcsecond.webcam import commands as webcam
|
|
13
14
|
|
|
14
15
|
from . import __version__
|
|
15
16
|
from .options import State
|
|
@@ -59,3 +60,6 @@ main.add_command(upload_data)
|
|
|
59
60
|
|
|
60
61
|
# Allow to try arcsecond by installing a local version
|
|
61
62
|
main.add_command(setup)
|
|
63
|
+
|
|
64
|
+
# Native webcam proxy — lets Docker containers reach USB webcams on the host.
|
|
65
|
+
main.add_command(webcam.webcam)
|
|
@@ -35,6 +35,12 @@ services:
|
|
|
35
35
|
depends_on:
|
|
36
36
|
- db
|
|
37
37
|
- broker
|
|
38
|
+
# Allows the backend to reach the host machine via host.docker.internal.
|
|
39
|
+
# Required on Linux; Docker Desktop on Windows/macOS adds this automatically.
|
|
40
|
+
# Used by the webcam proxy: set WEBCAM_PROXY_URL=http://host.docker.internal:8765
|
|
41
|
+
# in your .env file and run `arcsecond webcam start` on the host.
|
|
42
|
+
extra_hosts:
|
|
43
|
+
- "host.docker.internal:host-gateway"
|
|
38
44
|
env_file:
|
|
39
45
|
# You must have a .env file with secret keys beside this yml file.
|
|
40
46
|
- .env
|