Desktop application for quality-aware production planning at the ELCC plant. It loads a plant workbook, solves the MILP production plan with Pyomo + HiGHS, and presents the schedule, service levels and cost breakdown.
Bilingual: English and Persian (فارسی), with full right-to-left layout.
Stack: PySide6 (Qt 6) · Pyomo · HiGHS · pandas · matplotlib
pip install -r requirements.txtpython run_gui.pyThe app opens on the Data page. Point it at a plant workbook (Ctrl+O) and press F5 to get a plan.
No plant data ships with this repository. The ELCC production figures, prices and customer records the model was developed against are not redistributed, so
model/holds the Pyomo model but no dataset. Until you select a workbook, the Data page reports the bundled dataset as unreadable and a run stops withMissing input table(s): .... Seemodel/README.mdfor the schema an input workbook needs.
The window follows the workflow: Data → Run → Results (sidebar, or Ctrl+1/2/3).
Pick the Excel workbook that holds the plant inputs — browse, drop an .xlsx
onto the page, or choose a recent file. Every sheet is validated against what
the model needs before you can waste a solve on it:
| Sheet | Required columns |
|---|---|
products_data |
product, price, daily_capacity, initial_inventory, water_cost, fixed_cost, raw_stone_factor |
demand_forecast |
product, day, packing_type, customer, quantity |
conversions |
input_product, output_product, factor |
shifts |
shift, labor_cost |
energy_prices |
tier, electricity_price, gas_price |
resource_outages |
day, hour, resource |
The status chip reads Ready, Incomplete (missing sheets or columns) or Unreadable, and every sheet is previewable in a sortable, filterable table.
Set the solver, an optional time limit, MIP gap and thread count, then start the
solve. The stage checklist, elapsed timer and live solver log show what is
happening. Cancel (Esc) stops the run immediately.
Runs execute in an isolated process by default: the window stays responsive, Cancel is a real abort rather than a request, and a solver crash cannot take the application down. Turn it off in Solver settings only when debugging.
Six KPI cards (profit, revenue, cost, service level, production, utilization),
five charts (production, inventory, service by product, cost breakdown, and a
schedule heat map of active line-hours), and five result tables. Every table
sorts, filters and exports; Export workbook (Ctrl+E) writes all of them
plus a summary sheet to one .xlsx.
Ctrl+O |
Open workbook |
Ctrl+R |
Reload workbook |
F5 |
Run optimization |
Esc |
Cancel run |
Ctrl+E |
Export results |
Ctrl+1/2/3 |
Data / Run / Results |
Ctrl+T |
Toggle light / dark theme |
Ctrl+, |
Preferences |
F1 |
About |
The whole interface runs in English or Persian: menus, pages, validation messages, the solver log, chart titles and axis labels, and table headers. Switch with the language button at the bottom of the sidebar, View → Language, or in Preferences. The choice is remembered between sessions; English is the default on a fresh install.
Choosing Persian puts Qt into right-to-left mode, so the whole window mirrors — sidebar, menus, tables and dialogs. The UI font switches to a face with proper Arabic-script coverage.
A few deliberate decisions:
- Numbers stay in Latin digits in both languages (
13,087,414, not۱۳٬۰۸۷٬۴۱۴). The app never sets a PersianQLocale, so figures copied out of a table still parse as numbers in Excel and match the English view. - Exports follow the UI language. In Persian, sheet names and column headers are written in Persian, and CSV is saved as UTF-8 with BOM so Excel opens it correctly. Anything downstream that matches English column names should export from the English UI.
- Data stays as authored. Product names, customer names and packing types come from the workbook and are never translated.
- Chart text is not pre-shaped. matplotlib already applies Arabic-script joining and bidirectional ordering; running the text through a reshaper as well renders it backwards.
Translations live in one dictionary in apc_opt/i18n.py. To add or correct a
string, edit that file — there is no compile step. To add a third language, add
its entry to LANGUAGES and a catalogue to CATALOG.
The installed application directory is treated as read-only. Everything the app
writes lives under a per-user data folder (%LOCALAPPDATA%\APC Optimizer on
Windows), reachable from Help → Open application data folder:
APC Optimizer/
├─ workspace/ input tables materialised as CSV for the model
├─ logs/ app.log (rotating, 2 MB × 3)
├─ exports/ default destination for exported results
└─ ui/ generated theme assets
Selecting a workbook regenerates workspace/, which is what the model actually
reads — so the choice reaches the solver instead of only sitting in a text box.
The files under model/ are never modified by a run.
apc_opt/
├─ __init__.py app metadata (name, version)
├─ paths.py application vs. user-writable locations
├─ workspace.py workbook validation + CSV materialisation
├─ i18n.py language catalogue, RTL and font selection
├─ core.py pipeline: inputs → data → model → solve → extract
├─ result_extract.py solved instance → DataFrames + KPIs
├─ export.py Excel / CSV export
├─ runner.py out-of-process solver execution
└─ gui/
├─ app.py entry point, logging, crash handling
├─ main_window.py shell: sidebar, menus, status bar, orchestration
├─ theme.py design tokens + stylesheet (light / dark)
├─ icons.py runtime-rendered vector icons and QSS assets
├─ widgets.py cards, KPI tiles, tables, log console, stage list
├─ charts.py themed matplotlib charts
├─ models.py DataFrame-backed Qt item models
├─ workers.py run controller (process + thread fallback)
├─ settings.py persisted preferences
├─ dialogs.py About, Preferences, error dialog
└─ pages/ data / run / results pages
model/ the Pyomo model (no dataset — see model/README.md)
The optimization model in model/ is untouched — the app wraps it.
pyinstaller --noconfirm --clean APC-Optimizer.specOutput lands in dist/APC-Optimizer/. The spec bundles model/, assets/ and
the Pyomo/HiGHS data files, and builds windowed (the app has its own log view).
run_gui.py calls multiprocessing.freeze_support(), which frozen builds
require for the isolated solver process.
installer/inno_setup/ wraps the PyInstaller output in a Windows setup;
installer/pynsist/ builds an installer that bundles Python plus wheels. See
installer/README-INSTALLERS.md.
Zipping dist/APC-Optimizer/ gives a portable app: unzip anywhere and run
APC-Optimizer.exe. Python, Qt, Pyomo and HiGHS are all inside the folder,
nothing is installed system-wide, and the app writes only to
%LOCALAPPDATA%\APC Optimizer.
HiGHS via Pyomo APPsi is the default and ships with highspy. The Run page can
also target command-line HiGHS, CBC or GLPK if they are on PATH; a missing
solver is reported as a clean run failure rather than a crash.
MIT — see LICENSE. The bundled Pyomo model is covered by the same terms; the ELCC plant data it was developed against is not distributed here.