Skip to content

feat: macOS support - portable paths, solver discovery, and cross-platform runtime fixes#52

Closed
parthdagia05 wants to merge 1 commit intoEAPD-DRB:mainfrom
parthdagia05:feature/new-feature
Closed

feat: macOS support - portable paths, solver discovery, and cross-platform runtime fixes#52
parthdagia05 wants to merge 1 commit intoEAPD-DRB:mainfrom
parthdagia05:feature/new-feature

Conversation

@parthdagia05
Copy link

Summary

This PR integrates full macOS support into MUIOGO by resolving all platform-dependent
assumptions across the backend, frontend, and setup flow. It consolidates the work
planned across 9 individual PRs (#11, #13, #28, #31, #32, #35, #40, #44, #46) into
a single, tested change set.

What changed and why

Path Portability (CWD-Independent Startup)

  • Config.py: All Path('WebAPP') relative paths replaced with PROJECT_ROOT / 'WebAPP'
    computed via Path(__file__).resolve(). The app now starts correctly from any working
    directory, not just the project root.

  • app.py: Replaced os.path.abspath('WebAPP') with Config.WebAPP_PATH.resolve().
    Removed debug print() statements, added structured startup banner showing Python path,
    template/static dirs, mode, and server URL.

  • Config.py: Added os.makedirs(DATA_STORAGE, exist_ok=True) and wrapped os.chmod
    in try/except to handle macOS SIP permission restrictions gracefully.

Solver Discovery (GLPK/CBC)

  • OsemosysClass.py: Removed the if platform.system() == 'Windows' block with
    hardcoded Windows/x86 macOS paths. Replaced with _resolve_solver_folder() — a portable
    three-tier resolution chain:

    1. Environment variable ($GLPK_PATH / $CBC_PATH)
    2. System PATH via shutil.which() (finds Homebrew-installed solvers automatically)
    3. Bundled fallback under WebAPP/SOLVERs/

    Verified on Apple Silicon: GLPK resolves to /opt/homebrew/Cellar/glpk/5.0/bin,
    CBC to /opt/homebrew/Cellar/cbc/2.10.12/bin.

Frontend & Runtime

  • Base.Class.js: Replaced hardcoded http://127.0.0.1:5002/ with
    window.location.origin + "/" for local/dev mode, enabling compatibility with
    Codespaces and preview URLs.
  • Osemosys.Class.js: Added if (!casename) guards in getData() and
    getResultData() to prevent fetch('/DataStorage/null/...') calls after model
    deletion.
  • index.html: Commented out missing References/wijmo/licence.js script include
    to prevent 404 + MIME console errors.

Backend Robustness

  • SyncS3.py + UploadRoute.py: Replaced all hardcoded '\\' path separators
    with os.sep. Added if contents: guard before iterating over list_objects_v2
    response to prevent TypeError: 'NoneType' is not iterable.
  • CustomThreadClass.py: Background thread exceptions are now captured via
    sys.exc_info() in run() and re-raised with original traceback in join().
    Previously exceptions were silently swallowed.

Bug Fix

  • Config.py: Fixed EmissionActivityRatio dimension list — 'e''t' (Python
    string concatenation = 'et') corrected to 'e','t' (two separate elements).

Setup & Documentation

  • setup.sh: New cross-platform setup script that creates a Python venv, installs
    pip dependencies, installs GLPK/CBC via Homebrew (macOS) or apt (Linux), verifies
    solver availability, and creates required directories.

Files changed (10)

File Change
API/Classes/Base/Config.py CWD-independent paths, chmod guard, typo fix
API/app.py CWD-independent template/static, startup banner
API/Classes/Case/OsemosysClass.py 3-tier solver resolution
API/Classes/Base/SyncS3.py os.sep, None guard
API/Routes/Upload/UploadRoute.py os.sep, None guard
API/Classes/Base/CustomThreadClass.py Exception propagation
WebAPP/Classes/Base.Class.js Runtime origin API URL
WebAPP/Classes/Osemosys.Class.js Null casename guards
WebAPP/index.html Comment out missing licence.js
setup.sh New cross-platform setup script

Validation

  • ✅ App starts on macOS (Apple Silicon) with python3 app.py
  • ✅ All paths resolve correctly from any CWD
  • ✅ GLPK found at /opt/homebrew/Cellar/glpk/5.0/bin via shutil.which()
  • ✅ CBC found at /opt/homebrew/Cellar/cbc/2.10.12/bin via shutil.which()
  • ✅ UI loads at http://127.0.0.1:5002/
  • ✅ No Windows-only path assumptions remain

Related issues

Closes #51


…m fixes

- Config.py: anchor all paths to PROJECT_ROOT via __file__ (CWD-independent)
- app.py: use Config.WebAPP_PATH for template/static dirs, clean startup banner
- OsemosysClass.py: 3-tier solver resolution (env var → PATH → bundled fallback)
- SyncS3.py + UploadRoute.py: replace hardcoded backslash with os.sep
- SyncS3.py + UploadRoute.py: guard against None in S3 list_objects_v2
- CustomThreadClass.py: propagate exceptions from background threads
- Base.Class.js: use window.location.origin for API URL resolution
- Osemosys.Class.js: guard null casename in getData/getResultData
- index.html: comment out missing wijmo licence.js
- Config.py: fix EmissionActivityRatio typo ('e''t' → 'e','t')
- setup.sh: cross-platform setup script (venv, deps, GLPK/CBC via Homebrew)

Closes OSeMOSYS#3, OSeMOSYS#8, OSeMOSYS#12, OSeMOSYS#25, OSeMOSYS#34, OSeMOSYS#39, #43, #45
Supersedes PRs: OSeMOSYS#11, OSeMOSYS#13, OSeMOSYS#28, OSeMOSYS#31, OSeMOSYS#32, OSeMOSYS#35, OSeMOSYS#40, #44, #46
@SeaCelo
Copy link
Collaborator

SeaCelo commented Feb 26, 2026

Thanks for the strong cross-platform work here, @parthdagia05. This PR is very aligned with our macOS direction.

For this integration track, we prefer to merge the individual focused PRs directly so review stays manageable. Because of that, could you please rebase this PR onto the latest mac-port after those merges, and then narrow this PR to only the remaining differences?

Key items we found to address when you rebase:

  • API/Routes/Upload/UploadRoute.py: one normalization line uses hardcoded '\\'; please keep os.sep-based normalization.
  • API/Classes/Case/OsemosysClass.py: solver env var names differ from our current convention (SOLVER_GLPK_PATH, SOLVER_CBC_PATH).
  • setup.sh (repo root): duplicates setup flow from PR Add cross-platform setup flow for full MUIOGO environment #11 (scripts/setup.sh / scripts/setup_dev.py); please remove or make it a thin wrapper.
  • WebAPP/Classes/Osemosys.Class.js: null-casename guard behavior differs from the current PR40 variant; please align with merged behavior.

I'll change this PR to draft to make it easier to track

@SeaCelo SeaCelo marked this pull request as draft February 26, 2026 15:18
@SeaCelo
Copy link
Collaborator

SeaCelo commented Feb 27, 2026

Thanks for the thorough work here @parthdagia05, this is a substantial PR.

The Python-side changes have all been incorporated into the mac-port branch. The null casename guards in Osemosys.Class.js and the setup script are also covered.

One item worth noting: your window.location.origin change in Base.Class.js (replacing the hardcoded 127.0.0.1:5002) is a good improvement that isn't in mac-port yet. We'll pick that up separately if you want to to make another PR.

Closing as superseded by mac-port. Appreciate the effort — it covered a lot of ground.

@SeaCelo SeaCelo closed this Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Track] macOS compatibility integration via mac-port branch

2 participants