From c6c259c072448393b519f97c890fba744dc49206 Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Wed, 26 Mar 2025 18:02:06 +0100
Subject: [PATCH] Add boilerplate for Python project

---
 .gitignore              |  16 +++++-
 .pre-commit-config.yaml |  12 +++++
 Changelog.md            |   4 ++
 Dockerfile              |  30 +++++++++++
 LICENSE                 |  21 ++++++++
 pyproject.toml          | 117 ++++++++++++++++++++++++++++++++++++++++
 requirements.txt        |   4 ++
 setup.py                |  30 +++++++++++
 tox.ini                 |  23 ++++++++
 9 files changed, 256 insertions(+), 1 deletion(-)
 create mode 100644 .pre-commit-config.yaml
 create mode 100644 Changelog.md
 create mode 100644 Dockerfile
 create mode 100644 LICENSE
 create mode 100644 pyproject.toml
 create mode 100644 setup.py
 create mode 100644 tox.ini

diff --git a/.gitignore b/.gitignore
index f7275bb..3752bf7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,15 @@
-venv/
+__pycache__/
+*.egg-info
+.coverage*
+coverage.xml
+.vscode
+venv
+oss-params.json
+.mypy_cache
+.pytest_cache
+.ruff_cache
+.tox
+build/
+.idea
+.venv
+.env
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..a825c2d
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,12 @@
+repos:
+  - repo: https://github.com/astral-sh/ruff-pre-commit
+    # Ruff version.
+    rev: v0.1.15
+    hooks:
+      # Run the linter.
+      - id: ruff
+        args:
+          - --fix
+          - --preview
+          - --ignore=PLR0917,PLR0914
+          - --extend-exclude=test/*
diff --git a/Changelog.md b/Changelog.md
new file mode 100644
index 0000000..ee20d1f
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,4 @@
+# Changelog
+
+## [0.1] - 2025-03-26
+- Initial version
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c17bbc3
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,30 @@
+FROM python:3.12.7-alpine
+WORKDIR /app
+
+# Set environment variables for predictable Python behavior and UTF-8 encoding
+ENV PYTHONUNBUFFERED=1 \
+    PYTHONDONTWRITEBYTECODE=1 \
+    LANG=C.UTF-8 \
+    LC_ALL=C.UTF-8
+
+ARG ARTIFACT_VERSION
+
+RUN apk add --no-cache gcc libc-dev libffi-dev && \
+    addgroup -S appgroup && adduser -S appuser -G appgroup -h /app
+
+RUN pip install --no-cache-dir \
+    --pre \
+    --trusted-host 150.254.211.2 \
+    --extra-index-url https://150.254.211.2/artifactory/api/pypi/geant-swd-pypi/simple \
+    --target /app \
+    geant-capacity-planner==${ARTIFACT_VERSION}
+
+# Copy the shell scripts and ensure scripts do not have Windows line endings and make them executable
+COPY start-app.sh start-worker.sh start-scheduler.sh /app/
+RUN sed -i 's/\r$//' start-app.sh start-worker.sh start-scheduler.sh && \
+    chmod +x start-app.sh start-worker.sh start-scheduler.sh
+
+RUN chown -R appuser:appgroup /app
+USER appuser
+EXPOSE 8080
+ENTRYPOINT ["/app/start-app.sh"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b47daa7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 GÉANT Vereniging
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..542d08a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,117 @@
+[tool.mypy]
+exclude = ["venv"]
+ignore_missing_imports = true
+disallow_untyped_calls = true
+disallow_untyped_defs = true
+disallow_incomplete_defs = true
+disallow_untyped_decorators = true
+no_implicit_optional = true
+strict_optional = true
+namespace_packages = true
+warn_unused_ignores = true
+warn_redundant_casts = true
+warn_no_return = true
+warn_unreachable = true
+implicit_reexport = false
+strict_equality = true
+show_error_codes = true
+show_column_numbers = true
+# Suppress "note: By default the bodies of untyped functions are not checked"
+disable_error_code = "annotation-unchecked"
+# Forbid the use of a generic "type: ignore" without specifying the exact error that is ignored
+enable_error_code = "ignore-without-code"
+
+[tool.ruff]
+extend-exclude = [
+    "htmlcov",
+    "docs",
+]
+target-version = "py312"
+line-length = 120
+
+[tool.ruff.lint]
+ignore = [
+    "C901",
+    "COM812",
+    "D203",
+    "D213",
+    "ISC001",
+    "N805",
+    "PLC2801",
+    "PLR0913",
+    "PLR0904",
+    "PLW1514",
+]
+select = [
+    "A",
+    "ARG",
+    "B",
+    "BLE",
+    "C",
+    "COM",
+    "C4",
+    "C90",
+    "D",
+    "DTZ",
+    "E",
+    "EM",
+    "ERA",
+    "F",
+    "FA",
+    "FBT",
+    "FLY",
+    "FURB",
+    "G",
+    "I",
+    "ICN",
+    "INP",
+    "ISC",
+    "LOG",
+    "N",
+    "PERF",
+    "PGH",
+    "PIE",
+    "PL",
+    "PT",
+    "PTH",
+    "PYI",
+    "Q",
+    "RET",
+    "R",
+    "RET",
+    "RSE",
+    "RUF",
+    "S",
+    "SIM",
+    "SLF",
+    "T",
+    "T20",
+    "TID",
+    "TRY",
+    "UP",
+    "W",
+    "YTT"
+]
+
+[tool.ruff.lint.pydocstyle]
+convention = "google"
+
+[tool.ruff.lint.flake8-tidy-imports]
+ban-relative-imports = "all"
+
+[tool.ruff.lint.per-file-ignores]
+"setup.py" = ["D100"]
+
+[tool.ruff.lint.isort]
+known-third-party = ["pydantic"]
+known-first-party = ["test", "docs"]
+
+[tool.pytest.ini_options]
+markers = [
+    "noautofixt"
+]
+filterwarnings = [
+    "ignore",
+    "default:::capacity-planner",
+]
+asyncio_default_fixture_loop_scope = "function"
diff --git a/requirements.txt b/requirements.txt
index 1d4f386..d3be924 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,3 +12,7 @@ six==1.17.0
 tabulate==0.9.0
 tzdata==2025.1
 urllib3==2.3.0
+
+#  Development requirements
+ruff==0.11.2
+mypy==1.15.0
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..4933ffc
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,30 @@
+"""Setup script for the GÉANT Capacity Planner."""
+
+from setuptools import find_packages, setup
+
+setup(
+    name="geant-capacity-planner",
+    version="0.1",
+    author="GÉANT Vereniging",
+    author_email="goat@geant.org",
+    description="GÉANT Capacity Planner",
+    url="https://gitlab.software.geant.org/karel.vanklink/daniel",
+    packages=find_packages(),
+    install_requires=[
+        "certifi==2024.12.14",
+        "charset-normalizer == 3.4.1",
+        "idna==3.10",
+        "networkx==3.4.2",
+        "numpy==2.2.2",
+        "pandas==2.2.3",
+        "python-dateutil==2.9.0.post0",
+        "pytz==2025.1",
+        "requests==2.32.3",
+        "setuptools==75.8.0",
+        "six==1.17.0",
+        "tabulate==0.9.0",
+        "tzdata==2025.1",
+        "urllib3==2.3.0",
+    ],
+    include_package_data=True,
+)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..85de47d
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,23 @@
+[tox]
+envlist = py312
+
+[pytest]
+markers = "workflow,noautofixt"
+filterwarnings = "ignore,default:::capacity-planner"
+
+[testenv]
+passenv = SKIP_ALL_TESTS
+setenv =
+    TESTING=true
+deps =
+    pytest-cov
+    -r requirements.txt
+
+commands =
+    ruff check --respect-gitignore --preview .
+    ruff format --respect-gitignore --preview --check .
+    mypy .
+    sh -c 'if [ $SKIP_ALL_TESTS = 1 ]; then echo "Skipping coverage report"; else pytest --cov=gso --cov-report=xml --cov-report=html --cov-fail-under=90 -n auto {posargs}; fi'
+
+allowlist_externals =
+    sh
-- 
GitLab