pre-review fixes
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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", "m²", "range_min"),
|
||||
Dependency("physical", "footprint", "100", "m²", "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", "m²", "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", "m²", "range_min"),
|
||||
Dependency("physical", "energy_density", "1800000000", "J/kg", "provides"),
|
||||
Dependency("material", "radiation_shielding", "true", None, "requires"),
|
||||
],
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user