aceiot-models-cli 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aceiot_models_cli-0.4.0/.github/workflows/release.yml +87 -0
- aceiot_models_cli-0.4.0/.github/workflows/tests.yml +123 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/hive.db-wal +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.pre-commit-config.yaml +13 -6
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/CHANGELOG.md +27 -0
- aceiot_models_cli-0.4.0/CONTRIBUTING.md +133 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/PKG-INFO +11 -3
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/README.md +5 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/pyproject.toml +21 -7
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/cli.py +34 -24
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/config.py +3 -1
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/repl/core.py +8 -2
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/repl/executor.py +58 -24
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/repl/volttron_repl.py +25 -18
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/test_serializers.py +11 -11
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/utils/api_helpers.py +8 -8
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/volttron_commands.py +38 -33
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_config.py +8 -2
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_error_handling.py +12 -15
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_utils.py +9 -7
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_volttron_commands.py +11 -19
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_volttron_directory_upload.py +8 -10
- aceiot_models_cli-0.4.0/tox.ini +48 -0
- aceiot_models_cli-0.3.2/EXECUTIVE_SUMMARY.md +0 -151
- aceiot_models_cli-0.3.2/EXTRACTION_SUMMARY.md +0 -60
- aceiot_models_cli-0.3.2/IMPLEMENTATION_SUMMARY.md +0 -81
- aceiot_models_cli-0.3.2/IMPLEMENTATION_UPDATE_SUMMARY.md +0 -73
- aceiot_models_cli-0.3.2/MISSING_FEATURES.md +0 -53
- aceiot_models_cli-0.3.2/NEW_USE_CASES.md +0 -137
- aceiot_models_cli-0.3.2/PYPI_SETUP.md +0 -120
- aceiot_models_cli-0.3.2/REFACTORING_COMPLETE.md +0 -49
- aceiot_models_cli-0.3.2/REPL_IMPLEMENTATION_SPECIFICATION.md +0 -304
- aceiot_models_cli-0.3.2/ROADMAP.md +0 -172
- aceiot_models_cli-0.3.2/TEST_PLAN.md +0 -245
- aceiot_models_cli-0.3.2/UX_SPECIFICATION_REPL_MODE.md +0 -802
- aceiot_models_cli-0.3.2/VOLTTRON_CLI_UX_DESIGN.md +0 -261
- aceiot_models_cli-0.3.2/VOLTTRON_DEPLOYMENT_USER_FLOW.md +0 -127
- aceiot_models_cli-0.3.2/VOLTTRON_DESIGN_SUMMARY.md +0 -64
- aceiot_models_cli-0.3.2/VOLTTRON_IMPLEMENTATION_SUMMARY.md +0 -128
- aceiot_models_cli-0.3.2/VOLTTRON_REPL_UX_DESIGN.md +0 -318
- aceiot_models_cli-0.3.2/ace-api-kit +0 -1
- aceiot_models_cli-0.3.2/datamover/README.md +0 -52
- aceiot_models_cli-0.3.2/datamover/config +0 -6
- aceiot_models_cli-0.3.2/datamover/conftest.py +0 -6
- aceiot_models_cli-0.3.2/datamover/datamover/__init__.py +0 -0
- aceiot_models_cli-0.3.2/datamover/datamover/agent.py +0 -265
- aceiot_models_cli-0.3.2/datamover/setup.py +0 -72
- aceiot_models_cli-0.3.2/datamover/tests/test_datamover.py +0 -589
- aceiot_models_cli-0.3.2/datamover 3/README.md +0 -52
- aceiot_models_cli-0.3.2/datamover 3/config +0 -6
- aceiot_models_cli-0.3.2/datamover 3/conftest.py +0 -6
- aceiot_models_cli-0.3.2/datamover 3/datamover/__init__.py +0 -0
- aceiot_models_cli-0.3.2/datamover 3/datamover/agent.py +0 -265
- aceiot_models_cli-0.3.2/datamover 3/setup.py +0 -72
- aceiot_models_cli-0.3.2/datamover 3/tests/test_datamover.py +0 -589
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.github/workflows/publish.yml +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.github/workflows/test.yml +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.gitignore +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/config.json +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/hive.db +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/hive.db-shm +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/memory.db +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/sessions/session-1753211745508-yuylk957g-auto-save-1753211775510.json +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/sessions/session-1753218717423-518j1u7mc-auto-save-1753218747424.json +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/sessions/session-1753226483889-l6t8sliyv-auto-save-1753226513890.json +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/sessions/session-1753244606046-hwadu65zx-auto-save-1753244636047.json +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/sessions/session-1753281994715-c69q6r8uu-auto-save-1753282024716.json +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.hive-mind/sessions/session-1753300987160-kalws26dy-auto-save-1753301017161.json +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/.python-version +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/ACEIOT_MODELS_REQUIREMENTS.md +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/LICENSE +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/MANIFEST.in +0 -0
- /aceiot_models_cli-0.3.2/adr_demo_container-2025-05-01-2025-07-14.csv → /aceiot_models_cli-0.4.0/adr_demo_container-2025-05-01-2025-06-14.parquet +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/example-config.yaml +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/__init__.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/formatters.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/repl/__init__.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/repl/context.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/repl/parser.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/src/aceiot_models_cli/utils/__init__.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/__init__.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/conftest.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_cli.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_client_commands.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_point_commands.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_repl.py +0 -0
- {aceiot_models_cli-0.3.2 → aceiot_models_cli-0.4.0}/tests/test_site_commands.py +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
dry_run:
|
|
9
|
+
description: 'Dry run (do not publish)'
|
|
10
|
+
required: false
|
|
11
|
+
default: true
|
|
12
|
+
type: boolean
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test-matrix:
|
|
16
|
+
name: Test Python ${{ matrix.python-version }}
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
fail-fast: true
|
|
20
|
+
matrix:
|
|
21
|
+
# NOTE: Currently only testing on Python 3.13 because aceiot-models 0.2.7
|
|
22
|
+
# requires Python>=3.13. Once aceiot-models is updated to support Python 3.10+,
|
|
23
|
+
# we can re-enable testing on: ["3.10", "3.11", "3.12", "3.13"]
|
|
24
|
+
python-version: ["3.13"]
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
30
|
+
uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: ${{ matrix.python-version }}
|
|
33
|
+
|
|
34
|
+
- name: Install uv
|
|
35
|
+
uses: astral-sh/setup-uv@v4
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: |
|
|
39
|
+
uv pip install --system -e ".[dev]"
|
|
40
|
+
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: |
|
|
43
|
+
uv run pytest tests -v
|
|
44
|
+
|
|
45
|
+
build-and-publish:
|
|
46
|
+
name: Build and Publish
|
|
47
|
+
needs: test-matrix
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
permissions:
|
|
50
|
+
id-token: write
|
|
51
|
+
contents: read
|
|
52
|
+
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@v4
|
|
55
|
+
|
|
56
|
+
- name: Set up Python
|
|
57
|
+
uses: actions/setup-python@v5
|
|
58
|
+
with:
|
|
59
|
+
python-version: '3.13'
|
|
60
|
+
|
|
61
|
+
- name: Install uv
|
|
62
|
+
uses: astral-sh/setup-uv@v4
|
|
63
|
+
|
|
64
|
+
- name: Build package
|
|
65
|
+
run: |
|
|
66
|
+
uv build
|
|
67
|
+
|
|
68
|
+
- name: Check package
|
|
69
|
+
run: |
|
|
70
|
+
uv pip install --system twine
|
|
71
|
+
twine check dist/*
|
|
72
|
+
|
|
73
|
+
- name: List distribution files
|
|
74
|
+
run: |
|
|
75
|
+
ls -la dist/
|
|
76
|
+
|
|
77
|
+
- name: Publish to PyPI
|
|
78
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.dry_run)
|
|
79
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
80
|
+
with:
|
|
81
|
+
verbose: true
|
|
82
|
+
|
|
83
|
+
- name: Dry run notification
|
|
84
|
+
if: github.event_name == 'workflow_dispatch' && inputs.dry_run
|
|
85
|
+
run: |
|
|
86
|
+
echo "This was a dry run. Package was built but not published."
|
|
87
|
+
echo "To publish, run the workflow again with dry_run set to false."
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, develop ]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
name: Test Python ${{ matrix.python-version }}
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@v4
|
|
29
|
+
with:
|
|
30
|
+
enable-cache: true
|
|
31
|
+
cache-dependency-glob: |
|
|
32
|
+
pyproject.toml
|
|
33
|
+
requirements*.txt
|
|
34
|
+
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: |
|
|
37
|
+
uv pip install --system -e ".[dev]"
|
|
38
|
+
|
|
39
|
+
- name: Run tests with pytest
|
|
40
|
+
run: |
|
|
41
|
+
uv run pytest tests -v --cov=aceiot_models_cli --cov-report=term-missing --cov-report=xml
|
|
42
|
+
|
|
43
|
+
- name: Upload coverage to Codecov
|
|
44
|
+
if: matrix.python-version == '3.13'
|
|
45
|
+
uses: codecov/codecov-action@v4
|
|
46
|
+
with:
|
|
47
|
+
file: ./coverage.xml
|
|
48
|
+
fail_ci_if_error: false
|
|
49
|
+
verbose: true
|
|
50
|
+
|
|
51
|
+
lint:
|
|
52
|
+
name: Lint
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v4
|
|
57
|
+
|
|
58
|
+
- name: Set up Python
|
|
59
|
+
uses: actions/setup-python@v5
|
|
60
|
+
with:
|
|
61
|
+
python-version: '3.13'
|
|
62
|
+
|
|
63
|
+
- name: Install uv
|
|
64
|
+
uses: astral-sh/setup-uv@v4
|
|
65
|
+
|
|
66
|
+
- name: Install dependencies
|
|
67
|
+
run: |
|
|
68
|
+
uv pip install --system ruff
|
|
69
|
+
|
|
70
|
+
- name: Run ruff check
|
|
71
|
+
run: |
|
|
72
|
+
uv run ruff check src tests
|
|
73
|
+
|
|
74
|
+
- name: Run ruff format check
|
|
75
|
+
run: |
|
|
76
|
+
uv run ruff format --check src tests
|
|
77
|
+
|
|
78
|
+
type-check:
|
|
79
|
+
name: Type Check
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
|
|
82
|
+
steps:
|
|
83
|
+
- uses: actions/checkout@v4
|
|
84
|
+
|
|
85
|
+
- name: Set up Python
|
|
86
|
+
uses: actions/setup-python@v5
|
|
87
|
+
with:
|
|
88
|
+
python-version: '3.13'
|
|
89
|
+
|
|
90
|
+
- name: Install uv
|
|
91
|
+
uses: astral-sh/setup-uv@v4
|
|
92
|
+
|
|
93
|
+
- name: Install dependencies
|
|
94
|
+
run: |
|
|
95
|
+
uv pip install --system -e ".[dev]"
|
|
96
|
+
uv pip install --system pyright
|
|
97
|
+
|
|
98
|
+
- name: Run pyright
|
|
99
|
+
run: |
|
|
100
|
+
uv run pyright src
|
|
101
|
+
|
|
102
|
+
tox:
|
|
103
|
+
name: Tox Test Suite
|
|
104
|
+
runs-on: ubuntu-latest
|
|
105
|
+
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/checkout@v4
|
|
108
|
+
|
|
109
|
+
- name: Set up Python
|
|
110
|
+
uses: actions/setup-python@v5
|
|
111
|
+
with:
|
|
112
|
+
python-version: '3.13'
|
|
113
|
+
|
|
114
|
+
- name: Install uv
|
|
115
|
+
uses: astral-sh/setup-uv@v4
|
|
116
|
+
|
|
117
|
+
- name: Install tox and tox-uv
|
|
118
|
+
run: |
|
|
119
|
+
uv pip install --system tox tox-uv
|
|
120
|
+
|
|
121
|
+
- name: Run tox
|
|
122
|
+
run: |
|
|
123
|
+
uv run tox
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
-
rev:
|
|
3
|
+
rev: v5.0.0
|
|
4
4
|
hooks:
|
|
5
5
|
- id: trailing-whitespace
|
|
6
6
|
- id: end-of-file-fixer
|
|
@@ -13,15 +13,22 @@ repos:
|
|
|
13
13
|
- id: mixed-line-ending
|
|
14
14
|
|
|
15
15
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
16
|
-
rev: v0.
|
|
16
|
+
rev: v0.8.7
|
|
17
17
|
hooks:
|
|
18
18
|
- id: ruff
|
|
19
19
|
args: [--fix]
|
|
20
20
|
- id: ruff-format
|
|
21
21
|
|
|
22
|
-
- repo: https://github.com/
|
|
23
|
-
rev: v1.
|
|
22
|
+
- repo: https://github.com/RobertCraigie/pyright-python
|
|
23
|
+
rev: v1.1.401
|
|
24
24
|
hooks:
|
|
25
|
-
- id:
|
|
26
|
-
|
|
25
|
+
- id: pyright
|
|
26
|
+
language_version: python3.10
|
|
27
|
+
additional_dependencies:
|
|
28
|
+
- aceiot-models>=0.2.4
|
|
29
|
+
- click>=8.2.1
|
|
30
|
+
- requests>=2.31.0
|
|
31
|
+
- rich>=13.7.0
|
|
32
|
+
- pyyaml>=6.0.1
|
|
33
|
+
- pandas>=2.0.0
|
|
27
34
|
exclude: ^tests/
|
|
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.4.0] - 2025-07-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Full Python 3.10+ support now that aceiot-models 0.3.3 supports it
|
|
14
|
+
- Multi-version Python testing infrastructure with tox (3.10, 3.11, 3.12, 3.13)
|
|
15
|
+
- GitHub Actions CI/CD workflow for automated testing across all Python versions
|
|
16
|
+
- Pre-commit hooks for code quality checks
|
|
17
|
+
- CONTRIBUTING.md with development and testing guidelines
|
|
18
|
+
- Automated release workflow with testing
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Updated requires-python from >=3.13 to >=3.10
|
|
22
|
+
- Fixed datetime.UTC usage for Python 3.10 compatibility (using timezone.utc)
|
|
23
|
+
- Updated development dependencies to include tox and pre-commit
|
|
24
|
+
- Enhanced CI/CD pipeline infrastructure for multi-version testing
|
|
25
|
+
- Updated aceiot-models dependency to 0.3.3
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Removed local path override for aceiot-models dependency
|
|
29
|
+
- Fixed CI/CD workflows to use uv run for all commands
|
|
30
|
+
- Updated test to match new APIClient._request method name
|
|
31
|
+
- Resolved variable shadowing in deploy function
|
|
32
|
+
- Fixed missing Any import in volttron_commands
|
|
33
|
+
- Resolved all pyright type checking errors
|
|
34
|
+
|
|
8
35
|
## [0.3.2] - 2025-07-26
|
|
9
36
|
|
|
10
37
|
### Changed
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Contributing to ACE IoT Models CLI
|
|
2
|
+
|
|
3
|
+
We welcome contributions to the ACE IoT Models CLI! This document provides guidelines for contributing to the project.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
- Python 3.10 or higher
|
|
10
|
+
- [uv](https://github.com/astral-sh/uv) (recommended) or pip
|
|
11
|
+
|
|
12
|
+
### Setting up your development environment
|
|
13
|
+
|
|
14
|
+
1. Clone the repository:
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/ACE-IoT-Solutions/aceiot-models-cli.git
|
|
17
|
+
cd aceiot-models-cli
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. Install the package in development mode with dev dependencies:
|
|
21
|
+
```bash
|
|
22
|
+
# Using uv (recommended)
|
|
23
|
+
uv pip install -e ".[dev]"
|
|
24
|
+
|
|
25
|
+
# Or using pip
|
|
26
|
+
pip install -e ".[dev]"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Testing
|
|
30
|
+
|
|
31
|
+
We test against multiple Python versions to ensure compatibility. All code must pass tests on Python 3.10, 3.11, 3.12, and 3.13.
|
|
32
|
+
|
|
33
|
+
### Running tests locally
|
|
34
|
+
|
|
35
|
+
#### Using pytest directly:
|
|
36
|
+
```bash
|
|
37
|
+
# Run all tests
|
|
38
|
+
pytest
|
|
39
|
+
|
|
40
|
+
# Run with coverage
|
|
41
|
+
pytest --cov=aceiot_models_cli --cov-report=term-missing
|
|
42
|
+
|
|
43
|
+
# Run specific test file
|
|
44
|
+
pytest tests/test_cli.py
|
|
45
|
+
|
|
46
|
+
# Run with verbose output
|
|
47
|
+
pytest -v
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### Using tox for multi-version testing:
|
|
51
|
+
```bash
|
|
52
|
+
# Install tox if not already installed
|
|
53
|
+
uv pip install tox tox-uv
|
|
54
|
+
|
|
55
|
+
# Run tests on all Python versions
|
|
56
|
+
tox
|
|
57
|
+
|
|
58
|
+
# Run tests on specific Python version
|
|
59
|
+
tox -e py310
|
|
60
|
+
|
|
61
|
+
# Run only linting
|
|
62
|
+
tox -e lint
|
|
63
|
+
|
|
64
|
+
# Run only type checking
|
|
65
|
+
tox -e type
|
|
66
|
+
|
|
67
|
+
# Run formatting
|
|
68
|
+
tox -e format
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Code Quality
|
|
72
|
+
|
|
73
|
+
Before submitting a pull request, ensure your code meets our quality standards:
|
|
74
|
+
|
|
75
|
+
1. **Formatting**: Code must be formatted with ruff
|
|
76
|
+
```bash
|
|
77
|
+
ruff format src tests
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
2. **Linting**: Code must pass ruff checks
|
|
81
|
+
```bash
|
|
82
|
+
ruff check src tests
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
3. **Type Checking**: Code must pass pyright type checks
|
|
86
|
+
```bash
|
|
87
|
+
pyright src
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
4. **Tests**: All tests must pass
|
|
91
|
+
```bash
|
|
92
|
+
pytest
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
You can run all checks at once using tox:
|
|
96
|
+
```bash
|
|
97
|
+
tox
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Pull Request Process
|
|
101
|
+
|
|
102
|
+
1. Fork the repository and create your branch from `main`
|
|
103
|
+
2. Make your changes and add tests for new functionality
|
|
104
|
+
3. Ensure all tests pass on all supported Python versions
|
|
105
|
+
4. Update documentation as needed
|
|
106
|
+
5. Submit a pull request with a clear description of your changes
|
|
107
|
+
|
|
108
|
+
### CI/CD
|
|
109
|
+
|
|
110
|
+
Our GitHub Actions workflow will automatically:
|
|
111
|
+
- Run tests on Python 3.10, 3.11, 3.12, and 3.13
|
|
112
|
+
- Check code formatting with ruff
|
|
113
|
+
- Run type checking with pyright
|
|
114
|
+
- Generate coverage reports
|
|
115
|
+
|
|
116
|
+
All checks must pass before a PR can be merged.
|
|
117
|
+
|
|
118
|
+
## Release Process
|
|
119
|
+
|
|
120
|
+
Releases are managed through GitHub releases and automatically published to PyPI. Only maintainers can create releases.
|
|
121
|
+
|
|
122
|
+
### Creating a Release
|
|
123
|
+
|
|
124
|
+
1. Update version in `pyproject.toml`
|
|
125
|
+
2. Update `CHANGELOG.md` with release notes
|
|
126
|
+
3. Commit changes: `git commit -m "chore: bump version to X.Y.Z"`
|
|
127
|
+
4. Create and push tag: `git tag vX.Y.Z && git push origin vX.Y.Z`
|
|
128
|
+
5. Create GitHub release
|
|
129
|
+
6. GitHub Actions will automatically run tests and publish to PyPI
|
|
130
|
+
|
|
131
|
+
## Questions?
|
|
132
|
+
|
|
133
|
+
If you have questions, please open an issue on GitHub.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aceiot-models-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Command-line interface for ACE IoT Aerodrome API using aceiot-models package
|
|
5
5
|
Project-URL: Homepage, https://github.com/ACE-IoT-Solutions/aceiot-models-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/ACE-IoT-Solutions/aceiot-models-cli
|
|
@@ -13,11 +13,14 @@ Classifier: Development Status :: 4 - Beta
|
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
20
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
18
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
-
Requires-Python: >=3.
|
|
20
|
-
Requires-Dist: aceiot-models
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: aceiot-models==0.3.3
|
|
21
24
|
Requires-Dist: click-repl>=0.3.0
|
|
22
25
|
Requires-Dist: click>=8.2.1
|
|
23
26
|
Requires-Dist: email-validator>=2.1.0
|
|
@@ -34,6 +37,11 @@ Description-Content-Type: text/markdown
|
|
|
34
37
|
|
|
35
38
|
A command-line interface for interacting with the ACE IoT API using the `aceiot-models` package. This CLI provides comprehensive access to ACE IoT's fleet management capabilities for IoT devices, sites, and data points.
|
|
36
39
|
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- Python 3.10 or higher
|
|
43
|
+
- An ACE IoT API key
|
|
44
|
+
|
|
37
45
|
## Features
|
|
38
46
|
|
|
39
47
|
- Complete CLI for ACE IoT API operations
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
A command-line interface for interacting with the ACE IoT API using the `aceiot-models` package. This CLI provides comprehensive access to ACE IoT's fleet management capabilities for IoT devices, sites, and data points.
|
|
4
4
|
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Python 3.10 or higher
|
|
8
|
+
- An ACE IoT API key
|
|
9
|
+
|
|
5
10
|
## Features
|
|
6
11
|
|
|
7
12
|
- Complete CLI for ACE IoT API operations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "aceiot-models-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Command-line interface for ACE IoT Aerodrome API using aceiot-models package"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -12,14 +12,17 @@ classifiers = [
|
|
|
12
12
|
"Intended Audience :: Developers",
|
|
13
13
|
"License :: OSI Approved :: MIT License",
|
|
14
14
|
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
15
18
|
"Programming Language :: Python :: 3.13",
|
|
16
19
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
17
20
|
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
18
21
|
]
|
|
19
22
|
keywords = ["aceiot", "iot", "api", "cli", "bacnet"]
|
|
20
|
-
requires-python = ">=3.
|
|
23
|
+
requires-python = ">=3.10"
|
|
21
24
|
dependencies = [
|
|
22
|
-
"aceiot-models
|
|
25
|
+
"aceiot-models==0.3.3",
|
|
23
26
|
"click>=8.2.1",
|
|
24
27
|
"requests>=2.31.0",
|
|
25
28
|
"requests-toolbelt>=1.0.0",
|
|
@@ -50,11 +53,14 @@ dev = [
|
|
|
50
53
|
"pytest>=8.4.1",
|
|
51
54
|
"pytest-cov>=6.0.0",
|
|
52
55
|
"ruff>=0.12.4",
|
|
56
|
+
"tox>=4.0.0",
|
|
57
|
+
"tox-uv>=1.0.0",
|
|
58
|
+
"pre-commit>=3.0.0",
|
|
53
59
|
]
|
|
54
60
|
|
|
55
61
|
[tool.ruff]
|
|
56
62
|
line-length = 100
|
|
57
|
-
target-version = "
|
|
63
|
+
target-version = "py310"
|
|
58
64
|
fix = true
|
|
59
65
|
|
|
60
66
|
[tool.ruff.lint]
|
|
@@ -89,11 +95,19 @@ addopts = [
|
|
|
89
95
|
]
|
|
90
96
|
|
|
91
97
|
[tool.mypy]
|
|
92
|
-
python_version = "3.
|
|
98
|
+
python_version = "3.10"
|
|
93
99
|
warn_return_any = true
|
|
94
100
|
warn_unused_configs = true
|
|
95
101
|
disallow_untyped_defs = true
|
|
96
102
|
strict_optional = true
|
|
97
103
|
|
|
98
|
-
[tool.
|
|
99
|
-
|
|
104
|
+
[tool.pyright]
|
|
105
|
+
pythonVersion = "3.10"
|
|
106
|
+
typeCheckingMode = "basic"
|
|
107
|
+
exclude = [
|
|
108
|
+
"**/test_serializers.py",
|
|
109
|
+
"tests/**",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
# [tool.uv.sources]
|
|
113
|
+
# aceiot-models = { path = "../aceiot-models" } # For local development only
|
|
@@ -180,6 +180,7 @@ def create_client(
|
|
|
180
180
|
|
|
181
181
|
# Convert model to dict for API
|
|
182
182
|
from aceiot_models.serializers import serialize_for_api
|
|
183
|
+
|
|
183
184
|
client_dict = serialize_for_api(client_data)
|
|
184
185
|
result = client.create_client(client_dict)
|
|
185
186
|
print_success(f"Client '{name}' created successfully")
|
|
@@ -222,11 +223,7 @@ def list_sites(
|
|
|
222
223
|
# Handle client_name filtering differently
|
|
223
224
|
if client_name:
|
|
224
225
|
# Use get_client_sites for specific client
|
|
225
|
-
result = client.get_client_sites(
|
|
226
|
-
client_name=client_name,
|
|
227
|
-
page=page,
|
|
228
|
-
per_page=per_page
|
|
229
|
-
)
|
|
226
|
+
result = client.get_client_sites(client_name=client_name, page=page, per_page=per_page)
|
|
230
227
|
else:
|
|
231
228
|
# Get all sites
|
|
232
229
|
result = client.get_sites(
|
|
@@ -317,8 +314,13 @@ def get_site(ctx: Context, site_name: str) -> None:
|
|
|
317
314
|
@click.option("--start", required=True, help="Start time (ISO format, e.g., 2024-01-01T00:00:00Z)")
|
|
318
315
|
@click.option("--end", required=True, help="End time (ISO format, e.g., 2024-01-02T00:00:00Z)")
|
|
319
316
|
@click.option("--output-file", "-o", help="Output file path (defaults to <site>-<start>-<end>.csv)")
|
|
320
|
-
@click.option(
|
|
321
|
-
|
|
317
|
+
@click.option(
|
|
318
|
+
"--format",
|
|
319
|
+
"-f",
|
|
320
|
+
type=click.Choice(["csv", "parquet", "auto"]),
|
|
321
|
+
default="auto",
|
|
322
|
+
help="Output format (auto detects from file extension, defaults to csv)",
|
|
323
|
+
)
|
|
322
324
|
@click.option("--include-metadata", is_flag=True, help="Include point metadata in output")
|
|
323
325
|
@click.pass_context
|
|
324
326
|
def site_timeseries(
|
|
@@ -371,8 +373,8 @@ def site_timeseries(
|
|
|
371
373
|
format = "csv" # Default to CSV when no file specified
|
|
372
374
|
|
|
373
375
|
# Clean up timestamps for filename (remove colons and timezone indicators)
|
|
374
|
-
start_clean = start.replace(
|
|
375
|
-
end_clean = end.replace(
|
|
376
|
+
start_clean = start.replace(":", "-").replace("Z", "").replace("+", "_")
|
|
377
|
+
end_clean = end.replace(":", "-").replace("Z", "").replace("+", "_")
|
|
376
378
|
|
|
377
379
|
# Use appropriate extension based on format
|
|
378
380
|
extension = ".parquet" if format == "parquet" else ".csv"
|
|
@@ -391,7 +393,7 @@ def site_timeseries(
|
|
|
391
393
|
format = "csv"
|
|
392
394
|
# Add .csv extension if missing
|
|
393
395
|
if not output_path.suffix:
|
|
394
|
-
output_path = output_path.with_suffix(
|
|
396
|
+
output_path = output_path.with_suffix(".csv")
|
|
395
397
|
output_file = str(output_path)
|
|
396
398
|
|
|
397
399
|
# Ensure output_path is always a Path object
|
|
@@ -400,15 +402,17 @@ def site_timeseries(
|
|
|
400
402
|
try:
|
|
401
403
|
# Validate time format
|
|
402
404
|
try:
|
|
403
|
-
datetime.fromisoformat(start.replace(
|
|
404
|
-
datetime.fromisoformat(end.replace(
|
|
405
|
+
datetime.fromisoformat(start.replace("Z", "+00:00"))
|
|
406
|
+
datetime.fromisoformat(end.replace("Z", "+00:00"))
|
|
405
407
|
except ValueError as e:
|
|
406
408
|
print_error(f"Invalid time format: {e}")
|
|
407
409
|
print_error("Use ISO format, e.g., 2024-01-01T00:00:00Z")
|
|
408
410
|
ctx.exit(1)
|
|
409
411
|
|
|
410
412
|
# Fetch timeseries data directly using site endpoint
|
|
411
|
-
console.print(
|
|
413
|
+
console.print(
|
|
414
|
+
f"[cyan]Fetching timeseries data for site '{site_name}' from {start} to {end}...[/cyan]"
|
|
415
|
+
)
|
|
412
416
|
|
|
413
417
|
with console.status("Loading data...", spinner="dots"):
|
|
414
418
|
response = client.get_site_timeseries(site_name, start, end)
|
|
@@ -420,7 +424,9 @@ def site_timeseries(
|
|
|
420
424
|
|
|
421
425
|
# Get unique points from the data
|
|
422
426
|
unique_points = {sample.get("name") for sample in all_samples if sample.get("name")}
|
|
423
|
-
console.print(
|
|
427
|
+
console.print(
|
|
428
|
+
f"[green]✓ Fetched {len(all_samples)} data samples from {len(unique_points)} points[/green]"
|
|
429
|
+
)
|
|
424
430
|
|
|
425
431
|
# If metadata is requested, fetch point details
|
|
426
432
|
point_metadata = {}
|
|
@@ -466,13 +472,15 @@ def site_timeseries(
|
|
|
466
472
|
# Add metadata if requested
|
|
467
473
|
if include_metadata and sample.get("name") in point_metadata:
|
|
468
474
|
metadata = point_metadata[sample.get("name")]
|
|
469
|
-
row.update(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
475
|
+
row.update(
|
|
476
|
+
{
|
|
477
|
+
"point_id": metadata["id"],
|
|
478
|
+
"site": metadata["site"],
|
|
479
|
+
"point_type": metadata["point_type"],
|
|
480
|
+
"unit": metadata["unit"],
|
|
481
|
+
"description": metadata["description"],
|
|
482
|
+
}
|
|
483
|
+
)
|
|
476
484
|
|
|
477
485
|
df_data.append(row)
|
|
478
486
|
|
|
@@ -496,7 +504,9 @@ def site_timeseries(
|
|
|
496
504
|
df.to_parquet(output_path, index=False, engine="pyarrow")
|
|
497
505
|
|
|
498
506
|
# Summary
|
|
499
|
-
console.print(
|
|
507
|
+
console.print(
|
|
508
|
+
f"\n[green]✓ Successfully exported {len(df)} rows to {output_path.name}[/green]"
|
|
509
|
+
)
|
|
500
510
|
console.print(f" Full path: {output_path.absolute()}")
|
|
501
511
|
console.print(f" Time range: {df['timestamp'].min()} to {df['timestamp'].max()}")
|
|
502
512
|
console.print(f" Unique points: {df['point_name'].nunique()}")
|
|
@@ -668,8 +678,7 @@ def list_discovered_points(ctx: Context, site_name: str, page: int, per_page: in
|
|
|
668
678
|
# Convert Point objects back to dicts for JSON output
|
|
669
679
|
json_result = result.copy() # Keep original structure
|
|
670
680
|
json_result["items"] = [
|
|
671
|
-
point.model_dump() if isinstance(point, Point) else point
|
|
672
|
-
for point in points
|
|
681
|
+
point.model_dump() if isinstance(point, Point) else point for point in points
|
|
673
682
|
]
|
|
674
683
|
click.echo(format_json(json_result))
|
|
675
684
|
else:
|
|
@@ -895,6 +904,7 @@ def repl_mode(ctx: Context) -> None:
|
|
|
895
904
|
# Add the volttron command group to the CLI
|
|
896
905
|
cli.add_command(volttron)
|
|
897
906
|
|
|
907
|
+
|
|
898
908
|
# Entry point
|
|
899
909
|
def main() -> None:
|
|
900
910
|
"""Main entry point for the CLI."""
|
|
@@ -118,7 +118,9 @@ def init_config(api_key: str | None = None, api_url: str | None = None) -> None:
|
|
|
118
118
|
config_path = get_default_config_path()
|
|
119
119
|
|
|
120
120
|
# Check if config already exists
|
|
121
|
-
if config_path.exists() and not click.confirm(
|
|
121
|
+
if config_path.exists() and not click.confirm(
|
|
122
|
+
f"Config file already exists at {config_path}. Overwrite?"
|
|
123
|
+
):
|
|
122
124
|
return
|
|
123
125
|
|
|
124
126
|
# Get values from user if not provided
|