pre-review fixes

This commit is contained in:
2026-07-25 16:42:39 -05:00
parent e030d0d4f3
commit 63295ab80e
8 changed files with 67 additions and 103 deletions

View File

@@ -227,40 +227,36 @@ def review(ctx, combination_id):
@main.command()
@click.argument("domain_name")
@click.option("--format", "fmt", default="md", help="Export format (md)")
@click.option("--top", "-n", default=20, type=int, help="Number of results to export")
@click.option("--output", "-o", default=None, help="Output file path")
@click.pass_context
def export(ctx, domain_name, fmt, top, output):
"""Export results to a report."""
def export(ctx, domain_name, top, output):
"""Export results to a Markdown report."""
repo = _get_repo(ctx.obj["db"])
top_results = repo.get_top_results(domain_name, limit=top)
if not top_results:
click.echo(f"No results for domain '{domain_name}'.")
return
if fmt == "md":
lines = [f"# {domain_name} — Top {len(top_results)} Concepts\n"]
for i, r in enumerate(top_results, 1):
combo = r["combination"]
entity_names = " + ".join(e.name for e in combo.entities)
lines.append(f"## {i}. {entity_names} (score: {r['composite_score']:.4f})")
if r["novelty_flag"]:
lines.append(f"**Novelty:** {r['novelty_flag']}")
if r["llm_review"]:
lines.append(f"\n{r['llm_review']}")
if r["human_notes"]:
lines.append(f"\n*Notes:* {r['human_notes']}")
lines.append("")
lines = [f"# {domain_name} — Top {len(top_results)} Concepts\n"]
for i, r in enumerate(top_results, 1):
combo = r["combination"]
entity_names = " + ".join(e.name for e in combo.entities)
lines.append(f"## {i}. {entity_names} (score: {r['composite_score']:.4f})")
if r["novelty_flag"]:
lines.append(f"**Novelty:** {r['novelty_flag']}")
if r["llm_review"]:
lines.append(f"\n{r['llm_review']}")
if r["human_notes"]:
lines.append(f"\n*Notes:* {r['human_notes']}")
lines.append("")
content = "\n".join(lines)
if output:
Path(output).write_text(content)
click.echo(f"Exported to {output}")
else:
click.echo(content)
content = "\n".join(lines)
if output:
Path(output).write_text(content)
click.echo(f"Exported to {output}")
else:
click.echo(f"Unsupported format: {fmt}")
click.echo(content)
@main.group()

View File

@@ -268,8 +268,8 @@ class Repository:
by_key.setdefault(r["key"], []).append(r["value"])
return [DomainConstraint(key=k, allowed_values=v) for k, v in by_key.items()]
def get_domain(self, name: str) -> Domain | None:
row = self.conn.execute("SELECT * FROM domains WHERE name = ?", (name,)).fetchone()
def _load_domain(self, where: str, param: str | int) -> Domain | None:
row = self.conn.execute(f"SELECT * FROM domains WHERE {where} = ?", (param,)).fetchone()
if not row:
return None
weights = self.conn.execute(
@@ -296,37 +296,15 @@ class Repository:
constraints=self._load_domain_constraints(row["id"]),
)
def get_domain(self, name: str) -> Domain | None:
return self._load_domain("name", name)
def list_domains(self) -> list[Domain]:
rows = self.conn.execute("SELECT name FROM domains ORDER BY name").fetchall()
return [self.get_domain(r["name"]) for r in rows]
def get_domain_by_id(self, domain_id: int) -> Domain | None:
row = self.conn.execute("SELECT * FROM domains WHERE id = ?", (domain_id,)).fetchone()
if not row:
return None
weights = self.conn.execute(
"""SELECT m.name, m.unit, dmw.weight, dmw.norm_min, dmw.norm_max,
dmw.metric_id, dmw.lower_is_better
FROM domain_metric_weights dmw
JOIN metrics m ON dmw.metric_id = m.id
WHERE dmw.domain_id = ?""",
(row["id"],),
).fetchall()
return Domain(
id=row["id"],
name=row["name"],
description=row["description"] or "",
metric_bounds=[
MetricBound(
metric_name=w["name"], weight=w["weight"],
norm_min=w["norm_min"], norm_max=w["norm_max"],
metric_id=w["metric_id"], unit=w["unit"] or "",
lower_is_better=bool(w["lower_is_better"]),
)
for w in weights
],
constraints=self._load_domain_constraints(row["id"]),
)
return self._load_domain("id", domain_id)
def update_domain(self, domain_id: int, name: str, description: str) -> None:
self.conn.execute(

View File

@@ -338,7 +338,7 @@ RENEWABLE_ACTUATORS: list[Entity] = [
Dependency("energy", "energy_form", "radiation_pressure", None, "requires"),
Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"),
Dependency("environment", "star_proximity", "true", None, "requires"),
Dependency("physical", "surface_area", "100", "", "range_min"),
Dependency("physical", "footprint", "100", "", "range_min"),
Dependency("force", "thrust_profile", "continuous_low", None, "provides"),
Dependency("force", "power_density", "0.01", "W/kg", "provides"),
Dependency("environment", "medium", "space", None, "requires"),
@@ -393,6 +393,7 @@ ROCKET_ACTUATORS: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "nuclear_thermal", None, "requires"),
Dependency("physical", "mass", "1500", "kg", "range_min"),
Dependency("physical", "footprint", "20", "", "range_min"),
Dependency("force", "thrust_profile", "extreme_continuous", None, "provides"),
Dependency("force", "power_density", "50", "W/kg", "provides"),
Dependency("material", "radiation_shielding", "true", None, "requires"),
@@ -644,6 +645,7 @@ PROPELLANT_STORAGE: list[Entity] = [
Dependency("energy", "energy_form", "nuclear_thermal", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "nuclear_fuel", None, "requires"),
Dependency("physical", "mass", "500", "kg", "range_min"),
Dependency("physical", "footprint", "5", "", "range_min"),
Dependency("physical", "energy_density", "1800000000", "J/kg", "provides"),
Dependency("material", "radiation_shielding", "true", None, "requires"),
],

View File

@@ -2,7 +2,6 @@
from __future__ import annotations
import math
import os
import secrets
from pathlib import Path
@@ -55,53 +54,10 @@ 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.jinja_env.filters["qty"] = format_quantity
app.teardown_appcontext(close_db)

View File

@@ -59,12 +59,6 @@ def entity_detail(entity_id: int):
return render_template("entities/detail.html", entity=entity)
@bp.route("/<int:entity_id>/edit")
def entity_edit(entity_id: int):
"""Legacy route — redirect to detail page."""
return redirect(url_for("entities.entity_detail", entity_id=entity_id))
@bp.route("/<int:entity_id>/delete", methods=["POST"])
def entity_delete(entity_id: int):
repo = get_repo()

View File

@@ -31,7 +31,7 @@
<td>{{ e.description }}</td>
<td>{{ e.dependencies|length }}</td>
<td>
<a href="{{ url_for('entities.entity_edit', entity_id=e.id) }}" class="btn btn-sm">Edit</a>
<a href="{{ url_for('entities.entity_detail', entity_id=e.id) }}" class="btn btn-sm">Edit</a>
</td>
</tr>
{% endfor %}