Spaces:
Sleeping
Sleeping
| {% extends "base.html" %} | |
| {% block content %} | |
| <div class="container py-4"> | |
| <h3 class="fw-bold mb-4">Molecular Evolution with Genetic Algoritm</h3> | |
| <!-- Input / Config (CLI equivalent of cli.py prompts) --> | |
| <div class="card card-metric p-4 mb-4"> | |
| <form method="POST" id="gen-form"> | |
| <!-- Optimization Mode --> | |
| <div class="mb-3"> | |
| <label class="form-label">Optimisation Mode</label> | |
| <select class="form-select" name="mode" id="mode"> | |
| <option value="target" selected> | |
| Target a specific CN value (minimise error from target) | |
| </option> | |
| <option value="maximize"> | |
| Maximise CN (find highest possible CN) | |
| </option> | |
| </select> | |
| </div> | |
| <!-- Target CN (only shown for target mode) --> | |
| <div class="mb-3" id="target-cn-block"> | |
| <label class="form-label">Enter target CN</label> | |
| <input | |
| type="number" | |
| step="0.1" | |
| name="target_cn" | |
| id="target_cn" | |
| class="form-control" | |
| placeholder="50" | |
| value="50" | |
| required | |
| > | |
| <div class="form-text"> | |
| Very low targets can make optimisation challenging; consider CN > 40. | |
| </div> | |
| </div> | |
| <!-- Minimise YSI --> | |
| <div class="form-check mb-3"> | |
| <input class="form-check-input" type="checkbox" name="minimize_ysi" id="minimize_ysi"> | |
| <label class="form-check-label" for="minimize_ysi">Minimise YSI (y/n)</label> | |
| </div> | |
| <button id="generate-btn" class="btn btn-primary" type="submit"> | |
| Run Evolution | |
| </button> | |
| <div id="loading" class="mt-3" style="display:none;"> | |
| <div class="spinner-border text-primary" role="status"></div> | |
| <span class="ms-2">Generating molecules… this may take a few minutes.</span> | |
| </div> | |
| </form> | |
| </div> | |
| <!-- Configuration Summary (CLI equivalent printout) --> | |
| {% if run_info %} | |
| <div class="card card-metric p-4 mb-4"> | |
| <h5 class="fw-bold mb-3">CONFIGURATION SUMMARY</h5> | |
| <ul class="mb-0"> | |
| <li><strong>Mode:</strong> {{ run_info.mode }}</li> | |
| <li><strong>Minimise YSI:</strong> {{ run_info.minimize_ysi }}</li> | |
| <li><strong>Optimisation:</strong> | |
| {% if run_info.minimize_ysi == "Yes" %} | |
| Multi-objective (CN + YSI) | |
| {% else %} | |
| Single-objective (CN only) | |
| {% endif %} | |
| </li> | |
| </ul> | |
| </div> | |
| {% endif %} | |
| <!-- BEST CANDIDATES (CLI equivalent of results.py output) --> | |
| {% if final_table %} | |
| <div class="card card-metric p-4 mb-4"> | |
| <h5 class="fw-bold mb-3">BEST CANDIDATES</h5> | |
| <div class="table-responsive"> | |
| {{ final_table|safe }} | |
| </div> | |
| {% endif %} | |
| <!-- PARETO FRONT (CLI equivalent) --> | |
| {% if pareto_table %} | |
| <div class="card card-metric p-4 mb-4"> | |
| <h5 class="fw-bold mb-3">PARETO FRONT (Non-dominated solutions)</h5> | |
| <div class="table-responsive"> | |
| {{ pareto_table|safe }} | |
| </div> | |
| {% endif %} | |
| {% if history %} | |
| <div class="card card-metric p-4 mb-4"> | |
| <h5 class="fw-bold mb-3">Evolution Timeline (Sample Structures per Generation)</h5> | |
| <p class="text-muted mb-3"> | |
| The top 5 ranked survivor molecules are shown for each generation, based on the optimisation objective. | |
| </p> | |
| {% for h in history %} | |
| <div class="mb-4"> | |
| <h6 class="fw-bold mb-2">Generation {{ h.generation }}</h6> | |
| {% if h.samples and h.samples|length > 0 %} | |
| <div class="d-flex flex-wrap gap-3"> | |
| {% for s in h.samples %} | |
| <div class="border rounded p-2 text-center" style="width: 260px;"> | |
| <div class="badge bg-secondary mb-1"> | |
| Rank {{ s.rank }} | |
| </div> | |
| <!-- Molecule name / rank --> | |
| <div class="fw-semibold mb-1"> | |
| {{ s.name }} | |
| </div> | |
| <!-- Structure --> | |
| <img | |
| src="{{ url_for('static', filename='generated/' ~ s.img_id) }}" | |
| class="img-fluid" | |
| style="max-width:240px;" | |
| alt="Molecule structure" | |
| /> | |
| <!-- SMILES --> | |
| <div class="mt-2 small text-muted" style="word-break: break-word;"> | |
| {{ s.smiles }} | |
| </div> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| {% else %} | |
| <div class="text-muted">No valid samples captured for this generation.</div> | |
| {% endif %} | |
| </div> | |
| {% endfor %} | |
| </div> | |
| {% endif %} | |
| {% if error %} | |
| <div class="alert alert-danger">{{ error }}</div> | |
| {% endif %} | |
| </div> | |
| <script> | |
| const form = document.getElementById("gen-form"); | |
| const btn = document.getElementById("generate-btn"); | |
| const loading = document.getElementById("loading"); | |
| const modeSelect = document.getElementById("mode"); | |
| const targetBlock = document.getElementById("target-cn-block"); | |
| const targetInput = document.getElementById("target_cn"); | |
| function syncModeUI() { | |
| const isTarget = (modeSelect.value === "target"); | |
| targetBlock.style.display = isTarget ? "block" : "none"; | |
| targetInput.required = isTarget; | |
| } | |
| document.addEventListener("DOMContentLoaded", syncModeUI); | |
| modeSelect.addEventListener("change", syncModeUI); | |
| form.addEventListener("submit", () => { | |
| btn.disabled = true; | |
| loading.style.display = "block"; | |
| }); | |
| </script> | |
| {% endblock %} | |