QoL and metric value inverter
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import os
|
||||
import secrets
|
||||
from pathlib import Path
|
||||
@@ -53,10 +54,54 @@ def close_db(exc: BaseException | None = None) -> None:
|
||||
repo.conn.close()
|
||||
|
||||
|
||||
_SI_PREFIXES = [
|
||||
(1e12, "T"),
|
||||
(1e9, "G"),
|
||||
(1e6, "M"),
|
||||
(1e3, "k"),
|
||||
]
|
||||
|
||||
|
||||
def _si_format(value: object) -> str:
|
||||
"""Format a number with SI prefixes for readability.
|
||||
|
||||
Handles string inputs (like dep.value) by trying float conversion first.
|
||||
Non-numeric values are returned as-is.
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
num = float(value)
|
||||
except (ValueError, TypeError):
|
||||
return value
|
||||
elif isinstance(value, (int, float)):
|
||||
num = float(value)
|
||||
else:
|
||||
return str(value)
|
||||
|
||||
if math.isnan(num) or math.isinf(num):
|
||||
return str(value)
|
||||
|
||||
abs_num = abs(num)
|
||||
if abs_num < 1000:
|
||||
# Small numbers: drop trailing zeros, cap at 4 significant figures
|
||||
if num == int(num) and abs_num < 100:
|
||||
return str(int(num))
|
||||
return f"{num:.4g}"
|
||||
|
||||
for threshold, prefix in _SI_PREFIXES:
|
||||
if abs_num >= threshold:
|
||||
scaled = num / threshold
|
||||
return f"{scaled:.4g}{prefix}"
|
||||
|
||||
return f"{num:.4g}"
|
||||
|
||||
|
||||
def create_app() -> Flask:
|
||||
app = Flask(__name__)
|
||||
app.secret_key = _load_or_generate_secret_key()
|
||||
|
||||
app.jinja_env.filters["si"] = _si_format
|
||||
|
||||
app.teardown_appcontext(close_db)
|
||||
|
||||
# Register blueprints
|
||||
|
||||
@@ -80,7 +80,11 @@ def metric_add(domain_id: int):
|
||||
if not metric_name:
|
||||
flash("Metric name is required.", "error")
|
||||
else:
|
||||
mb = MetricBound(metric_name=metric_name, weight=weight, norm_min=norm_min, norm_max=norm_max, unit=unit)
|
||||
lower_is_better = bool(request.form.get("lower_is_better"))
|
||||
mb = MetricBound(
|
||||
metric_name=metric_name, weight=weight, norm_min=norm_min,
|
||||
norm_max=norm_max, unit=unit, lower_is_better=lower_is_better,
|
||||
)
|
||||
repo.add_metric_bound(domain_id, mb)
|
||||
flash("Metric added.", "success")
|
||||
domain = repo.get_domain_by_id(domain_id)
|
||||
@@ -99,7 +103,8 @@ def metric_edit(domain_id: int, metric_id: int):
|
||||
domain = repo.get_domain_by_id(domain_id)
|
||||
return render_template("domains/_metrics_table.html", domain=domain)
|
||||
unit = request.form.get("unit", "").strip()
|
||||
repo.update_metric_bound(domain_id, metric_id, weight, norm_min, norm_max, unit)
|
||||
lower_is_better = bool(request.form.get("lower_is_better"))
|
||||
repo.update_metric_bound(domain_id, metric_id, weight, norm_min, norm_max, unit, lower_is_better)
|
||||
flash("Metric updated.", "success")
|
||||
domain = repo.get_domain_by_id(domain_id)
|
||||
return render_template("domains/_metrics_table.html", domain=domain)
|
||||
|
||||
@@ -54,6 +54,9 @@ def result_detail(domain_name: str, combo_id: int):
|
||||
return redirect(url_for("results.results_domain", domain_name=domain_name))
|
||||
|
||||
result = repo.get_result(combo_id, domain.id)
|
||||
if not result:
|
||||
flash("No results for this combination in this domain.", "error")
|
||||
return redirect(url_for("results.results_domain", domain_name=domain_name))
|
||||
scores = repo.get_combination_scores(combo_id, domain.id)
|
||||
|
||||
return render_template(
|
||||
|
||||
@@ -54,6 +54,7 @@ h3 { font-size: 1rem; margin-bottom: 0.25rem; }
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
.card-grid > * { min-width: 0; overflow-x: auto; }
|
||||
|
||||
/* ── Tables ──────────────────────────────────────────────── */
|
||||
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
|
||||
@@ -79,7 +80,10 @@ table.compact th, table.compact td { padding: 0.25rem 0.4rem; font-size: 0.85rem
|
||||
.badge-range_min, .badge-range_max { background: #fef3c7; color: #92400e; }
|
||||
.badge-excludes { background: #fee2e2; color: #991b1b; }
|
||||
.badge-valid { background: #dcfce7; color: #166534; }
|
||||
.badge-blocked { background: #fee2e2; color: #991b1b; }
|
||||
.badge-p1_fail { background: #fee2e2; color: #991b1b; }
|
||||
.badge-p2_fail { background: #fee2e2; color: #991b1b; }
|
||||
.badge-p3_fail { background: #fee2e2; color: #991b1b; }
|
||||
.badge-p4_fail { background: #fee2e2; color: #991b1b; }
|
||||
.badge-scored { background: #dbeafe; color: #1e40af; }
|
||||
.badge-llm_reviewed { background: #e0f2fe; color: #0369a1; }
|
||||
.badge-reviewed { background: #f3e8ff; color: #6b21a8; }
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<th>Weight</th>
|
||||
<th>Norm Min</th>
|
||||
<th>Norm Max</th>
|
||||
<th>Direction</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -15,8 +16,9 @@
|
||||
<td>{{ mb.metric_name }}</td>
|
||||
<td>{{ mb.unit or '—' }}</td>
|
||||
<td>{{ mb.weight }}</td>
|
||||
<td>{{ mb.norm_min }}</td>
|
||||
<td>{{ mb.norm_max }}</td>
|
||||
<td>{{ mb.norm_min|si }}</td>
|
||||
<td>{{ mb.norm_max|si }}</td>
|
||||
<td>{{ '↓ lower' if mb.lower_is_better else '↑ higher' }}</td>
|
||||
<td class="actions">
|
||||
<button class="btn btn-sm"
|
||||
onclick="this.closest('tr').nextElementSibling.style.display='table-row'; this.closest('tr').style.display='none'">
|
||||
@@ -39,6 +41,7 @@
|
||||
<td><input name="weight" type="number" step="any" value="{{ mb.weight }}" required></td>
|
||||
<td><input name="norm_min" type="number" step="any" value="{{ mb.norm_min }}" required></td>
|
||||
<td><input name="norm_max" type="number" step="any" value="{{ mb.norm_max }}" required></td>
|
||||
<td><label><input type="checkbox" name="lower_is_better" value="1" {{ 'checked' if mb.lower_is_better }}> lower is better</label></td>
|
||||
<td>
|
||||
<button type="submit" class="btn btn-sm btn-primary">Save</button>
|
||||
<button type="button" class="btn btn-sm"
|
||||
@@ -63,6 +66,7 @@
|
||||
<input name="weight" type="number" step="any" placeholder="weight" value="1.0" required>
|
||||
<input name="norm_min" type="number" step="any" placeholder="norm min" value="0.0" required>
|
||||
<input name="norm_max" type="number" step="any" placeholder="norm max" value="1.0" required>
|
||||
<label><input type="checkbox" name="lower_is_better" value="1"> lower is better</label>
|
||||
<button type="submit" class="btn btn-primary">Add</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<p>{{ d.description }}</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Metric</th><th>Unit</th><th>Weight</th><th>Norm Min</th><th>Norm Max</th></tr>
|
||||
<tr><th>Metric</th><th>Unit</th><th>Weight</th><th>Norm Min</th><th>Norm Max</th><th>Direction</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for mb in d.metric_bounds %}
|
||||
@@ -24,8 +24,9 @@
|
||||
<td>{{ mb.metric_name }}</td>
|
||||
<td>{{ mb.unit }}</td>
|
||||
<td>{{ mb.weight }}</td>
|
||||
<td>{{ mb.norm_min }}</td>
|
||||
<td>{{ mb.norm_max }}</td>
|
||||
<td>{{ mb.norm_min|si }}</td>
|
||||
<td>{{ mb.norm_max|si }}</td>
|
||||
<td>{{ '↓ lower' if mb.lower_is_better else '↑ higher' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<tr>
|
||||
<td>{{ dep.category }}</td>
|
||||
<td>{{ dep.key }}</td>
|
||||
<td>{{ dep.value }}</td>
|
||||
<td>{{ dep.value|si }}</td>
|
||||
<td>{{ dep.unit or '—' }}</td>
|
||||
<td><span class="badge badge-{{ dep.constraint_type }}">{{ dep.constraint_type }}</span></td>
|
||||
<td class="actions">
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<td>1 — Constraints</td>
|
||||
<td>{{ run.combos_pass1 or 0 }} checked
|
||||
{%- if (run.combos_pass2 or 0) > 0 and (run.combos_pass1 or 0) > (run.combos_pass2 or 0) %},
|
||||
<span class="badge badge-blocked">{{ (run.combos_pass1 or 0) - (run.combos_pass2 or 0) }} blocked</span>
|
||||
<span class="badge badge-p1_fail">{{ (run.combos_pass1 or 0) - (run.combos_pass2 or 0) }} failed</span>
|
||||
{%- endif -%}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<th>Status</th>
|
||||
<th>Total</th>
|
||||
<th>P1 Checked</th>
|
||||
<th>P1 Blocked</th>
|
||||
<th>P1 Failed</th>
|
||||
<th>P2 Estimated</th>
|
||||
<th>P3 Scored</th>
|
||||
<th>P4 Reviewed</th>
|
||||
@@ -113,7 +113,7 @@
|
||||
<td><span class="badge badge-{{ run.status }}">{{ run.status }}</span></td>
|
||||
<td>{{ run.total_combos or '—' }}</td>
|
||||
<td>{{ run.combos_pass1 or '—' }}</td>
|
||||
<td>{% if blocked %}<span class="badge badge-blocked">{{ blocked }}</span>{% else %}—{% endif %}</td>
|
||||
<td>{% if blocked %}<span class="badge badge-p1_fail">{{ blocked }}</span>{% else %}—{% endif %}</td>
|
||||
<td>{{ run.combos_pass2 or '—' }}</td>
|
||||
<td>{{ run.combos_pass3 or '—' }}</td>
|
||||
<td>{{ run.combos_pass4 or '—' }}</td>
|
||||
@@ -133,7 +133,7 @@
|
||||
<h3>{{ d.name }} <span class="subtitle">{{ d.description }}</span></h3>
|
||||
<dl class="summary-dl">
|
||||
<dt>Results</dt><dd>{{ s.total_results }} scored combinations</dd>
|
||||
<dt>Blocked</dt><dd>{{ s.blocked }} combinations</dd>
|
||||
<dt>Failed</dt><dd>{{ s.failed }} combinations</dd>
|
||||
<dt>Score range</dt><dd class="score-cell">{{ "%.4f"|format(s.min_score) }} — {{ "%.4f"|format(s.max_score) }}</dd>
|
||||
<dt>Avg score</dt><dd class="score-cell">{{ "%.4f"|format(s.avg_score) }}</dd>
|
||||
<dt>Last pass</dt><dd>{{ s.last_pass }}</dd>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
{% for dep in e.dependencies %}
|
||||
<tr>
|
||||
<td>{{ dep.key }}</td>
|
||||
<td>{{ dep.value }}{{ ' ' + dep.unit if dep.unit else '' }}</td>
|
||||
<td>{{ dep.value|si }}{{ ' ' + dep.unit if dep.unit else '' }}</td>
|
||||
<td><span class="badge badge-{{ dep.constraint_type }}">{{ dep.constraint_type }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -77,10 +77,10 @@
|
||||
<tr>
|
||||
<td>{{ s.metric_name }}</td>
|
||||
{% set unit = s.metric_unit or '' %}
|
||||
<td class="score-cell">{{ "%.2f"|format(s.raw_value) if s.raw_value is not none else '—' }}{{ ' ' + unit if unit and s.raw_value is not none else '' }}</td>
|
||||
<td class="score-cell">{{ s.raw_value|si if s.raw_value is not none else '—' }}{{ ' ' + unit if unit and s.raw_value is not none else '' }}</td>
|
||||
<td>
|
||||
{%- if mb -%}
|
||||
{{ "%.2f"|format(mb.norm_min) }} — {{ "%.2f"|format(mb.norm_max) }}{{ ' ' + unit if unit else '' }}
|
||||
{{ mb.norm_min|si }} — {{ mb.norm_max|si }}{{ ' ' + unit if unit else '' }}
|
||||
{%- else -%}
|
||||
—
|
||||
{%- endif -%}
|
||||
@@ -88,22 +88,22 @@
|
||||
<td>
|
||||
{%- if mb and s.raw_value is not none -%}
|
||||
{%- if s.raw_value <= mb.norm_min -%}
|
||||
<span class="badge badge-blocked">at/below min</span>
|
||||
<span class="badge badge-{{ 'valid' if mb.lower_is_better else 'p1_fail' }}">at/below min{{ ' (best)' if mb.lower_is_better else '' }}</span>
|
||||
{%- elif s.raw_value >= mb.norm_max -%}
|
||||
<span class="badge badge-valid">at/above max</span>
|
||||
<span class="badge badge-{{ 'p1_fail' if mb.lower_is_better else 'valid' }}">at/above max{{ ' (worst)' if mb.lower_is_better else '' }}</span>
|
||||
{%- else -%}
|
||||
{% set pct = ((s.raw_value - mb.norm_min) / (mb.norm_max - mb.norm_min) * 100) | int %}
|
||||
<div class="metric-bar-container">
|
||||
<div class="metric-bar" style="width: {{ pct }}%"></div>
|
||||
</div>
|
||||
<span class="metric-bar-label">~{{ pct }}%</span>
|
||||
<span class="metric-bar-label">~{{ pct }}%{{ ' ↓' if mb.lower_is_better else '' }}</span>
|
||||
{%- endif -%}
|
||||
{%- else -%}
|
||||
—
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="score-cell">{{ "%.4f"|format(s.normalized_score) if s.normalized_score is not none else '—' }}</td>
|
||||
<td>{{ "%.0f%%"|format(mb.weight * 100) if mb else '—' }}</td>
|
||||
<td>{{ "%.0f%%"|format(mb.weight * 100) if mb else '—' }}{{ ' ↓' if mb and mb.lower_is_better else '' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<td>{{ r.combination.entities|map(attribute='name')|join(' + ') }}</td>
|
||||
<td><span class="badge badge-{{ r.combination.status }}">{{ r.combination.status }}</span></td>
|
||||
<td class="block-reason-cell">
|
||||
{%- if r.combination.status == 'blocked' and r.combination.block_reason -%}
|
||||
{%- if r.combination.status.endswith('_fail') and r.combination.block_reason -%}
|
||||
{{ r.combination.block_reason }}
|
||||
{%- elif r.novelty_flag -%}
|
||||
{{ r.novelty_flag }}
|
||||
|
||||
Reference in New Issue
Block a user