mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-02-24 01:46:21 +00:00
fix: Adapt versioning scheme to Home Assistant and switch to uv (#896)
Some checks are pending
Bump Version / Bump Version Workflow (push) Waiting to run
docker-build / platform-excludes (push) Waiting to run
docker-build / build (push) Blocked by required conditions
docker-build / merge (push) Blocked by required conditions
pre-commit / pre-commit (push) Waiting to run
Run Pytest on Pull Request / test (push) Waiting to run
Some checks are pending
Bump Version / Bump Version Workflow (push) Waiting to run
docker-build / platform-excludes (push) Waiting to run
docker-build / build (push) Blocked by required conditions
docker-build / merge (push) Blocked by required conditions
pre-commit / pre-commit (push) Waiting to run
Run Pytest on Pull Request / test (push) Waiting to run
Home Assistant expects versioning always increases numbers. Add a date component to the development version to comply with this expectation. The scheme is now 0.0.0.dev<date><hash>. Use uv for creating and managing the virtual environment for developement. This enourmously speeds up dependency updates. For this change dependency requirements are now solely handled in pyproject.toml. requirements.tx and requirements-dev.txt are deleted. Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
2
.env
2
.env
@@ -11,7 +11,7 @@ DOCKER_COMPOSE_DATA_DIR=${HOME}/.local/share/net.akkudoktor.eos
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Image / build
|
# Image / build
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
VERSION=0.2.0.dev58204789
|
VERSION=config.yaml
|
||||||
PYTHON_VERSION=3.13.9
|
PYTHON_VERSION=3.13.9
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
13
.github/workflows/pytest.yml
vendored
13
.github/workflows/pytest.yml
vendored
@@ -18,15 +18,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.13.9"
|
python-version: "3.13.9"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install uv
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install -r requirements-dev.txt
|
pip install uv
|
||||||
|
|
||||||
- name: Run Pytest
|
- name: Sync environment with uv
|
||||||
run: |
|
run: |
|
||||||
pip install -e .
|
uv sync --extra dev
|
||||||
python -m pytest --finalize --check-config-side-effect -vs --cov src --cov-report term-missing
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
uv run pytest --finalize --check-config-side-effect -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
- name: Upload test artifacts
|
- name: Upload test artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ repos:
|
|||||||
|
|
||||||
# --- Static type checking ---
|
# --- Static type checking ---
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v1.18.2
|
rev: v1.19.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- types-requests==2.32.4.20250913
|
- types-requests==2.32.4.20250913
|
||||||
- pandas-stubs==2.3.2.250926
|
- pandas-stubs==3.0.0.260204
|
||||||
- tokenize-rt==6.2.0
|
- tokenize-rt==6.2.0
|
||||||
- types-docutils==0.22.2.20251006
|
- types-docutils==0.22.3.20251115
|
||||||
- types-PyYaml==6.0.12.20250915
|
- types-PyYaml==6.0.12.20250915
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|
||||||
@@ -59,23 +59,29 @@ repos:
|
|||||||
# Validate commit messages (using Python wrapper)
|
# Validate commit messages (using Python wrapper)
|
||||||
- id: commitizen-commit
|
- id: commitizen-commit
|
||||||
name: Commitizen (venv-aware)
|
name: Commitizen (venv-aware)
|
||||||
entry: python3 scripts/cz_check_commit_message.py
|
entry: scripts/cz_check_commit_message.py
|
||||||
language: system
|
language: python
|
||||||
|
additional_dependencies:
|
||||||
|
- .
|
||||||
stages: [commit-msg]
|
stages: [commit-msg]
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|
||||||
# Branch name check on push (using Python wrapper)
|
# Branch name check on push (using Python wrapper)
|
||||||
- id: commitizen-branch
|
- id: commitizen-branch
|
||||||
name: Commitizen branch check
|
name: Commitizen branch check
|
||||||
entry: python3 scripts/cz_check_branch.py
|
entry: scripts/cz_check_branch.py
|
||||||
language: system
|
language: python
|
||||||
|
additional_dependencies:
|
||||||
|
- .
|
||||||
stages: [pre-push]
|
stages: [pre-push]
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|
||||||
# Validate new commit messages before push (using Python wrapper)
|
# Validate new commit messages before push (using Python wrapper)
|
||||||
- id: commitizen-new-commits
|
- id: commitizen-new-commits
|
||||||
name: Commitizen (check new commits only, .venv aware)
|
name: Commitizen (check new commits only, .venv aware)
|
||||||
entry: python3 -m scripts.cz_check_new_commits
|
entry: scripts/cz_check_new_commits.py
|
||||||
language: system
|
language: python
|
||||||
|
additional_dependencies:
|
||||||
|
- .
|
||||||
stages: [pre-push]
|
stages: [pre-push]
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ in Home Assistant.
|
|||||||
The prediction and measurement data can now be backed by a database. The database allows
|
The prediction and measurement data can now be backed by a database. The database allows
|
||||||
to keep historic prediction data and measurement data for long time without keeping
|
to keep historic prediction data and measurement data for long time without keeping
|
||||||
it in memory. The database supports backend selection, compression, incremental data load,
|
it in memory. The database supports backend selection, compression, incremental data load,
|
||||||
automatic data saving to storage, automatic vaccum and compaction. Two database backends
|
automatic data saving to storage, automatic vacuum and compaction. Two database backends
|
||||||
are integrated and can be configured, LMDB and SQLight3.
|
are integrated and can be configured, LMDB and SQLight3.
|
||||||
|
|
||||||
In addition, bugs were fixed and new features were added.
|
In addition, bugs were fixed and new features were added.
|
||||||
@@ -63,7 +63,7 @@ In addition, bugs were fixed and new features were added.
|
|||||||
real (test) environment pathes.
|
real (test) environment pathes.
|
||||||
- development version scheme
|
- development version scheme
|
||||||
The development versioning scheme is adaptet to fit to docker and
|
The development versioning scheme is adaptet to fit to docker and
|
||||||
home assistant expectations. The new scheme is x.y.z and x.y.z.dev<hash>.
|
home assistant expectations. The new scheme is x.y.z and x.y.z.dev'date''hash'.
|
||||||
Hash is only digits as expected by home assistant. Development version
|
Hash is only digits as expected by home assistant. Development version
|
||||||
is appended by .dev as expected by docker.
|
is appended by .dev as expected by docker.
|
||||||
- use mean value in interval on resampling for array
|
- use mean value in interval on resampling for array
|
||||||
@@ -133,6 +133,8 @@ In addition, bugs were fixed and new features were added.
|
|||||||
- add home assistant add-on development environment
|
- add home assistant add-on development environment
|
||||||
Add VSCode devcontainer and task definition for home assistant add-on
|
Add VSCode devcontainer and task definition for home assistant add-on
|
||||||
development.
|
development.
|
||||||
|
- Use uv to manage the virtual environment for development.
|
||||||
|
This enormously increases dependency updates.
|
||||||
- improve documentation
|
- improve documentation
|
||||||
|
|
||||||
## 0.2.0 (2025-11-09)
|
## 0.2.0 (2025-11-09)
|
||||||
|
|||||||
@@ -59,20 +59,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
libopenblas-dev liblapack-dev \
|
libopenblas-dev liblapack-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# - Copy project metadata first (better Docker layer caching)
|
||||||
|
COPY pyproject.toml .
|
||||||
|
|
||||||
# - Create venv
|
# - Create venv
|
||||||
RUN python3 -m venv ${VENV_PATH}
|
RUN python3 -m venv ${VENV_PATH}
|
||||||
|
|
||||||
# - Upgrade pip inside venv
|
# - Upgrade pip inside venv
|
||||||
RUN pip install --upgrade pip setuptools wheel
|
RUN pip install --upgrade pip setuptools wheel
|
||||||
|
|
||||||
# - Install deps
|
|
||||||
COPY requirements.txt .
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
|
|
||||||
# Install EOS/ EOSdash
|
# Install EOS/ EOSdash
|
||||||
# - Copy source
|
# - Copy source
|
||||||
COPY src/ ./src
|
COPY src/ ./src
|
||||||
COPY pyproject.toml .
|
|
||||||
|
|
||||||
# - Create version information
|
# - Create version information
|
||||||
COPY scripts/get_version.py ./scripts/get_version.py
|
COPY scripts/get_version.py ./scripts/get_version.py
|
||||||
|
|||||||
123
Makefile
123
Makefile
@@ -1,8 +1,18 @@
|
|||||||
# Define the targets
|
# Define the targets
|
||||||
.PHONY: help venv pip install dist test test-full test-system test-ci test-profile docker-run docker-build docs read-docs clean format gitlint mypy run run-dev run-dash run-dash-dev prepare-version test-version
|
.PHONY: help install dist test test-system test-ci test-profile \
|
||||||
|
docker-run docker-build docs read-docs clean format gitlint mypy \
|
||||||
|
run run-dev run-dash run-dash-dev prepare-version test-version uv-update
|
||||||
|
|
||||||
|
# Use uv for all program actions
|
||||||
|
UV := uv
|
||||||
|
PYTHON := $(UV) run python
|
||||||
|
PYTEST := $(UV) run pytest
|
||||||
|
MYPY := $(UV) run mypy
|
||||||
|
PRECOMMIT := $(UV) run pre-commit
|
||||||
|
COMMITIZEN := $(UV) run cz
|
||||||
|
|
||||||
# - Take VERSION from version.py
|
# - Take VERSION from version.py
|
||||||
VERSION := $(shell python3 scripts/get_version.py)
|
VERSION := $(shell $(PYTHON) scripts/get_version.py)
|
||||||
|
|
||||||
# Default target
|
# Default target
|
||||||
all: help
|
all: help
|
||||||
@@ -10,13 +20,11 @@ all: help
|
|||||||
# Target to display help information
|
# Target to display help information
|
||||||
help:
|
help:
|
||||||
@echo "Available targets:"
|
@echo "Available targets:"
|
||||||
@echo " venv - Set up a Python 3 virtual environment."
|
|
||||||
@echo " pip - Install dependencies from requirements.txt."
|
|
||||||
@echo " pip-dev - Install dependencies from requirements-dev.txt."
|
|
||||||
@echo " format - Format source code."
|
@echo " format - Format source code."
|
||||||
@echo " gitlint - Lint last commit message."
|
@echo " gitlint - Lint last commit message."
|
||||||
@echo " mypy - Run mypy."
|
@echo " mypy - Run mypy."
|
||||||
@echo " install - Install EOS in editable form (development mode) into virtual environment."
|
@echo " install - Install EOS in editable form (development mode) into virtual environment."
|
||||||
|
@echo " update-env - Update virtual environmenr to match pyproject.toml."
|
||||||
@echo " docker-run - Run entire setup on docker"
|
@echo " docker-run - Run entire setup on docker"
|
||||||
@echo " docker-build - Rebuild docker image"
|
@echo " docker-build - Rebuild docker image"
|
||||||
@echo " docs - Generate HTML documentation (in build/docs/html/)."
|
@echo " docs - Generate HTML documentation (in build/docs/html/)."
|
||||||
@@ -36,57 +44,48 @@ help:
|
|||||||
@echo " clean - Remove generated documentation, distribution and virtual environment."
|
@echo " clean - Remove generated documentation, distribution and virtual environment."
|
||||||
@echo " prepare-version - Prepare a version defined in setup.py."
|
@echo " prepare-version - Prepare a version defined in setup.py."
|
||||||
|
|
||||||
# Target to set up a Python 3 virtual environment
|
|
||||||
venv:
|
|
||||||
python3 -m venv .venv
|
|
||||||
@PYVER=$$(./.venv/bin/python --version) && \
|
|
||||||
echo "Virtual environment created in '.venv' with $$PYVER. Activate it using 'source .venv/bin/activate'."
|
|
||||||
|
|
||||||
# Target to install dependencies from requirements.txt
|
|
||||||
pip: venv
|
|
||||||
.venv/bin/pip install --upgrade pip
|
|
||||||
.venv/bin/pip install -r requirements.txt
|
|
||||||
@echo "Dependencies installed from requirements.txt."
|
|
||||||
|
|
||||||
# Target to install dependencies from requirements.txt
|
|
||||||
pip-dev: pip
|
|
||||||
.venv/bin/pip install -r requirements-dev.txt
|
|
||||||
@echo "Dependencies installed from requirements-dev.txt."
|
|
||||||
|
|
||||||
# Target to create a version.txt
|
# Target to create a version.txt
|
||||||
version-txt:
|
version-txt:
|
||||||
echo "$(VERSION)" > version.txt
|
# Get the version from the package for setuptools (and pip)
|
||||||
|
VERSION=$$(${PYTHON} scripts/get_version.py)
|
||||||
|
@echo "$(VERSION)" > version.txt
|
||||||
|
@echo "version.txt set to '$(VERSION)'."
|
||||||
|
|
||||||
# Target to install EOS in editable form (development mode) into virtual environment.
|
# Target to install EOS in editable form (development mode) into virtual environment.
|
||||||
install: pip-dev version-txt
|
install: version-txt
|
||||||
.venv/bin/pip install build
|
# Upgrade installation and dependencies
|
||||||
.venv/bin/pip install -e .
|
$(UV) sync --extra dev
|
||||||
@echo "EOS installed in editable form (development mode)."
|
@echo "EOS version $(VERSION) installed in editable form (development mode)."
|
||||||
|
|
||||||
|
# Target to rebuild the virtual environment.
|
||||||
|
update-env:
|
||||||
|
@echo "Rebuilding virtual environment to match pyproject.toml..."
|
||||||
|
uv rebuild
|
||||||
|
@echo "Environment rebuilt."
|
||||||
|
|
||||||
# Target to create a distribution.
|
# Target to create a distribution.
|
||||||
dist: pip
|
dist: version-txt
|
||||||
.venv/bin/pip install build
|
$(PIP) install build
|
||||||
.venv/bin/python -m build --wheel
|
$(PYTHON) -m build --wheel
|
||||||
@echo "Distribution created (see dist/)."
|
@echo "Distribution created (see dist/)."
|
||||||
|
|
||||||
# Target to generate documentation
|
# Target to generate documentation
|
||||||
gen-docs: pip-dev version-txt
|
gen-docs: version-txt
|
||||||
.venv/bin/pip install -e .
|
$(PYTHON) ./scripts/generate_config_md.py --output-file docs/_generated/config.md
|
||||||
.venv/bin/python ./scripts/generate_config_md.py --output-file docs/_generated/config.md
|
$(PYTHON) ./scripts/generate_openapi_md.py --output-file docs/_generated/openapi.md
|
||||||
.venv/bin/python ./scripts/generate_openapi_md.py --output-file docs/_generated/openapi.md
|
$(PYTHON) ./scripts/generate_openapi.py --output-file openapi.json
|
||||||
.venv/bin/python ./scripts/generate_openapi.py --output-file openapi.json
|
|
||||||
@echo "Documentation generated to openapi.json and docs/_generated."
|
@echo "Documentation generated to openapi.json and docs/_generated."
|
||||||
|
|
||||||
# Target to build HTML documentation
|
# Target to build HTML documentation
|
||||||
docs: pip-dev
|
docs: install
|
||||||
.venv/bin/pytest --finalize tests/test_docsphinx.py
|
$(PYTEST) --finalize tests/test_docsphinx.py
|
||||||
@echo "Documentation build to build/docs/html/."
|
@echo "Documentation build to build/docs/html/."
|
||||||
|
|
||||||
# Target to read the HTML documentation
|
# Target to read the HTML documentation
|
||||||
read-docs:
|
read-docs:
|
||||||
@echo "Read the documentation in your browser"
|
@echo "Read the documentation in your browser"
|
||||||
.venv/bin/pytest --finalize tests/test_docsphinx.py
|
$(PYTEST) --finalize tests/test_docsphinx.py
|
||||||
.venv/bin/python -m webbrowser build/docs/html/index.html
|
$(PYTHON) -m webbrowser build/docs/html/index.html
|
||||||
|
|
||||||
# Clean Python bytecode
|
# Clean Python bytecode
|
||||||
clean-bytecode:
|
clean-bytecode:
|
||||||
@@ -104,64 +103,66 @@ clean-docs:
|
|||||||
clean: clean-docs
|
clean: clean-docs
|
||||||
@echo "Cleaning virtual env, distribution and build directories"
|
@echo "Cleaning virtual env, distribution and build directories"
|
||||||
rm -rf build .venv
|
rm -rf build .venv
|
||||||
|
@echo "Cleaning uv environment"
|
||||||
|
$(UV) clean
|
||||||
@echo "Deletion complete."
|
@echo "Deletion complete."
|
||||||
|
|
||||||
run:
|
run:
|
||||||
@echo "Starting EOS production server, please wait..."
|
@echo "Starting EOS production server, please wait..."
|
||||||
.venv/bin/python -m akkudoktoreos.server.eos --startup_eosdash true
|
$(PYTHON) -m akkudoktoreos.server.eos --startup_eosdash true
|
||||||
|
|
||||||
run-dev:
|
run-dev:
|
||||||
@echo "Starting EOS development server, please wait..."
|
@echo "Starting EOS development server, please wait..."
|
||||||
.venv/bin/python -m akkudoktoreos.server.eos --host localhost --port 8503 --log_level DEBUG --startup_eosdash false --reload true
|
$(PYTHON) -m akkudoktoreos.server.eos --host localhost --port 8503 --log_level DEBUG --startup_eosdash false --reload true
|
||||||
|
|
||||||
run-dash:
|
run-dash:
|
||||||
@echo "Starting EOSdash production server, please wait..."
|
@echo "Starting EOSdash production server, please wait..."
|
||||||
.venv/bin/python -m akkudoktoreos.server.eosdash
|
$(PYTHON) -m akkudoktoreos.server.eosdash
|
||||||
|
|
||||||
run-dash-dev:
|
run-dash-dev:
|
||||||
@echo "Starting EOSdash development server, please wait..."
|
@echo "Starting EOSdash development server, please wait..."
|
||||||
.venv/bin/python -m akkudoktoreos.server.eosdash --host localhost --port 8504 --log_level DEBUG --reload true
|
$(PYTHON) -m akkudoktoreos.server.eosdash --host localhost --port 8504 --log_level DEBUG --reload true
|
||||||
|
|
||||||
# Target to setup tests.
|
# Target to setup tests.
|
||||||
test-setup: pip-dev
|
test-setup: install
|
||||||
@echo "Setup tests"
|
@echo "Setup tests"
|
||||||
|
|
||||||
# Target to run tests.
|
# Target to run tests.
|
||||||
test:
|
test:
|
||||||
@echo "Running tests..."
|
@echo "Running tests..."
|
||||||
.venv/bin/pytest -vs --cov src --cov-report term-missing
|
$(PYTEST) -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
# Target to run tests as done by CI on Github.
|
# Target to run tests as done by CI on Github.
|
||||||
test-ci:
|
test-ci:
|
||||||
@echo "Running tests as CI..."
|
@echo "Running tests as CI..."
|
||||||
.venv/bin/pytest --finalize --check-config-side-effect -vs --cov src --cov-report term-missing
|
$(PYTEST) --finalize --check-config-side-effect -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
# Target to run tests including the system tests.
|
# Target to run tests including the system tests.
|
||||||
test-system:
|
test-system:
|
||||||
@echo "Running tests incl. system tests..."
|
@echo "Running tests incl. system tests..."
|
||||||
.venv/bin/pytest --system-test -vs --cov src --cov-report term-missing
|
$(PYTEST) --system-test -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
# Target to run all tests.
|
# Target to run all tests.
|
||||||
test-finalize:
|
test-finalize:
|
||||||
@echo "Running all tests..."
|
@echo "Running all tests..."
|
||||||
.venv/bin/pytest --finalize
|
$(PYTEST) --finalize
|
||||||
|
|
||||||
# Target to run tests including the single test optimization with profiling.
|
# Target to run tests including the single test optimization with profiling.
|
||||||
test-profile:
|
test-profile:
|
||||||
@echo "Running single test optimization with profiling..."
|
@echo "Running single test optimization with profiling..."
|
||||||
.venv/bin/python tests/single_test_optimization.py --profile
|
$(PYTHON) tests/single_test_optimization.py --profile
|
||||||
|
|
||||||
# Target to format code.
|
# Target to format code.
|
||||||
format:
|
format:
|
||||||
.venv/bin/pre-commit run --all-files
|
$(PRECOMMIT) run --all-files
|
||||||
|
|
||||||
# Target to trigger gitlint using pre-commit for the latest commit messages
|
# Target to trigger git linting using commitizen for the latest commit messages
|
||||||
gitlint:
|
gitlint:
|
||||||
.venv/bin/cz check --rev-range main..HEAD
|
$(COMMITIZEN) check --rev-range main..HEAD
|
||||||
|
|
||||||
# Target to format code.
|
# Target to format code.
|
||||||
mypy:
|
mypy:
|
||||||
.venv/bin/mypy
|
$(MYPY)
|
||||||
|
|
||||||
# Run entire setup on docker
|
# Run entire setup on docker
|
||||||
docker-run:
|
docker-run:
|
||||||
@@ -180,15 +181,15 @@ docker-build:
|
|||||||
# Take UPDATE_FILES from GitHub action bump-version.yml
|
# Take UPDATE_FILES from GitHub action bump-version.yml
|
||||||
UPDATE_FILES := $(shell sed -n 's/^[[:space:]]*UPDATE_FILES[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' \
|
UPDATE_FILES := $(shell sed -n 's/^[[:space:]]*UPDATE_FILES[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' \
|
||||||
.github/workflows/bump-version.yml)
|
.github/workflows/bump-version.yml)
|
||||||
prepare-version: #pip-dev
|
prepare-version: install
|
||||||
@echo "Update version to $(VERSION) from version.py in files $(UPDATE_FILES) and doc"
|
@echo "Update version to $(VERSION) from version.py in files $(UPDATE_FILES) and doc"
|
||||||
.venv/bin/python ./scripts/update_version.py $(VERSION) $(UPDATE_FILES)
|
$(PYTHON) ./scripts/update_version.py $(VERSION) $(UPDATE_FILES)
|
||||||
.venv/bin/python ./scripts/convert_lightweight_tags.py
|
$(PYTHON) ./scripts/convert_lightweight_tags.py
|
||||||
.venv/bin/python ./scripts/generate_config_md.py --output-file docs/_generated/config.md
|
$(PYTHON) ./scripts/generate_config_md.py --output-file docs/_generated/config.md
|
||||||
.venv/bin/python ./scripts/generate_openapi_md.py --output-file docs/_generated/openapi.md
|
$(PYTHON) ./scripts/generate_openapi_md.py --output-file docs/_generated/openapi.md
|
||||||
.venv/bin/python ./scripts/generate_openapi.py --output-file openapi.json
|
$(PYTHON) ./scripts/generate_openapi.py --output-file openapi.json
|
||||||
.venv/bin/pytest -vv --finalize tests/test_version.py
|
$(PYTEST) -vv --finalize tests/test_version.py
|
||||||
|
|
||||||
test-version:
|
test-version:
|
||||||
echo "Test version information to be correctly set in all version files"
|
echo "Test version information to be correctly set in all version files"
|
||||||
.venv/bin/pytest -vv tests/test_version.py
|
$(PYTEST) -vv tests/test_version.py
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# the root directory (no add-on folder as usual).
|
# the root directory (no add-on folder as usual).
|
||||||
|
|
||||||
name: "Akkudoktor-EOS"
|
name: "Akkudoktor-EOS"
|
||||||
version: "0.2.0.dev58204789"
|
version: "0.2.0.dev2602231150315077"
|
||||||
slug: "eos"
|
slug: "eos"
|
||||||
description: "Akkudoktor-EOS add-on"
|
description: "Akkudoktor-EOS add-on"
|
||||||
url: "https://github.com/Akkudoktor-EOS/EOS"
|
url: "https://github.com/Akkudoktor-EOS/EOS"
|
||||||
|
|||||||
@@ -120,7 +120,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"version": "0.2.0.dev58204789",
|
"version": "0.2.0.dev2602231150315077",
|
||||||
"data_folder_path": "/home/user/.local/share/net.akkudoktoreos.net",
|
"data_folder_path": "/home/user/.local/share/net.akkudoktoreos.net",
|
||||||
"data_output_subpath": "output",
|
"data_output_subpath": "output",
|
||||||
"latitude": 52.52,
|
"latitude": 52.52,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
| latitude | `EOS_GENERAL__LATITUDE` | `Optional[float]` | `rw` | `52.52` | Latitude in decimal degrees between -90 and 90. North is positive (ISO 19115) (°) |
|
| latitude | `EOS_GENERAL__LATITUDE` | `Optional[float]` | `rw` | `52.52` | Latitude in decimal degrees between -90 and 90. North is positive (ISO 19115) (°) |
|
||||||
| longitude | `EOS_GENERAL__LONGITUDE` | `Optional[float]` | `rw` | `13.405` | Longitude in decimal degrees within -180 to 180 (°) |
|
| longitude | `EOS_GENERAL__LONGITUDE` | `Optional[float]` | `rw` | `13.405` | Longitude in decimal degrees within -180 to 180 (°) |
|
||||||
| timezone | | `Optional[str]` | `ro` | `N/A` | Computed timezone based on latitude and longitude. |
|
| timezone | | `Optional[str]` | `ro` | `N/A` | Computed timezone based on latitude and longitude. |
|
||||||
| version | `EOS_GENERAL__VERSION` | `str` | `rw` | `0.2.0.dev58204789` | Configuration file version. Used to check compatibility. |
|
| version | `EOS_GENERAL__VERSION` | `str` | `rw` | `0.2.0.dev2602231150315077` | Configuration file version. Used to check compatibility. |
|
||||||
:::
|
:::
|
||||||
<!-- pyml enable line-length -->
|
<!-- pyml enable line-length -->
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"general": {
|
"general": {
|
||||||
"version": "0.2.0.dev58204789",
|
"version": "0.2.0.dev2602231150315077",
|
||||||
"data_folder_path": "/home/user/.local/share/net.akkudoktoreos.net",
|
"data_folder_path": "/home/user/.local/share/net.akkudoktoreos.net",
|
||||||
"data_output_subpath": "output",
|
"data_output_subpath": "output",
|
||||||
"latitude": 52.52,
|
"latitude": 52.52,
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"general": {
|
"general": {
|
||||||
"version": "0.2.0.dev58204789",
|
"version": "0.2.0.dev2602231150315077",
|
||||||
"data_folder_path": "/home/user/.local/share/net.akkudoktoreos.net",
|
"data_folder_path": "/home/user/.local/share/net.akkudoktoreos.net",
|
||||||
"data_output_subpath": "output",
|
"data_output_subpath": "output",
|
||||||
"latitude": 52.52,
|
"latitude": 52.52,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Akkudoktor-EOS
|
# Akkudoktor-EOS
|
||||||
|
|
||||||
**Version**: `v0.2.0.dev58204789`
|
**Version**: `v0.2.0.dev2602231150315077`
|
||||||
|
|
||||||
<!-- pyml disable line-length -->
|
<!-- pyml disable line-length -->
|
||||||
**Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period.
|
**Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period.
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ Example:
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
.venv\Scripts\python src/akkudoktoreos/server/eos.py --log-level DEBUG
|
uv run python src/akkudoktoreos/server/eos.py --log-level DEBUG
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
.venv/bin/python src/akkudoktoreos/server/eos.py --log-level DEBUG
|
uv run python src/akkudoktoreos/server/eos.py --log-level DEBUG
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -80,21 +80,15 @@ This is recommended for developers who want to modify the source code and test c
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
python -m venv .venv
|
uv run python scripts/get_version.py > version.txt
|
||||||
.venv\Scripts\pip install --upgrade pip
|
uv sync --extra dev
|
||||||
.venv\Scripts\pip install -r requirements-dev.txt
|
|
||||||
.venv\Scripts\pip install build
|
|
||||||
.venv\Scripts\pip install -e .
|
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python3 -m venv .venv
|
uv run python scripts/get_version.py > version.txt
|
||||||
.venv/bin/pip install --upgrade pip
|
uv sync --extra dev
|
||||||
.venv/bin/pip install -r requirements-dev.txt
|
|
||||||
.venv/bin/pip install build
|
|
||||||
.venv/bin/pip install -e .
|
|
||||||
|
|
||||||
.. tab:: Linux Make
|
.. tab:: Linux Make
|
||||||
|
|
||||||
@@ -103,25 +97,7 @@ This is recommended for developers who want to modify the source code and test c
|
|||||||
make install
|
make install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2.2 – Activate the Virtual Environment
|
### Step 2.2 - Install pre-commit
|
||||||
|
|
||||||
```{eval-rst}
|
|
||||||
.. tabs::
|
|
||||||
|
|
||||||
.. tab:: Windows
|
|
||||||
|
|
||||||
.. code-block:: powershell
|
|
||||||
|
|
||||||
.venv\Scripts\activate.bat
|
|
||||||
|
|
||||||
.. tab:: Linux
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
source .venv/bin/activate
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2.3 - Install pre-commit
|
|
||||||
|
|
||||||
Our code style and commit message checks use [`pre-commit`](https://pre-commit.com).
|
Our code style and commit message checks use [`pre-commit`](https://pre-commit.com).
|
||||||
|
|
||||||
@@ -157,13 +133,13 @@ Make EOS accessible at [http://localhost:8503/docs](http://localhost:8503/docs)
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
python -m akkudoktoreos.server.eos
|
uv run python -m akkudoktoreos.server.eos
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python -m akkudoktoreos.server.eos
|
uv run python -m akkudoktoreos.server.eos
|
||||||
|
|
||||||
.. tab:: Linux Make
|
.. tab:: Linux Make
|
||||||
|
|
||||||
@@ -194,13 +170,13 @@ interfere with the EOS server trying to start EOSdash.
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
python -m akkudoktoreos.server.eosdash --host localhost --port 8504 --log_level DEBUG --reload true
|
uv run python -m akkudoktoreos.server.eosdash --host localhost --port 8504 --log_level DEBUG --reload true
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python -m akkudoktoreos.server.eosdash --host localhost --port 8504 --log_level DEBUG --reload true
|
uv run python -m akkudoktoreos.server.eosdash --host localhost --port 8504 --log_level DEBUG --reload true
|
||||||
|
|
||||||
.. tab:: Linux Make
|
.. tab:: Linux Make
|
||||||
|
|
||||||
@@ -216,13 +192,13 @@ interfere with the EOS server trying to start EOSdash.
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
python -m akkudoktoreos.server.eos --host localhost --port 8503 --log_level DEBUG --startup_eosdash false --reload true
|
uv run python -m akkudoktoreos.server.eos --host localhost --port 8503 --log_level DEBUG --startup_eosdash false --reload true
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python -m akkudoktoreos.server.eos --host localhost --port 8503 --log_level DEBUG --startup_eosdash false --reload true
|
uv run python -m akkudoktoreos.server.eos --host localhost --port 8503 --log_level DEBUG --startup_eosdash false --reload true
|
||||||
|
|
||||||
.. tab:: Linux Make
|
.. tab:: Linux Make
|
||||||
|
|
||||||
@@ -381,13 +357,13 @@ At a minimum, you should run the module tests:
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
pytest -vs --cov src --cov-report term-missing
|
uv run pytest -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pytest -vs --cov src --cov-report term-missing
|
uv run pytest -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
.. tab:: Linux Make
|
.. tab:: Linux Make
|
||||||
|
|
||||||
@@ -413,13 +389,13 @@ resources:
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
pytest --system-test -vs --cov src --cov-report term-missing
|
uv run pytest --system-test -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pytest --system-test -vs --cov src --cov-report term-missing
|
uv run pytest --system-test -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
.. tab:: Linux Make
|
.. tab:: Linux Make
|
||||||
|
|
||||||
@@ -437,13 +413,13 @@ To do profiling use:
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
python tests/single_test_optimization.py --profile
|
uv run python tests/single_test_optimization.py --profile
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python tests/single_test_optimization.py --profile
|
uv run python tests/single_test_optimization.py --profile
|
||||||
|
|
||||||
.. tab:: Linux Make
|
.. tab:: Linux Make
|
||||||
|
|
||||||
@@ -575,13 +551,13 @@ Ensure your changes do not break existing functionality:
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
pytest -vs --cov src --cov-report term-missing
|
uv run pytest -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
pytest -vs --cov src --cov-report term-missing
|
uv run pytest -vs --cov src --cov-report term-missing
|
||||||
|
|
||||||
.. tab:: Linux Make
|
.. tab:: Linux Make
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ Before installing, ensure you have the following:
|
|||||||
|
|
||||||
### For Source / Release Installation (M1/M2)
|
### For Source / Release Installation (M1/M2)
|
||||||
|
|
||||||
- Python 3.10 or higher
|
- Python 3.11+
|
||||||
- pip
|
- uv (recommended)
|
||||||
- Git (only for source)
|
- Git (only for source)
|
||||||
- Tar/Zip (for release package)
|
- Tar/Zip (for release package)
|
||||||
|
|
||||||
@@ -52,6 +52,24 @@ Akkudoktor-EOS is a [Home Assistant add-on](https://www.home-assistant.io/addons
|
|||||||
have access to add-ons.
|
have access to add-ons.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Install uv (one-time setup)
|
||||||
|
|
||||||
|
```{eval-rst}
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. tab:: Windows
|
||||||
|
|
||||||
|
.. code-block:: powershell
|
||||||
|
|
||||||
|
irm https://astral.sh/uv/install.ps1 | iex
|
||||||
|
|
||||||
|
.. tab:: Linux
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
|
```
|
||||||
|
|
||||||
## Installation from Source (GitHub) (M1)
|
## Installation from Source (GitHub) (M1)
|
||||||
|
|
||||||
Recommended for developers or users wanting the latest updates.
|
Recommended for developers or users wanting the latest updates.
|
||||||
@@ -85,17 +103,13 @@ Recommended for developers or users wanting the latest updates.
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
python -m venv .venv
|
uv sync --extra dev
|
||||||
.venv\Scripts\pip install -r requirements.txt
|
|
||||||
.venv\Scripts\pip install -e .
|
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python -m venv .venv
|
uv sync --extra dev
|
||||||
.venv/bin/pip install -r requirements.txt
|
|
||||||
.venv/bin/pip install -e .
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -108,13 +122,13 @@ Recommended for developers or users wanting the latest updates.
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
.venv\Scripts\python -m akkudoktoreos.server.eos
|
uv run python -m akkudoktoreos.server.eos
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
.venv/bin/python -m akkudoktoreos.server.eos
|
uv run python -m akkudoktoreos.server.eos
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -134,13 +148,13 @@ stage of the installation provide appropriate IP addresses on startup.
|
|||||||
|
|
||||||
.. code-block:: powershell
|
.. code-block:: powershell
|
||||||
|
|
||||||
.venv\Scripts\python -m akkudoktoreos.server.eos --host 0.0.0.0 --eosdash-host 0.0.0.0
|
uv run python -m akkudoktoreos.server.eos --host 0.0.0.0 --eosdash-host 0.0.0.0
|
||||||
|
|
||||||
.. tab:: Linux
|
.. tab:: Linux
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
.venv/bin/python -m akkudoktoreos.server.eos --host 0.0.0.0 --eosdash-host 0.0.0.0
|
uv run python -m akkudoktoreos.server.eos --host 0.0.0.0 --eosdash-host 0.0.0.0
|
||||||
|
|
||||||
```
|
```
|
||||||
<!-- pyml enable line-length -->
|
<!-- pyml enable line-length -->
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ git checkout v0.1.0
|
|||||||
Then reinstall dependencies:
|
Then reinstall dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
.venv/bin/pip install -r requirements.txt --upgrade
|
uv sync
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Release package (M2)
|
#### Release package (M2)
|
||||||
@@ -62,7 +62,7 @@ Refer to **Method 2** in the [Installation Guideline](install-page).
|
|||||||
### 3) Restart EOS (M1/M2)
|
### 3) Restart EOS (M1/M2)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
.venv/bin/python -m akkudoktoreos.server.eos
|
uv run python -m akkudoktoreos.server.eos
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4) Restore configuration (optional) (M1/M2)
|
### 4) Restore configuration (optional) (M1/M2)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"name": "Apache 2.0",
|
"name": "Apache 2.0",
|
||||||
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
|
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
|
||||||
},
|
},
|
||||||
"version": "v0.2.0.dev58204789"
|
"version": "v0.2.0.dev2602231150315077"
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"/v1/admin/cache/clear": {
|
"/v1/admin/cache/clear": {
|
||||||
@@ -4451,7 +4451,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Version",
|
"title": "Version",
|
||||||
"description": "Configuration file version. Used to check compatibility.",
|
"description": "Configuration file version. Used to check compatibility.",
|
||||||
"default": "0.2.0.dev58204789"
|
"default": "0.2.0.dev2602231150315077"
|
||||||
},
|
},
|
||||||
"data_folder_path": {
|
"data_folder_path": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -4514,7 +4514,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Version",
|
"title": "Version",
|
||||||
"description": "Configuration file version. Used to check compatibility.",
|
"description": "Configuration file version. Used to check compatibility.",
|
||||||
"default": "0.2.0.dev58204789"
|
"default": "0.2.0.dev2602231150315077"
|
||||||
},
|
},
|
||||||
"data_folder_path": {
|
"data_folder_path": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -13,18 +13,87 @@ classifiers = [
|
|||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
]
|
]
|
||||||
|
dependencies = [
|
||||||
|
"babel==2.18.0",
|
||||||
|
"beautifulsoup4==4.14.3",
|
||||||
|
"cachebox==5.2.2",
|
||||||
|
"numpy==2.4.2",
|
||||||
|
"numpydantic==1.7.0",
|
||||||
|
"matplotlib==3.10.8",
|
||||||
|
"contourpy==1.3.3",
|
||||||
|
"fastapi[standard-no-fastapi-cloud-cli]==0.128.0",
|
||||||
|
"fastapi_cli==0.0.21",
|
||||||
|
"rich-toolkit==0.19.4",
|
||||||
|
"python-fasthtml==0.12.47",
|
||||||
|
"MonsterUI==1.0.43",
|
||||||
|
"markdown-it-py==4.0.0",
|
||||||
|
"mdit-py-plugins==0.5.0",
|
||||||
|
"bokeh==3.8.2",
|
||||||
|
"uvicorn==0.40.0",
|
||||||
|
"scipy==1.17.0",
|
||||||
|
"tzfpy==1.1.1",
|
||||||
|
"deap==1.4.3",
|
||||||
|
"requests==2.32.5",
|
||||||
|
"pandas==3.0.0",
|
||||||
|
"pendulum==3.2.0",
|
||||||
|
"platformdirs==4.9.2",
|
||||||
|
"psutil==7.2.2",
|
||||||
|
"pvlib==0.15.0",
|
||||||
|
"pydantic==2.12.5",
|
||||||
|
"pydantic_extra_types==2.11.0",
|
||||||
|
"statsmodels==0.14.6",
|
||||||
|
"pydantic-settings==2.13.1",
|
||||||
|
"linkify-it-py==2.0.3",
|
||||||
|
"loguru==0.7.3",
|
||||||
|
"lmdb==1.7.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
# Pre-commit framework - basic package requirements handled by pre-commit itself
|
||||||
|
# - pre-commit-hooks
|
||||||
|
# - isort
|
||||||
|
# - ruff
|
||||||
|
# - mypy (mirrors-mypy) - sync with requirements-dev.txt (if on pypi)
|
||||||
|
# - pymarkdown
|
||||||
|
# - commitizen - sync with requirements-dev.txt (if on pypi)
|
||||||
|
#
|
||||||
|
# !!! Sync .pre-commit-config.yaml with these dependencies !!!
|
||||||
|
"pre-commit==4.5.1",
|
||||||
|
"mypy==1.19.1",
|
||||||
|
"types-requests==2.32.4.20260107", # for mypy
|
||||||
|
"pandas-stubs==3.0.0.260204", # for mypy
|
||||||
|
"tokenize-rt==6.2.0", # for mypy
|
||||||
|
"types-docutils==0.22.3.20251115", # for mypy
|
||||||
|
"types-PyYaml==6.0.12.20250915", # for mypy
|
||||||
|
"commitizen==4.13.8",
|
||||||
|
"deprecated==1.3.1", # for commitizen
|
||||||
|
|
||||||
|
# Sphinx
|
||||||
|
"sphinx==9.0.4",
|
||||||
|
"sphinx_rtd_theme==3.1.0",
|
||||||
|
"sphinx-tabs==3.4.7",
|
||||||
|
"GitPython==3.1.46",
|
||||||
|
"myst-parser==5.0.0",
|
||||||
|
"docutils==0.21.2",
|
||||||
|
|
||||||
|
# Pytest
|
||||||
|
"pytest==9.0.2",
|
||||||
|
"pytest-asyncio==1.3.0",
|
||||||
|
"pytest-cov==7.0.0",
|
||||||
|
"pytest-xprocess==1.0.2",
|
||||||
|
"coverage==7.13.4",
|
||||||
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://github.com/Akkudoktor-EOS/EOS"
|
Homepage = "https://github.com/Akkudoktor-EOS/EOS"
|
||||||
Issues = "https://github.com/Akkudoktor-EOS/EOS/issues"
|
Issues = "https://github.com/Akkudoktor-EOS/EOS/issues"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=61.0"]
|
requires = ["setuptools>=77", "wheel"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[tool.setuptools.dynamic]
|
[tool.setuptools.dynamic]
|
||||||
dependencies = {file = ["requirements.txt"]}
|
|
||||||
optional-dependencies = {dev = { file = ["requirements-dev.txt"] }}
|
|
||||||
# version.txt must be generated
|
# version.txt must be generated
|
||||||
version = { file = "version.txt" }
|
version = { file = "version.txt" }
|
||||||
|
|
||||||
@@ -92,6 +161,7 @@ markers = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
mypy_path= "src"
|
||||||
files = ["src", "tests"]
|
files = ["src", "tests"]
|
||||||
exclude = "class_soc_calc\\.py$"
|
exclude = "class_soc_calc\\.py$"
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
-r requirements.txt
|
|
||||||
|
|
||||||
# Pre-commit framework - basic package requirements handled by pre-commit itself
|
|
||||||
# - pre-commit-hooks
|
|
||||||
# - isort
|
|
||||||
# - ruff
|
|
||||||
# - mypy (mirrors-mypy) - sync with requirements-dev.txt (if on pypi)
|
|
||||||
# - pymarkdown
|
|
||||||
# - commitizen - sync with requirements-dev.txt (if on pypi)
|
|
||||||
#
|
|
||||||
# !!! Sync .pre-commit-config.yaml and requirements-dev.txt !!!
|
|
||||||
pre-commit==4.5.1
|
|
||||||
mypy==1.19.1
|
|
||||||
types-requests==2.32.4.20260107 # for mypy
|
|
||||||
pandas-stubs==3.0.0.260204 # for mypy
|
|
||||||
tokenize-rt==6.2.0 # for mypy
|
|
||||||
types-docutils==0.22.3.20251115 # for mypy
|
|
||||||
types-PyYaml==6.0.12.20250915 # for mypy
|
|
||||||
commitizen==4.13.8
|
|
||||||
deprecated==1.3.1 # for commitizen
|
|
||||||
|
|
||||||
# Sphinx
|
|
||||||
sphinx==9.0.4
|
|
||||||
sphinx_rtd_theme==3.1.0
|
|
||||||
sphinx-tabs==3.4.7
|
|
||||||
GitPython==3.1.46
|
|
||||||
myst-parser==5.0.0
|
|
||||||
docutils==0.21.2
|
|
||||||
|
|
||||||
# Pytest
|
|
||||||
pytest==9.0.2
|
|
||||||
pytest-asyncio==1.3.0
|
|
||||||
pytest-cov==7.0.0
|
|
||||||
pytest-xprocess==1.0.2
|
|
||||||
coverage==7.13.4
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
babel==2.18.0
|
|
||||||
beautifulsoup4==4.14.3
|
|
||||||
cachebox==5.2.2
|
|
||||||
numpy==2.4.2
|
|
||||||
numpydantic==1.7.0
|
|
||||||
matplotlib==3.10.8
|
|
||||||
contourpy==1.3.3
|
|
||||||
fastapi[standard-no-fastapi-cloud-cli]==0.128.0
|
|
||||||
fastapi_cli==0.0.21
|
|
||||||
rich-toolkit==0.19.4
|
|
||||||
python-fasthtml==0.12.47
|
|
||||||
MonsterUI==1.0.43
|
|
||||||
markdown-it-py==4.0.0
|
|
||||||
mdit-py-plugins==0.5.0
|
|
||||||
bokeh==3.8.2
|
|
||||||
uvicorn==0.40.0
|
|
||||||
scipy==1.17.0
|
|
||||||
tzfpy==1.1.1
|
|
||||||
deap==1.4.3
|
|
||||||
requests==2.32.5
|
|
||||||
pandas==3.0.0
|
|
||||||
pendulum==3.2.0
|
|
||||||
platformdirs==4.9.2
|
|
||||||
psutil==7.2.2
|
|
||||||
pvlib==0.15.0
|
|
||||||
pydantic==2.12.5
|
|
||||||
pydantic_extra_types==2.11.0
|
|
||||||
statsmodels==0.14.6
|
|
||||||
pydantic-settings==2.13.1
|
|
||||||
linkify-it-py==2.0.3
|
|
||||||
loguru==0.7.3
|
|
||||||
lmdb==1.7.5
|
|
||||||
42
scripts/cz_check_branch.py
Normal file → Executable file
42
scripts/cz_check_branch.py
Normal file → Executable file
@@ -11,16 +11,40 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def find_cz() -> str:
|
def find_cz() -> list[str]:
|
||||||
venv = os.getenv("VIRTUAL_ENV")
|
"""Return command to invoke Commitizen via virtualenv or globally."""
|
||||||
paths = [Path(venv)] if venv else []
|
candidates = []
|
||||||
paths.append(Path.cwd() / ".venv")
|
|
||||||
|
|
||||||
for base in paths:
|
# 1️⃣ Currently active virtualenv
|
||||||
cz = base / ("Scripts" if os.name == "nt" else "bin") / ("cz.exe" if os.name == "nt" else "cz")
|
venv = os.getenv("VIRTUAL_ENV")
|
||||||
if cz.exists():
|
if venv:
|
||||||
return str(cz)
|
candidates.append(Path(venv))
|
||||||
return "cz"
|
|
||||||
|
# 2️⃣ uv-managed virtualenv
|
||||||
|
uv_venv = Path(".uv") / "venv"
|
||||||
|
if uv_venv.exists():
|
||||||
|
candidates.append(uv_venv)
|
||||||
|
|
||||||
|
# 3️⃣ traditional .venv
|
||||||
|
dot_venv = Path(".venv")
|
||||||
|
if dot_venv.exists():
|
||||||
|
candidates.append(dot_venv)
|
||||||
|
|
||||||
|
# Check each candidate for Commitizen binary
|
||||||
|
for base in candidates:
|
||||||
|
cz_path = base / ("Scripts" if os.name == "nt" else "bin") / ("cz.exe" if os.name == "nt" else "cz")
|
||||||
|
if cz_path.exists():
|
||||||
|
return [str(cz_path)]
|
||||||
|
|
||||||
|
# 4️⃣ fallback to uv run cz
|
||||||
|
try:
|
||||||
|
subprocess.run(["uv", "run", "cz", "--version"], check=True, stdout=subprocess.DEVNULL)
|
||||||
|
return ["uv", "run", "cz"]
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 5️⃣ fallback to system cz
|
||||||
|
return ["cz"]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
53
scripts/cz_check_commit_message.py
Normal file → Executable file
53
scripts/cz_check_commit_message.py
Normal file → Executable file
@@ -2,6 +2,12 @@
|
|||||||
"""Commitizen commit message checker that is .venv aware.
|
"""Commitizen commit message checker that is .venv aware.
|
||||||
|
|
||||||
Works for commits with -m or commit message file.
|
Works for commits with -m or commit message file.
|
||||||
|
|
||||||
|
Cross-platform + uv/.venv aware:
|
||||||
|
- Prefers activated virtual environment (VIRTUAL_ENV)
|
||||||
|
- Falls back to uv-managed .uv/venv
|
||||||
|
- Falls back to .venv
|
||||||
|
- Falls back to global cz
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -10,19 +16,40 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def find_cz() -> str:
|
def find_cz() -> list[str]:
|
||||||
"""Find Commitizen executable, preferring virtualenv."""
|
"""Return command to invoke Commitizen via virtualenv or globally."""
|
||||||
venv = os.getenv("VIRTUAL_ENV")
|
candidates = []
|
||||||
paths = []
|
|
||||||
if venv:
|
|
||||||
paths.append(Path(venv))
|
|
||||||
paths.append(Path.cwd() / ".venv")
|
|
||||||
|
|
||||||
for base in paths:
|
# 1️⃣ Currently active virtualenv
|
||||||
cz = base / ("Scripts" if os.name == "nt" else "bin") / ("cz.exe" if os.name == "nt" else "cz")
|
venv = os.getenv("VIRTUAL_ENV")
|
||||||
if cz.exists():
|
if venv:
|
||||||
return str(cz)
|
candidates.append(Path(venv))
|
||||||
return "cz"
|
|
||||||
|
# 2️⃣ uv-managed virtualenv
|
||||||
|
uv_venv = Path(".uv") / "venv"
|
||||||
|
if uv_venv.exists():
|
||||||
|
candidates.append(uv_venv)
|
||||||
|
|
||||||
|
# 3️⃣ traditional .venv
|
||||||
|
dot_venv = Path(".venv")
|
||||||
|
if dot_venv.exists():
|
||||||
|
candidates.append(dot_venv)
|
||||||
|
|
||||||
|
# Check each candidate for Commitizen binary
|
||||||
|
for base in candidates:
|
||||||
|
cz_path = base / ("Scripts" if os.name == "nt" else "bin") / ("cz.exe" if os.name == "nt" else "cz")
|
||||||
|
if cz_path.exists():
|
||||||
|
return [str(cz_path)]
|
||||||
|
|
||||||
|
# 4️⃣ fallback to uv run cz
|
||||||
|
try:
|
||||||
|
subprocess.run(["uv", "run", "cz", "--version"], check=True, stdout=subprocess.DEVNULL)
|
||||||
|
return ["uv", "run", "cz"]
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 5️⃣ fallback to system cz
|
||||||
|
return ["cz"]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -47,7 +74,7 @@ def main():
|
|||||||
print(f"🔍 Checking commit message using {cz}...")
|
print(f"🔍 Checking commit message using {cz}...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_call([cz, "check", "--commit-msg-file", commit_msg_file])
|
subprocess.check_call(cz + ["check", "--commit-msg-file", commit_msg_file])
|
||||||
print("✅ Commit message follows Commitizen convention.")
|
print("✅ Commit message follows Commitizen convention.")
|
||||||
return 0
|
return 0
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
|
|||||||
58
scripts/cz_check_new_commits.py
Normal file → Executable file
58
scripts/cz_check_new_commits.py
Normal file → Executable file
@@ -1,10 +1,11 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Pre-push hook: Commitizen check for *new commits only*.
|
"""Pre-push hook: Commitizen check for *new commits only*.
|
||||||
|
|
||||||
Cross-platform + virtualenv-aware:
|
Cross-platform + uv/.venv aware:
|
||||||
- Prefers activated virtual environment (VIRTUAL_ENV)
|
- Prefers activated virtual environment (VIRTUAL_ENV)
|
||||||
- Falls back to ./.venv if found
|
- Falls back to uv-managed .uv/venv
|
||||||
- Falls back to global cz otherwise
|
- Falls back to .venv
|
||||||
|
- Falls back to global cz
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -13,23 +14,40 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def find_cz_executable() -> str:
|
def find_cz() -> list[str]:
|
||||||
"""Return path to Commitizen executable, preferring virtual environments."""
|
"""Return command to invoke Commitizen via virtualenv or globally."""
|
||||||
# 1️⃣ Active virtual environment (if running inside one)
|
candidates = []
|
||||||
venv_env = os.getenv("VIRTUAL_ENV")
|
|
||||||
if venv_env:
|
# 1️⃣ Currently active virtualenv
|
||||||
cz_path = Path(venv_env) / ("Scripts" if os.name == "nt" else "bin") / ("cz.exe" if os.name == "nt" else "cz")
|
venv = os.getenv("VIRTUAL_ENV")
|
||||||
|
if venv:
|
||||||
|
candidates.append(Path(venv))
|
||||||
|
|
||||||
|
# 2️⃣ uv-managed virtualenv
|
||||||
|
uv_venv = Path(".uv") / "venv"
|
||||||
|
if uv_venv.exists():
|
||||||
|
candidates.append(uv_venv)
|
||||||
|
|
||||||
|
# 3️⃣ traditional .venv
|
||||||
|
dot_venv = Path(".venv")
|
||||||
|
if dot_venv.exists():
|
||||||
|
candidates.append(dot_venv)
|
||||||
|
|
||||||
|
# Check each candidate for Commitizen binary
|
||||||
|
for base in candidates:
|
||||||
|
cz_path = base / ("Scripts" if os.name == "nt" else "bin") / ("cz.exe" if os.name == "nt" else "cz")
|
||||||
if cz_path.exists():
|
if cz_path.exists():
|
||||||
return str(cz_path)
|
return [str(cz_path)]
|
||||||
|
|
||||||
# 2️⃣ Local .venv in repo root
|
# 4️⃣ fallback to uv run cz
|
||||||
repo_venv = Path.cwd() / ".venv"
|
try:
|
||||||
cz_path = repo_venv / ("Scripts" if os.name == "nt" else "bin") / ("cz.exe" if os.name == "nt" else "cz")
|
subprocess.run(["uv", "run", "cz", "--version"], check=True, stdout=subprocess.DEVNULL)
|
||||||
if cz_path.exists():
|
return ["uv", "run", "cz"]
|
||||||
return str(cz_path)
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
|
||||||
# 3️⃣ Global fallback
|
# 5️⃣ fallback to system cz
|
||||||
return "cz"
|
return ["cz"]
|
||||||
|
|
||||||
|
|
||||||
def get_merge_base() -> str | None:
|
def get_merge_base() -> str | None:
|
||||||
@@ -48,17 +66,17 @@ def get_merge_base() -> str | None:
|
|||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
cz = find_cz_executable()
|
cz = find_cz()
|
||||||
base = get_merge_base()
|
base = get_merge_base()
|
||||||
|
|
||||||
if not base:
|
if not base:
|
||||||
print("⚠️ No upstream found; skipping Commitizen check for new commits.")
|
print("⚠️ No upstream found; skipping Commitizen check {cz} for new commits.")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
print(f"🔍 Using {cz} to check new commits from {base}..HEAD ...")
|
print(f"🔍 Using {cz} to check new commits from {base}..HEAD ...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_call([cz, "check", "--rev-range", f"{base}..HEAD"])
|
subprocess.check_call(cz + ["check", "--rev-range", f"{base}..HEAD"])
|
||||||
print("✅ All new commits follow Commitizen conventions.")
|
print("✅ All new commits follow Commitizen conventions.")
|
||||||
return 0
|
return 0
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ from loguru import logger
|
|||||||
from pydantic.fields import ComputedFieldInfo, FieldInfo
|
from pydantic.fields import ComputedFieldInfo, FieldInfo
|
||||||
from pydantic_core import PydanticUndefined
|
from pydantic_core import PydanticUndefined
|
||||||
|
|
||||||
|
# Add the src directory to sys.path so import akkudoktoreos works in all cases
|
||||||
|
PROJECT_ROOT = Path(__file__).parent.parent
|
||||||
|
SRC_DIR = PROJECT_ROOT / "src"
|
||||||
|
sys.path.insert(0, str(SRC_DIR))
|
||||||
|
|
||||||
from akkudoktoreos.config.config import ConfigEOS, default_data_folder_path
|
from akkudoktoreos.config.config import ConfigEOS, default_data_folder_path
|
||||||
from akkudoktoreos.core.coreabc import get_config, singletons_init
|
from akkudoktoreos.core.coreabc import get_config, singletons_init
|
||||||
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ import argparse
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Add the src directory to sys.path so import akkudoktoreos works in all cases
|
||||||
|
PROJECT_ROOT = Path(__file__).parent.parent
|
||||||
|
SRC_DIR = PROJECT_ROOT / "src"
|
||||||
|
sys.path.insert(0, str(SRC_DIR))
|
||||||
|
|
||||||
from akkudoktoreos.core.coreabc import get_config
|
from akkudoktoreos.core.coreabc import get_config
|
||||||
from akkudoktoreos.server.eos import app
|
from akkudoktoreos.server.eos import app
|
||||||
|
|||||||
@@ -1,15 +1,29 @@
|
|||||||
#!.venv/bin/python
|
|
||||||
"""Get version of EOS"""
|
"""Get version of EOS"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info < (3, 11):
|
||||||
|
print(
|
||||||
|
f"ERROR: Python >=3.11 is required. Found {sys.version_info.major}.{sys.version_info.minor}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Add the src directory to sys.path so Sphinx can import akkudoktoreos
|
# Add the src directory to sys.path so import akkudoktoreos works in all cases
|
||||||
PROJECT_ROOT = Path(__file__).parent.parent
|
PROJECT_ROOT = Path(__file__).parent.parent
|
||||||
SRC_DIR = PROJECT_ROOT / "src"
|
SRC_DIR = PROJECT_ROOT / "src"
|
||||||
sys.path.insert(0, str(SRC_DIR))
|
sys.path.insert(0, str(SRC_DIR))
|
||||||
|
|
||||||
from akkudoktoreos.core.version import __version__
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print(__version__)
|
# Import here to prevent mypy to execute the functions that evaluate __version__
|
||||||
|
try:
|
||||||
|
from akkudoktoreos.core.version import __version__
|
||||||
|
version = __version__
|
||||||
|
except Exception:
|
||||||
|
# This may be a first time install
|
||||||
|
raise RuntimeError("Can not find out version!")
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
print(version)
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
# Add the src directory to sys.path so import akkudoktoreos works in all cases
|
||||||
|
PROJECT_ROOT = Path(__file__).parent.parent
|
||||||
|
SRC_DIR = PROJECT_ROOT / "src"
|
||||||
|
sys.path.insert(0, str(SRC_DIR))
|
||||||
|
|
||||||
|
|
||||||
# --- Patterns to match version strings ---
|
# --- Patterns to match version strings ---
|
||||||
VERSION_PATTERNS = [
|
VERSION_PATTERNS = [
|
||||||
# Python: __version__ = "1.2.3"
|
# Python: __version__ = "1.2.3"
|
||||||
@@ -88,6 +94,21 @@ def update_version_in_file(file_path: Path, new_version: str) -> bool:
|
|||||||
return file_would_be_updated
|
return file_would_be_updated
|
||||||
|
|
||||||
|
|
||||||
|
def update_version_date_file() -> str:
|
||||||
|
"""Write current version date to __version_date__.py"""
|
||||||
|
from akkudoktoreos.core.version import VERSION_DATE_FILE, _version_date_hash
|
||||||
|
|
||||||
|
version_date, _ = _version_date_hash()
|
||||||
|
version_date_str = version_date.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
content = f'VERSION_DATE = "{version_date_str}"\n'
|
||||||
|
|
||||||
|
VERSION_DATE_FILE.write_text(content)
|
||||||
|
|
||||||
|
print(f"Updated {VERSION_DATE_FILE} with UTC date {version_date_str}")
|
||||||
|
|
||||||
|
return str(VERSION_DATE_FILE)
|
||||||
|
|
||||||
|
|
||||||
def main(version: str, files: List[str]):
|
def main(version: str, files: List[str]):
|
||||||
if not version:
|
if not version:
|
||||||
raise ValueError("No version provided")
|
raise ValueError("No version provided")
|
||||||
@@ -103,6 +124,8 @@ def main(version: str, files: List[str]):
|
|||||||
if update_version_in_file(path, version):
|
if update_version_in_file(path, version):
|
||||||
updated_files.append(str(path))
|
updated_files.append(str(path))
|
||||||
|
|
||||||
|
updated_files.append(update_version_date_file())
|
||||||
|
|
||||||
if updated_files:
|
if updated_files:
|
||||||
print(f"Updated files: {', '.join(updated_files)}")
|
print(f"Updated files: {', '.join(updated_files)}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
1
src/akkudoktoreos/core/_version_date.py
Normal file
1
src/akkudoktoreos/core/_version_date.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VERSION_DATE = "2026-02-23T11:41:01Z"
|
||||||
@@ -1,8 +1,21 @@
|
|||||||
"""Version information for akkudoktoreos."""
|
"""Version information for akkudoktoreos."""
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# version.py may be used __BEFORE__ the dependencies are installed.
|
||||||
|
# Use only standard python libraries
|
||||||
|
#
|
||||||
|
# Several warnings/ erros are silenced because they are
|
||||||
|
# non-critical in this context - see noqa.
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from datetime import ( # Don't use akkudoktoreos.utils.datetimeutil (-> pendulum)
|
||||||
|
datetime,
|
||||||
|
timezone,
|
||||||
|
)
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -14,14 +27,18 @@ VERSION_BASE = "0.2.0.dev"
|
|||||||
# Project hash of relevant files
|
# Project hash of relevant files
|
||||||
HASH_EOS = ""
|
HASH_EOS = ""
|
||||||
|
|
||||||
# Number of digits to append to .dev to identify a development version
|
# Number of hash digits to append to .dev to identify a development version
|
||||||
VERSION_DEV_PRECISION = 8
|
VERSION_DEV_HASH_PRECISION = 8
|
||||||
|
|
||||||
|
# File to hold the date of the latest commit.
|
||||||
|
VERSION_DATE_FILE = Path(__file__).parent / "_version_date.py"
|
||||||
|
|
||||||
# Hashing configuration
|
# Hashing configuration
|
||||||
DIR_PACKAGE_ROOT = Path(__file__).resolve().parent.parent
|
DIR_PACKAGE_ROOT = Path(__file__).resolve().parent.parent
|
||||||
ALLOWED_SUFFIXES: set[str] = {".py", ".md", ".json"}
|
ALLOWED_SUFFIXES: set[str] = {".py", ".md", ".json"}
|
||||||
EXCLUDED_DIR_PATTERNS: set[str] = {"*_autosum", "*__pycache__", "*_generated"}
|
EXCLUDED_DIR_PATTERNS: set[str] = {"*_autosum", "*__pycache__", "*_generated"}
|
||||||
EXCLUDED_FILES: set[Path] = set()
|
# Excluded from hash/date calculation to avoid self-referencing loop
|
||||||
|
EXCLUDED_FILES: set[Path] = {VERSION_DATE_FILE}
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
@@ -169,18 +186,78 @@ def hash_tree(
|
|||||||
return digest, files
|
return digest, files
|
||||||
|
|
||||||
|
|
||||||
|
def newest_commit_or_dirty_datetime(files: list[Path]) -> datetime:
|
||||||
|
"""Return the newest relevant datetime for the given files in UTC.
|
||||||
|
|
||||||
|
Checks for uncommitted changes among the given files first. If any file
|
||||||
|
has staged or unstaged modifications, the current UTC datetime is returned
|
||||||
|
to reflect that the working tree is ahead of the last commit. Otherwise,
|
||||||
|
the datetime of the most recent git commit touching any of the given files
|
||||||
|
is returned. If git is unavailable (e.g. after pip install), falls back to
|
||||||
|
reading the date from VERSION_DATE_FILE.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
files: List of file paths to check for changes and commit history.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The current UTC datetime if any file has uncommitted changes, otherwise
|
||||||
|
the UTC datetime of the most recent commit touching any of the given
|
||||||
|
files, or the datetime stored in VERSION_DATE_FILE as a last resort.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError: If no version date can be determined from any source.
|
||||||
|
"""
|
||||||
|
# Check for uncommitted changes among watched files
|
||||||
|
try:
|
||||||
|
status_result = subprocess.run( # noqa: S603
|
||||||
|
["git", "status", "--porcelain", "--"] + [str(f) for f in files],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
cwd=DIR_PACKAGE_ROOT,
|
||||||
|
)
|
||||||
|
if status_result.stdout.strip():
|
||||||
|
return datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
|
result = subprocess.run( # noqa: S603
|
||||||
|
["git", "log", "-1", "--format=%ct", "--"] + [str(f) for f in files],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
cwd=DIR_PACKAGE_ROOT,
|
||||||
|
)
|
||||||
|
ts = result.stdout.strip()
|
||||||
|
if ts:
|
||||||
|
return datetime.fromtimestamp(int(ts), tz=timezone.utc)
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError, ValueError): # noqa: S110
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallback to VERSION_DATE_FILE
|
||||||
|
if VERSION_DATE_FILE.exists():
|
||||||
|
try:
|
||||||
|
ns: dict[str, str] = {}
|
||||||
|
exec(VERSION_DATE_FILE.read_text(), {}, ns) # noqa: S102
|
||||||
|
date_str = ns.get("VERSION_DATE")
|
||||||
|
if date_str:
|
||||||
|
return datetime.fromisoformat(date_str).astimezone(timezone.utc)
|
||||||
|
except Exception: # noqa: S110
|
||||||
|
pass
|
||||||
|
|
||||||
|
raise RuntimeError("This should not happen - No version date info available")
|
||||||
|
|
||||||
|
|
||||||
# ---------------------
|
# ---------------------
|
||||||
# Version hash function
|
# Version hash function
|
||||||
# ---------------------
|
# ---------------------
|
||||||
|
|
||||||
|
|
||||||
def _version_hash() -> str:
|
def _version_date_hash() -> tuple[datetime, str]:
|
||||||
"""Calculate project hash.
|
"""Calculate project date and hash.
|
||||||
|
|
||||||
Only package files in src/akkudoktoreos can be hashed to make it work also for packages.
|
Only package files in src/akkudoktoreos can be hashed to make it work also for packages.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
SHA256 hash of the project files
|
lattest commit date and SHA256 hash of the project files
|
||||||
"""
|
"""
|
||||||
if not str(DIR_PACKAGE_ROOT).endswith("src/akkudoktoreos"):
|
if not str(DIR_PACKAGE_ROOT).endswith("src/akkudoktoreos"):
|
||||||
error_msg = f"DIR_PACKAGE_ROOT does not end with src/akkudoktoreos: {DIR_PACKAGE_ROOT}"
|
error_msg = f"DIR_PACKAGE_ROOT does not end with src/akkudoktoreos: {DIR_PACKAGE_ROOT}"
|
||||||
@@ -197,23 +274,29 @@ def _version_hash() -> str:
|
|||||||
excluded_files=EXCLUDED_FILES,
|
excluded_files=EXCLUDED_FILES,
|
||||||
)
|
)
|
||||||
|
|
||||||
return hash_digest
|
date = newest_commit_or_dirty_datetime(hashed_files)
|
||||||
|
|
||||||
|
return date, hash_digest
|
||||||
|
|
||||||
|
|
||||||
def _version_calculate() -> str:
|
def _version_calculate() -> str:
|
||||||
"""Calculate the full version string.
|
"""Calculate the full version string.
|
||||||
|
|
||||||
For release versions: "x.y.z"
|
For release versions: "x.y.z"
|
||||||
For dev versions: "x.y.z.dev<hash>"
|
For dev versions: "x.y.z.dev<date><hash>"
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Full version string
|
Full version string
|
||||||
"""
|
"""
|
||||||
if VERSION_BASE.endswith(".dev"):
|
if VERSION_BASE.endswith(".dev"):
|
||||||
# After dev only digits are allowed - convert hexdigest to digits
|
# After dev only digits are allowed - convert hexdigest to digits
|
||||||
hash_value = int(_version_hash(), 16)
|
version_date, version_hash = _version_date_hash()
|
||||||
hash_digits = str(hash_value % (10**VERSION_DEV_PRECISION)).zfill(VERSION_DEV_PRECISION)
|
hash_value = int(version_hash, 16)
|
||||||
return f"{VERSION_BASE}{hash_digits}"
|
hash_digits = str(hash_value % (10**VERSION_DEV_HASH_PRECISION)).zfill(
|
||||||
|
VERSION_DEV_HASH_PRECISION
|
||||||
|
)
|
||||||
|
date_digits = version_date.strftime("%y%m%d%H") if version_date else "00000000"
|
||||||
|
return f"{VERSION_BASE}{date_digits}{hash_digits}"
|
||||||
else:
|
else:
|
||||||
# Release version - use base as-is
|
# Release version - use base as-is
|
||||||
return VERSION_BASE
|
return VERSION_BASE
|
||||||
@@ -234,10 +317,10 @@ __version__ = _version_calculate()
|
|||||||
# Regular expression to split the version string into pieces
|
# Regular expression to split the version string into pieces
|
||||||
VERSION_RE = re.compile(
|
VERSION_RE = re.compile(
|
||||||
r"""
|
r"""
|
||||||
^(?P<base>\d+\.\d+\.\d+) # x.y.z
|
^(?P<base>\d+\.\d+\.\d+) # x.y.z
|
||||||
(?:\. # .dev<hash> starts here
|
(?:\.dev # literal '.dev' for development versions
|
||||||
(?P<dev>dev) # literal 'dev'
|
(?P<date>\d{8}) # 8-digit date: YYMMDDHH
|
||||||
(?P<hash>[a-f0-9]+)? # optional <hash> (hex digits)
|
(?P<hash>[a-f0-9]+)? # hex hash
|
||||||
)?
|
)?
|
||||||
$
|
$
|
||||||
""",
|
""",
|
||||||
@@ -251,7 +334,7 @@ def version() -> dict[str, Optional[str]]:
|
|||||||
The version string shall be of the form:
|
The version string shall be of the form:
|
||||||
x.y.z
|
x.y.z
|
||||||
x.y.z.dev
|
x.y.z.dev
|
||||||
x.y.z.dev<HASH>
|
x.y.z.dev<date><hash>
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from xprocess import ProcessStarter, XProcess
|
|||||||
|
|
||||||
from akkudoktoreos.config.config import ConfigEOS
|
from akkudoktoreos.config.config import ConfigEOS
|
||||||
from akkudoktoreos.core.coreabc import get_config, get_prediction, singletons_init
|
from akkudoktoreos.core.coreabc import get_config, get_prediction, singletons_init
|
||||||
from akkudoktoreos.core.version import _version_hash, version
|
from akkudoktoreos.core.version import _version_date_hash, version
|
||||||
from akkudoktoreos.server.server import get_default_host
|
from akkudoktoreos.server.server import get_default_host
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
@@ -510,26 +510,41 @@ def server_base(
|
|||||||
if extra_env:
|
if extra_env:
|
||||||
env.update(extra_env)
|
env.update(extra_env)
|
||||||
|
|
||||||
# assure server to be installed
|
project_dir = Path(__file__).parent.parent
|
||||||
try:
|
|
||||||
project_dir = Path(__file__).parent.parent
|
@staticmethod
|
||||||
subprocess.run(
|
def _ensure_package(env: dict, project_dir: Path) -> None:
|
||||||
[sys.executable, "-c", "import", "akkudoktoreos.server.eos"],
|
"""Ensure 'akkudoktoreos' is importable in this Python environment."""
|
||||||
check=True,
|
try:
|
||||||
env=env,
|
subprocess.run(
|
||||||
stdout=subprocess.PIPE,
|
[sys.executable, "-c", "import akkudoktoreos.server.eos"],
|
||||||
stderr=subprocess.PIPE,
|
check=True,
|
||||||
cwd=project_dir,
|
env=env,
|
||||||
)
|
stdout=subprocess.PIPE,
|
||||||
except subprocess.CalledProcessError:
|
stderr=subprocess.PIPE,
|
||||||
subprocess.run(
|
cwd=project_dir,
|
||||||
[sys.executable, "-m", "pip", "install", "-e", str(project_dir)],
|
)
|
||||||
env=env,
|
except subprocess.CalledProcessError:
|
||||||
check=True,
|
# If inside a normal venv or uv-managed environment, install in place
|
||||||
stdout=subprocess.PIPE,
|
uv_root = os.getenv("UV_VENV_ROOT") # set by uv if active
|
||||||
stderr=subprocess.PIPE,
|
venv_active = hasattr(sys, "real_prefix") or sys.prefix != sys.base_prefix
|
||||||
cwd=project_dir,
|
if uv_root or venv_active:
|
||||||
)
|
print("Package not found, installing in current environment...")
|
||||||
|
subprocess.run(
|
||||||
|
[sys.executable, "-m", "pip", "install", "-e", str(project_dir)],
|
||||||
|
check=True,
|
||||||
|
env=env,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
cwd=project_dir,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Cannot import 'akkudoktoreos.server.eos' in the system Python. "
|
||||||
|
"Activate a virtual environment first."
|
||||||
|
)
|
||||||
|
|
||||||
|
_ensure_package(env, project_dir)
|
||||||
|
|
||||||
# Set command to start server process
|
# Set command to start server process
|
||||||
args = [
|
args = [
|
||||||
@@ -644,7 +659,7 @@ def version_and_hash() -> Generator[dict[str, Optional[str]], None, None]:
|
|||||||
Runs once per test session.
|
Runs once per test session.
|
||||||
"""
|
"""
|
||||||
info = version()
|
info = version()
|
||||||
info["hash_current"] = _version_hash()
|
_, info["hash_current"] = _version_date_hash()
|
||||||
|
|
||||||
yield info
|
yield info
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ from akkudoktoreos.core.version import (
|
|||||||
EXCLUDED_FILES,
|
EXCLUDED_FILES,
|
||||||
HashConfig,
|
HashConfig,
|
||||||
_version_calculate,
|
_version_calculate,
|
||||||
_version_hash,
|
_version_date_hash,
|
||||||
collect_files,
|
collect_files,
|
||||||
hash_files,
|
hash_files,
|
||||||
)
|
)
|
||||||
|
from akkudoktoreos.utils.datetimeutil import to_datetime
|
||||||
|
|
||||||
DIR_PROJECT_ROOT = Path(__file__).parent.parent
|
DIR_PROJECT_ROOT = Path(__file__).parent.parent
|
||||||
GET_VERSION_SCRIPT = DIR_PROJECT_ROOT / "scripts" / "get_version.py"
|
GET_VERSION_SCRIPT = DIR_PROJECT_ROOT / "scripts" / "get_version.py"
|
||||||
@@ -127,7 +128,7 @@ def write_file(path: Path, content: str):
|
|||||||
|
|
||||||
# -- Test version calculation ---
|
# -- Test version calculation ---
|
||||||
|
|
||||||
def test_version_hash() -> None:
|
def test_version_date_hash() -> None:
|
||||||
"""Test which files are used for version hash calculation."""
|
"""Test which files are used for version hash calculation."""
|
||||||
|
|
||||||
watched_paths = [DIR_PACKAGE_ROOT]
|
watched_paths = [DIR_PACKAGE_ROOT]
|
||||||
@@ -196,22 +197,26 @@ def test_version_non_dev(monkeypatch):
|
|||||||
|
|
||||||
def test_version_dev_precision_8(monkeypatch):
|
def test_version_dev_precision_8(monkeypatch):
|
||||||
"""Test that a dev version appends exactly 8 digits derived from the hash."""
|
"""Test that a dev version appends exactly 8 digits derived from the hash."""
|
||||||
fake_hash = "abcdef1234567890" # deterministic fake digest
|
fake_hash = "abcdef1234567890"
|
||||||
|
fake_date = "2025-02-22T09:28:22Z"
|
||||||
|
fake_date_hash = (to_datetime(fake_date), fake_hash) # deterministic fake digest
|
||||||
|
|
||||||
monkeypatch.setattr("akkudoktoreos.core.version._version_hash", lambda: fake_hash)
|
monkeypatch.setattr("akkudoktoreos.core.version._version_date_hash", lambda: fake_date_hash)
|
||||||
monkeypatch.setattr("akkudoktoreos.core.version.VERSION_BASE", "0.2.0.dev")
|
monkeypatch.setattr("akkudoktoreos.core.version.VERSION_BASE", "0.2.0.dev")
|
||||||
monkeypatch.setattr("akkudoktoreos.core.version.VERSION_DEV_PRECISION", 8)
|
monkeypatch.setattr("akkudoktoreos.core.version.VERSION_DEV_HASH_PRECISION", 8)
|
||||||
|
|
||||||
result = _version_calculate()
|
result = _version_calculate()
|
||||||
|
|
||||||
# Compute expected suffix using the same logic as _version_calculate
|
# Compute expected suffix using the same logic as _version_calculate
|
||||||
hash_value = int(fake_hash, 16)
|
hash_value = int(fake_hash, 16)
|
||||||
expected_digits = str(hash_value % (10 ** 8)).zfill(8)
|
expected_hash_digits = str(hash_value % (10 ** 8)).zfill(8)
|
||||||
|
|
||||||
expected = f"0.2.0.dev{expected_digits}"
|
expected_date_digits = to_datetime(fake_date, as_string="YYMMDDHH")
|
||||||
|
|
||||||
|
expected = f"0.2.0.dev{expected_date_digits}{expected_hash_digits}"
|
||||||
|
|
||||||
assert result == expected
|
assert result == expected
|
||||||
assert len(expected_digits) == 8
|
assert len(expected_hash_digits) == 8
|
||||||
assert result.startswith("0.2.0.dev")
|
assert result.startswith("0.2.0.dev")
|
||||||
assert result == expected
|
assert result == expected
|
||||||
|
|
||||||
@@ -219,21 +224,25 @@ def test_version_dev_precision_8(monkeypatch):
|
|||||||
def test_version_dev_precision_8_different_hash(monkeypatch):
|
def test_version_dev_precision_8_different_hash(monkeypatch):
|
||||||
"""A different hash must produce a different 8-digit suffix."""
|
"""A different hash must produce a different 8-digit suffix."""
|
||||||
fake_hash = "1234abcd9999ffff"
|
fake_hash = "1234abcd9999ffff"
|
||||||
|
fake_date = "2025-02-22T09:28:22Z"
|
||||||
|
fake_date_hash = (to_datetime(fake_date), fake_hash) # deterministic fake digest
|
||||||
|
|
||||||
monkeypatch.setattr("akkudoktoreos.core.version._version_hash", lambda: fake_hash)
|
monkeypatch.setattr("akkudoktoreos.core.version._version_date_hash", lambda: fake_date_hash)
|
||||||
monkeypatch.setattr("akkudoktoreos.core.version.VERSION_BASE", "0.2.0.dev")
|
monkeypatch.setattr("akkudoktoreos.core.version.VERSION_BASE", "0.2.0.dev")
|
||||||
monkeypatch.setattr("akkudoktoreos.core.version.VERSION_DEV_PRECISION", 8)
|
monkeypatch.setattr("akkudoktoreos.core.version.VERSION_DEV_HASH_PRECISION", 8)
|
||||||
|
|
||||||
result = _version_calculate()
|
result = _version_calculate()
|
||||||
|
|
||||||
# Compute expected suffix using the same logic as _version_calculate
|
# Compute expected suffix using the same logic as _version_calculate
|
||||||
hash_value = int(fake_hash, 16)
|
hash_value = int(fake_hash, 16)
|
||||||
expected_digits = str(hash_value % (10 ** 8)).zfill(8)
|
expected_hash_digits = str(hash_value % (10 ** 8)).zfill(8)
|
||||||
|
|
||||||
expected = f"0.2.0.dev{expected_digits}"
|
expected_date_digits = to_datetime(fake_date, as_string="YYMMDDHH")
|
||||||
|
|
||||||
|
expected = f"0.2.0.dev{expected_date_digits}{expected_hash_digits}"
|
||||||
|
|
||||||
assert result == expected
|
assert result == expected
|
||||||
assert len(expected_digits) == 8
|
assert len(expected_hash_digits) == 8
|
||||||
assert result.startswith("0.2.0.dev")
|
assert result.startswith("0.2.0.dev")
|
||||||
assert result == expected
|
assert result == expected
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user