Add Flask web UI, Docker Compose, core engine + tests

- physcom core: CLI, 5-pass pipeline, SQLite repo, 37 tests
- physcom_web: Flask app with HTMX for entity/domain/pipeline/results CRUD
- Docker Compose: web + cli services sharing a named volume for the DB
- Clean up local settings to use wildcard permissions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Simonson, Andrew
2026-02-18 13:59:53 -06:00
parent 6e0f82835a
commit 8118a62242
54 changed files with 3505 additions and 1 deletions

View File

@@ -0,0 +1,69 @@
{% extends "base.html" %}
{% block title %}Results — PhysCom{% endblock %}
{% block content %}
<h1>Results</h1>
<div class="form-row" style="margin-bottom:1rem">
{% for d in domains %}
<a href="{{ url_for('results.results_domain', domain_name=d.name) }}"
class="btn {{ 'btn-primary' if domain and domain.name == d.name else '' }}">
{{ d.name }}
</a>
{% endfor %}
</div>
{% if domain and results is not none %}
<div class="card">
<h2>{{ domain.name }} <span class="subtitle">{{ domain.description }}</span></h2>
{% if statuses %}
<div class="filter-row">
<span>Filter:</span>
<a href="{{ url_for('results.results_domain', domain_name=domain.name) }}"
class="btn btn-sm {{ '' if status_filter else 'btn-primary' }}">All</a>
{% for s, cnt in statuses.items() %}
<a href="{{ url_for('results.results_domain', domain_name=domain.name, status=s) }}"
class="btn btn-sm {{ 'btn-primary' if status_filter == s else '' }}">
{{ s }} ({{ cnt }})
</a>
{% endfor %}
</div>
{% endif %}
{% if not results %}
<p class="empty">No results yet. <a href="{{ url_for('pipeline.pipeline_form') }}">Run the pipeline</a> first.</p>
{% else %}
<table>
<thead>
<tr>
<th>#</th>
<th>Score</th>
<th>Entities</th>
<th>Status</th>
<th>Novelty</th>
<th></th>
</tr>
</thead>
<tbody>
{% for r in results %}
<tr>
<td>{{ loop.index }}</td>
<td class="score-cell">{{ "%.4f"|format(r.composite_score) }}</td>
<td>{{ r.combination.entities|map(attribute='name')|join(' + ') }}</td>
<td><span class="badge badge-{{ r.combination.status }}">{{ r.combination.status }}</span></td>
<td>{{ r.novelty_flag or '—' }}</td>
<td>
<a href="{{ url_for('results.result_detail', domain_name=domain.name, combo_id=r.combination.id) }}"
class="btn btn-sm">View</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
{% elif not domain %}
<p class="empty">Select a domain above to view results.</p>
{% endif %}
{% endblock %}