Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e08999d
chore: add .gitignore (#1)
Antek-N Dec 10, 2025
729e4ab
chore: add Poetry for dependency management (#2)
Antek-N Dec 10, 2025
846ee25
chore: add requirements and requirements-dev file (#3)
Antek-N Dec 10, 2025
a9ceed5
chore: add-license (#4)
Antek-N Dec 10, 2025
c07cf49
feat: add logging_config file (#5)
Antek-N Dec 10, 2025
d2023e3
chore: add pyinstaller .spec file (#6)
Antek-N Dec 10, 2025
578d97f
chore: add pre-commit configuration (#7)
Antek-N Dec 10, 2025
0a80d28
feat: add application configuration (#8)
Antek-N Dec 10, 2025
8e105f1
ci: add CI (#9)
Antek-N Dec 10, 2025
8bcaefe
assets: add static data files and images (#10)
Antek-N Dec 10, 2025
97918db
feat: implement spatial railway graph analysis and route calculation …
Antek-N Dec 10, 2025
50a2946
feat: implement utility calculators for price and distance (#12)
Antek-N Dec 10, 2025
d31bf45
feat: implement GTFS data loader and processing pipeline (#13)
Antek-N Dec 10, 2025
dfe7fbf
feat: implement station graph data structures (#14)
Antek-N Dec 10, 2025
60c7a9e
feat: implement Dijkstra pathfinding algorithm and time expansion (#15)
Antek-N Dec 10, 2025
b0316da
feat: add interactive map plotting and canvas logic (#16)
Antek-N Dec 10, 2025
67e32fc
feat: add input panels, ticket popups, and station info logic (#17)
Antek-N Dec 10, 2025
dee3b0f
feat: implement main GUI creator (#18)
Antek-N Dec 10, 2025
2e7e55e
feat: implement application entry point and main loop (#19)
Antek-N Dec 10, 2025
69069ff
docs: add automatic documentation with mkdocs (#20)
Antek-N Dec 10, 2025
8ec20fc
ci: add CD (#21)
Antek-N Dec 10, 2025
7c26a9c
docs: add README (#22)
Antek-N Dec 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Build EXE on PR

# Run only on pull requests targeting develop or main
on:
pull_request:
branches: [develop, main]

# Concurrency: one run per commit in a PR
concurrency:
group: pr-exe-${{ github.event.pull_request.head.sha }}
cancel-in-progress: true

# Least-privilege permissions
permissions:
contents: read

# Global environment variables
env:
PYTHON_VERSION: "3.12"
APP_NAME: "RailNetworkGraph"
SPEC_PATH: "RailNetworkGraph.spec"

# Job: build Windows EXE with PyInstaller
jobs:
build-windows-exe:
name: Build Windows EXE (PyInstaller)
runs-on: windows-latest
timeout-minutes: 30
env:
PIP_DISABLE_PIP_VERSION_CHECK: "1"
PYTHONDONTWRITEBYTECODE: "1"

steps:
# Get repository code
- name: Checkout
uses: actions/checkout@v4

# Install chosen Python version + cache pip
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
cache-dependency-path: |
pyproject.toml
poetry.lock

# Install Poetry itself (via pip)
- name: Install Poetry
run: python -m pip install --upgrade pip poetry

# Keep virtualenv inside repo for easier caching
- name: Enable in-project venv for Poetry
run: poetry config virtualenvs.in-project true

# Cache the .venv folder based on lockfile + Python version
- name: Cache Poetry venv
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-

# Install all dependencies (main + dev) from pyproject.toml
- name: Install deps (poetry)
run: poetry install --no-interaction --with dev

# Verify .spec file exists, otherwise fail with error
- name: Verify .spec exists
run: |
if (-not (Test-Path "${{ env.SPEC_PATH }}")) {
Write-Error "Missing PyInstaller spec file '${{ env.SPEC_PATH }}'"
}

# Build EXE using PyInstaller with the .spec file
# Then list the contents of dist/ for debugging/logging
- name: Build exe with PyInstaller (.spec only)
run: |
poetry run pyinstaller --noconfirm --clean "${{ env.SPEC_PATH }}"
if (Test-Path dist) { Get-ChildItem -Recurse dist | Format-Table -AutoSize }

# Upload the dist/ folder as artifact for download
- name: Upload artifact (dist)
uses: actions/upload-artifact@v4
with:
name: ${{ env.APP_NAME }}-dist
path: dist/**
if-no-files-found: error
retention-days: 7
88 changes: 88 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: CI

# Triggers: on any push (all branches), ignore tags, also on PRs
on:
push:
branches: ["**"]
tags-ignore: ["*"]
pull_request:

# Concurrency: one run per branch/PR, cancel old if new starts
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

# Least-privilege permissions for this workflow
permissions:
contents: read

# Global environment
env:
PYTHON_VERSION: "3.12"

# Job: linting, type checks, and tests
jobs:
checks:
name: Lint, types & tests
runs-on: windows-latest
timeout-minutes: 15
env:
PIP_DISABLE_PIP_VERSION_CHECK: "1"
PYTHONDONTWRITEBYTECODE: "1"

steps:
# Get repository code
- name: Checkout
uses: actions/checkout@v4

# Install chosen Python version + enable pip cache
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
cache-dependency-path: |
pyproject.toml
poetry.lock

# Install Poetry itself (via pip)
- name: Install Poetry
run: python -m pip install --upgrade pip poetry

# Keep virtualenv inside repo for easier caching
- name: Enable in-project venv for Poetry
run: poetry config virtualenvs.in-project true

# Cache the .venv folder based on lockfile + Python version
- name: Cache Poetry venv
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-

# Install all dependencies (main + dev) from pyproject.toml
- name: Install deps (poetry)
run: poetry install --no-interaction --with dev

# Run linter (Ruff) with GitHub-friendly output
- name: Ruff (lint)
run: poetry run ruff check --output-format=github .

# Run formatter (Black) in check-only mode, with diff if fails
- name: Black (format check)
run: poetry run black --check --diff .

# Cache mypy cache folder to speed up type checking
- name: Cache mypy
uses: actions/cache@v4
with:
path: .mypy_cache
key: mypy-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
mypy-${{ runner.os }}-${{ env.PYTHON_VERSION }}-

# Run static type checker (Mypy) with auto-install types
- name: Mypy (type check)
run: poetry run mypy --install-types --non-interactive --pretty .
151 changes: 151 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: Build & Publish EXE (Release or Tag)

# Run workflow when:
# - a release is published in GitHub UI
# - any tag is pushed (tags: ["*"])
on:
release:
types: [published]

# Permissions: allow writing release assets to the repository
permissions:
contents: write

# Concurrency: group runs per tag/release, don't cancel older runs
concurrency:
group: rel-${{ github.event_name == 'release' && github.event.release.tag_name || github.ref_name }}
cancel-in-progress: false

# Global environment variables
env:
PYTHON_VERSION: "3.12"
APP_NAME: "RailNetworkGraph"
SPEC_PATH: "RailNetworkGraph.spec"
TAG_NAME: ${{ github.event_name == 'release' && github.event.release.tag_name || github.ref_name }}

jobs:
build-and-publish:
name: Build & Publish Windows EXE
runs-on: windows-latest
timeout-minutes: 30
env:
PIP_DISABLE_PIP_VERSION_CHECK: "1"
PYTHONDONTWRITEBYTECODE: "1"

steps:
# Get repository code
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # fetch full history (needed for branch ancestry check)
ref: ${{ env.TAG_NAME }}

# Verify that the tag commit is part of the main branch history (PowerShell)
- name: Verify tag points to commit on main
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'release'
run: |
git fetch origin +refs/heads/main:refs/remotes/origin/main
$TagSha = (git rev-parse HEAD).Trim()
git merge-base --is-ancestor $TagSha origin/main
if ($LASTEXITCODE -ne 0) {
Write-Error "Tag '${{ env.TAG_NAME }}' does not point to a commit on the 'main' branch. Publishing blocked."
}


# Install a chosen Python version + enable pip cache (helps to install Poetry)
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
cache-dependency-path: |
pyproject.toml
poetry.lock

# Install Poetry itself (via pip)
- name: Install Poetry
run: python -m pip install --upgrade pip poetry

# Keep virtualenv inside repo for easier caching
- name: Enable in-project venv for Poetry
run: poetry config virtualenvs.in-project true

# Cache the .venv folder based on lockfile + Python version
- name: Cache Poetry venv
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-

# Install all dependencies (main + dev) from pyproject.toml
- name: Install deps (poetry)
run: poetry install --no-interaction --with dev

# Verify .spec file exists, otherwise fail with error
- name: Verify .spec exists
run: |
if (-not (Test-Path "${{ env.SPEC_PATH }}")) {
Write-Error "Missing PyInstaller spec file '${{ env.SPEC_PATH }}'"
}

# Build EXE using PyInstaller with the .spec file
# Then list the contents of dist/ for debugging/logging
- name: Build exe with PyInstaller (.spec only)
run: |
poetry run pyinstaller --noconfirm --clean "${{ env.SPEC_PATH }}"
if (Test-Path dist) { Get-ChildItem -Recurse dist | Format-Table -AutoSize }

# Sanity check: ensure at least one .exe exists before publishing
- name: Ensure at least one EXE exists
run: |
$files = Get-ChildItem -Path dist -Filter *.exe -Recurse -ErrorAction SilentlyContinue
if (-not $files) { Write-Error "No .exe files found in dist/**" }

# Package all built EXE files into a single ZIP archive
- name: Package EXE(s) into ZIP
run: |
$zipName = "dist/${{ env.APP_NAME }}-${{ env.TAG_NAME }}.zip"
if (Test-Path $zipName) { Remove-Item $zipName -Force }
$exeFiles = Get-ChildItem -Path dist -Filter *.exe -Recurse | Select-Object -ExpandProperty FullName
if (-not $exeFiles) { Write-Error "No .exe files to zip"; exit 1 }
Compress-Archive -Path $exeFiles -DestinationPath $zipName -Force
Get-ChildItem -Path dist -Filter *.zip -Recurse | ForEach-Object { Write-Host $_.FullName }

# Generate a SHA256 checksum file for the created ZIP
- name: Create sha256
run: |
$zipPath = "dist/${{ env.APP_NAME }}-${{ env.TAG_NAME }}.zip"
$hash = Get-FileHash $zipPath -Algorithm SHA256 | Select-Object -ExpandProperty Hash
$hash | Out-File -FilePath "dist/${{ env.APP_NAME }}-${{ env.TAG_NAME }}.sha256" -Encoding ascii
Get-ChildItem -Path dist -Filter *.sha256 -Recurse | ForEach-Object { Write-Host $_.FullName }

# Publish release metadata on GitHub (create or update release without assets)
- name: Create/Update GitHub Release (no files)
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.TAG_NAME }}
name: ${{ env.TAG_NAME }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Upload the generated ZIP file as a release asset
- name: Upload ZIP asset
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ env.TAG_NAME }}
file: dist/${{ env.APP_NAME }}-${{ env.TAG_NAME }}.zip
overwrite: true
file_glob: false

# Upload the SHA256 checksum file as a release asset
- name: Upload SHA256 asset
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ env.TAG_NAME }}
file: dist/${{ env.APP_NAME }}-${{ env.TAG_NAME }}.sha256
overwrite: true
file_glob: false
57 changes: 57 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Ignore PyCharm project files
.idea/
*.iml

# Ignore virtual environment directories
venv/
env/
ENV/
.venv/
.ENV/
.venv.bak/
venv.bak/

# Ignore Python bytecode files
__pycache__/
*.py[cod]
*$py.class

# Ignore temporary and log files
*.log
*.tmp
*.swp
*.swo
cache

# Ignore system files (macOS / Windows / Linux)
.DS_Store
Thumbs.db
ehthumbs.db
Icon?
desktop.ini

# Ignore test-related files
.coverage
.tox/
.nox/
.cache/
pytest_cache/

# Ignore pip logs
pip-log.txt
pip-delete-this-directory.txt

# Ignore Jupyter Notebook checkpoints
.ipynb_checkpoints/

# Ignore editor-specific configuration files
.vscode/

# Ignore build artifacts
build/
dist/
*.egg-info/
.eggs/

# mypy
.mypy_cache/
Loading