diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a1011af --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,88 @@ +name: Test + +on: + push: + branches: # on all branches except `typos` + - '**' + - '!typos' + paths-ignore: + - 'docs/**' + - 'examples/**' + - 'data/**' + - '.git*' + - 'README.md' + pull_request: + branches: + - '**' + schedule: # Every Monday at 04:00 UTC + - cron: '0 4 * * 1' + +jobs: + caching: + strategy: + matrix: + python-version: ["3.12"] + os: [ ubuntu-latest ] + fail-fast: false + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + + defaults: + run: + shell: bash -elo pipefail {0} + + name: Cache for ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v6 + + - name: Get week number + run: echo "WEEK=$(date +'%V')" >> $GITHUB_ENV + + - name: Set up Python ${{ matrix.python-version }} + uses: mamba-org/setup-micromamba@v2 + with: + create-args: python=${{ matrix.python-version }} + environment-file: environment-dev.yml + cache-environment: true + cache-environment-key: W${{ env.WEEK }} + + run-tests: + needs: caching + strategy: + matrix: + python-version: ["3.12"] + os: [ ubuntu-latest ] + submodule: + - { + name: "Functions", + pytest_args: "tests/test_functions.py" } + fail-fast: false + + name: ${{ matrix.submodule.name }} (${{ matrix.python-version }} on ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + defaults: + run: + shell: bash -el {0} + + steps: + - uses: actions/checkout@v6 + + - name: Get week number + run: echo "WEEK=$(date +'%V')" >> $GITHUB_ENV + + - name: Set up Python ${{ matrix.python-version }} + uses: mamba-org/setup-micromamba@v2 + with: + create-args: python=${{ matrix.python-version }} + environment-file: environment-dev.yml + cache-environment: true + cache-environment-key: W${{ env.WEEK }} + + - name: Install linkbikenet + run: pip install --no-build-isolation --no-deps -e . + + - name: "Run tests ${{ matrix.submodule.name }}" + run: | + pytest ${{ matrix.submodule.pytest_args }} diff --git a/environment-dev.yml b/environment-dev.yml new file mode 100644 index 0000000..abbf3dd --- /dev/null +++ b/environment-dev.yml @@ -0,0 +1,32 @@ +name: lbnenvdev +channels: + - conda-forge +dependencies: + - python=3.12 + - jupyter + - jupyterlab + - ipywidgets + - ipykernel + - matplotlib + - geopandas>=0.14 + - osmnx>=1.9.4 + - pip + - pytest + - pixi-pycharm + - pre-commit + - sphinx + - numpydoc + - myst-nb + - sphinx-book-theme + - nbsphinx + - myst-parser + - ipython + - pip: + # not available via conda-forge: + - opencv-python + - pre-commit + - sphinxcontrib-bibtex + - sphinx-copybutton + - sphinx-gallery + - python-slugify + - furo \ No newline at end of file diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..7d66d2a --- /dev/null +++ b/environment.yml @@ -0,0 +1,8 @@ +name: lbnenv +channels: + - conda-forge +dependencies: + - python>=3.12 + - osmnx>=2.10 + - networkx>=3.6.1 + - numpy>=2.4.6 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..20fe579 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,59 @@ +[build-system] +requires = ["setuptools >= 77.0.3"] +build-backend = "setuptools.build_meta" + +[project] +name = "linkbikenet" +version = "0.5.0" +authors = [ + { name = "Manuel Knepper" }, + { name = "Michael Szell" }, +] +maintainers = [{ name = "Manuel Knepper", email = "manuel.knepper@gmx.net" }] +license = "AGPL-3.0-or-later" +readme = "README.md" +description = "BikeNetKit Python package to link disconnected bicycle network components" +keywords = ["Bicycle network planning", "Networks", "OpenStreetMap", "Urban Planning", "Urban Mobility"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Natural Language :: English", +] +requires-python = ">=3.10" +dependencies = [ + "geopandas>=0.14", + "matplotlib", + "osmnx>=1.9.4", + "geojson", + "pip", + "numpy", +] + +[project.urls] +Repository = "https://github.com/BikeNetKit/LinkBikeNet" +Issues = "https://github.com/BikeNetKit/LinkBikeNet/issues" + +[project.optional-dependencies] +test = ["pytest"] +doc = ["sphinx", "momepy"] + +[tool.ruff.lint.extend-per-file-ignores] +"__init__.py" = ["F401"] # "Imported but unused: happens with packages + +[tool.setuptools.packages.find] +exclude = [ + "examples*", + "tests*", + "results", + "docs*", + "data*", + "scripts*", +] +namespaces = false \ No newline at end of file diff --git a/tests/test_functions.py b/tests/test_functions.py new file mode 100644 index 0000000..db5fb08 --- /dev/null +++ b/tests/test_functions.py @@ -0,0 +1,38 @@ +import networkx as nx +import pytest +from linkbikenet.functions import * + +@pytest.fixture +def create_test_components_largest(): + G = nx.Graph() + G.add_nodes_from([(1, {"x": 1, "y": 2}), (2, {"x": 5, "y": 6}), (3, {"x": 7, "y": 8}), (4, {"x": 10, "y": 10}), (5, {"x": 11, "y": 11}), (6, {"x": 15, "y": 20})]) + G.add_edges_from([(1, 2, {'length': 5}), (2, 3, {'length': 2}), (1, 3, {'length': 7}), (4, 5, {'length': 1})]) + wcc = [G.subgraph(c).copy() for c in sorted(nx.connected_components(G), key=lambda c: sum( + [l[-1] for l in G.subgraph(c).copy().edges.data('length')]), reverse=True)] + return wcc + +@pytest.fixture +def create_validation_pair_largest(): + pair = 3,4 + return pair + +def test_pair_between_largest_components(create_test_components_largest, create_validation_pair_largest): + assert pair_between_largest_components(create_test_components_largest) == create_validation_pair_largest + + +@pytest.fixture +def create_test_components_nearest(): + G = nx.Graph() + G.add_nodes_from([(1, {"x": 1, "y": 2}), (2, {"x": 5, "y": 6}), (3, {"x": 7, "y": 8}), (4, {"x": 30, "y": 30}), (5, {"x": 31, "y": 31}), (6, {"x": 10, "y": 10})]) + G.add_edges_from([(1, 2, {'length': 5}), (2, 3, {'length': 2}), (1, 3, {'length': 7}), (4, 5, {'length': 1})]) + wcc = [G.subgraph(c).copy() for c in sorted(nx.connected_components(G), key=lambda c: sum( + [l[-1] for l in G.subgraph(c).copy().edges.data('length')]), reverse=True)] + return wcc + +@pytest.fixture +def create_validation_pair_nearest(): + pair = 3,6 + return pair + +def test_pair_between_nearest_components(create_test_components_nearest, create_validation_pair_nearest): + assert pair_between_nearest_components(create_test_components_nearest) == create_validation_pair_nearest