Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 27 additions & 0 deletions gui/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
__pycache__
**/__pycache__
*.pyc
*.pyo
*.pyd
.Python
.git
.gitignore
.env
*.env
*.log

# Don't copy local Julia depot into the image — it gets rebuilt during build
julia_depot/

# Editor / OS noise
.DS_Store
*.swp
*.swo
.vscode/
.idea/

# Test / notebook artefacts
*.ipynb
.ipynb_checkpoints/
htmlcov/
.coverage
45 changes: 45 additions & 0 deletions gui/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# ── Stage 1: build ───────────────────────────────────────────────────────────
FROM python:3.11-slim AS base

# System tools needed to compile Python extensions and download Julia
RUN apt-get update && apt-get install -y --no-install-recommends \
wget ca-certificates git gcc g++ make \
&& rm -rf /var/lib/apt/lists/*

# ── Install Julia ─────────────────────────────────────────────────────────────
ARG JULIA_VERSION=1.10.3
RUN wget -qO /tmp/julia.tar.gz \
"https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-${JULIA_VERSION}-linux-x86_64.tar.gz" \
&& mkdir -p /opt/julia \
&& tar -xzf /tmp/julia.tar.gz -C /opt/julia --strip-components=1 \
&& ln -s /opt/julia/bin/julia /usr/local/bin/julia \
&& rm /tmp/julia.tar.gz

# Julia stores downloaded packages and precompiled .ji files here.
# Putting it outside /app means it survives COPY . . and can be a named volume.
ENV JULIA_DEPOT_PATH=/julia_depot
RUN mkdir -p /julia_depot

ENV PYTHONUNBUFFERED=1

WORKDIR /app

# ── Python dependencies ───────────────────────────────────────────────────────
# Copy only requirements first so pip layer is cached on code-only changes
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# ── App source ────────────────────────────────────────────────────────────────
COPY . .

# ── Pre-install and precompile Julia packages ─────────────────────────────────
# Running this during build bakes the compiled .ji files into the image so
# container startup is fast (~5 s) instead of requiring a 5-10 min cold-compile.
RUN python3 -c "\
import autoeis as ae; \
import numpy as np; \
print('AutoEIS + Julia packages installed and precompiled.')"

EXPOSE 8050

CMD ["gunicorn", "-c", "gunicorn_conf.py", "app:server"]
66 changes: 66 additions & 0 deletions gui/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import dash
from dash import Dash, dcc, html
import dash_bootstrap_components as dbc

app = Dash(
__name__,
use_pages=True,
external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.FONT_AWESOME],
suppress_callback_exceptions=True,
title="AutoEIS GUI",
)

STEPS = [
("① Data", "/"),
("② Mode", "/mode"),
("③ Fitting", "/fitting"),
("④ Results", "/results"),
]

navbar = dbc.Navbar(
dbc.Container(
[
dbc.NavbarBrand(
html.Span(
[html.I(className="fa fa-bolt me-2 text-warning"), "AutoEIS GUI"],
className="fw-bold fs-5",
)
),
dbc.Nav(
[
dbc.NavItem(dbc.NavLink(label, href=href, active="exact", className="fw-semibold"))
for label, href in STEPS
],
className="ms-auto gap-1",
navbar=True,
),
],
fluid=True,
),
color="dark",
dark=True,
sticky="top",
className="mb-4 shadow",
)

app.layout = html.Div(
[
# Shared state stores — all in app layout so they survive page navigation
dcc.Store(id="store-data", storage_type="memory"),
dcc.Store(id="store-mode", storage_type="memory"),
dcc.Store(id="store-task-id", storage_type="memory"),
dcc.Store(id="store-results", storage_type="memory"),
# Data-page stores (moved here so data page state survives navigation)
dcc.Store(id="store-raw", storage_type="memory"),
dcc.Store(id="store-deleted", data=[]),
dcc.Store(id="store-pending-delete", data=[]),
dcc.Store(id="store-df-cols", storage_type="memory"),
navbar,
dbc.Container(dash.page_container, fluid=True, className="pb-5"),
]
)

server = app.server # expose for gunicorn/deployment

if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=8050)
47 changes: 47 additions & 0 deletions gui/assets/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* AutoEIS GUI – custom styles */

body {
background-color: #f8f9fa;
font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
}

/* Navbar brand */
.navbar-brand {
font-size: 1.3rem;
}

/* Card hover effect for Quick Mode selection */
[id^="{"type":"quick-card"]:hover {
border-color: #0d6efd !important;
box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* DataTable tweaks */
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner td {
font-family: "Courier New", monospace;
}

/* Plotly chart border */
.js-plotly-plot {
border-radius: 4px;
}

/* Progress bar label */
#progress-stage {
font-size: 0.9rem;
}

/* Summary badges spacing */
.badge {
font-size: 0.78rem;
}

/* Collapse card header button */
#pp-toggle {
text-decoration: none;
font-size: 1rem;
}
#pp-toggle:focus {
box-shadow: none;
}
35 changes: 35 additions & 0 deletions gui/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: "3.9"

services:
autoeis:
build: .
ports:
- "8050:8050"
environment:
- PORT=8050
- MAX_CONCURRENT_TASKS=3 # cap simultaneous analyses
- LOG_LEVEL=info
restart: unless-stopped
# Persist the Julia depot so a re-deploy doesn't re-precompile from scratch
volumes:
- julia_depot:/julia_depot

# Optional nginx reverse proxy with HTTPS.
# Remove this block if you're using a cloud platform (Fly, Railway, Render)
# that handles SSL termination for you.
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/certs:/etc/nginx/certs:ro # put your SSL certs here
depends_on:
- autoeis
restart: unless-stopped
profiles:
- with-nginx # run with: docker compose --profile with-nginx up

volumes:
julia_depot:
Loading
Loading