Milestone 5 polish

This commit is contained in:
2026-04-26 00:08:55 +02:00
parent 8d1e1ff583
commit e8fdfe6e17
6 changed files with 76 additions and 5 deletions

View File

@@ -587,10 +587,14 @@ class RegistryService:
status: str | None = None,
language: str | None = None,
framework: str | None = None,
ability: str | None = None,
capability: str | None = None,
) -> list[SearchResult]:
return self.store.search(
query,
status=status,
language=language,
framework=framework,
ability=ability,
capability=capability,
)

View File

@@ -1282,6 +1282,8 @@ class RegistryStore:
status: str | None = None,
language: str | None = None,
framework: str | None = None,
ability: str | None = None,
capability: str | None = None,
) -> list[SearchResult]:
term = query.strip()
needle = f"%{term}%"
@@ -1294,6 +1296,8 @@ class RegistryStore:
status=status,
language=language,
framework=framework,
ability=ability,
capability=capability,
)
if repository_ids is not None and not repository_ids:
return []
@@ -1502,6 +1506,8 @@ class RegistryStore:
status: str | None,
language: str | None,
framework: str | None,
ability: str | None,
capability: str | None,
) -> list[int] | None:
filters: list[set[int]] = []
if status:
@@ -1530,6 +1536,26 @@ class RegistryStore:
(framework,),
).fetchall()
filters.append({row["repository_id"] for row in rows})
if ability:
rows = connection.execute(
"""
SELECT DISTINCT repository_id
FROM approved_abilities
WHERE name LIKE ? OR description LIKE ?
""",
(f"%{ability}%", f"%{ability}%"),
).fetchall()
filters.append({row["repository_id"] for row in rows})
if capability:
rows = connection.execute(
"""
SELECT DISTINCT repository_id
FROM approved_capabilities
WHERE name LIKE ? OR description LIKE ?
""",
(f"%{capability}%", f"%{capability}%"),
).fetchall()
filters.append({row["repository_id"] for row in rows})
if not filters:
return None
repository_ids = set.intersection(*filters)

View File

@@ -771,6 +771,8 @@ def search(
status: str | None = None,
language: str | None = None,
framework: str | None = None,
ability: str | None = None,
capability: str | None = None,
service: RegistryService = Depends(get_service),
) -> list[dict[str, object]]:
return [
@@ -780,6 +782,8 @@ def search(
status=status,
language=language,
framework=framework,
ability=ability,
capability=capability,
)
]

View File

@@ -179,6 +179,8 @@ def search_page(
status: str = "",
language: str = "",
framework: str = "",
ability: str = "",
capability: str = "",
service: RegistryService = Depends(get_service),
) -> HTMLResponse:
results = (
@@ -187,6 +189,8 @@ def search_page(
status=status or None,
language=language or None,
framework=framework or None,
ability=ability or None,
capability=capability or None,
)
if q.strip()
else []
@@ -194,7 +198,7 @@ def search_page(
rows = "\n".join(
f"""
<tr>
<td><a href="/ui/repos/{result.repository_id}">{escape(result.repository_name)}</a></td>
<td><a href="{search_result_href(asdict(result))}">{escape(result.repository_name)}</a></td>
<td><span class="pill">{escape(result.match_type)}</span></td>
<td>
<strong>{escape(result.match_name)}</strong>
@@ -223,6 +227,8 @@ def search_page(
<label>Status <input name="status" value="{escape(status)}" placeholder="indexed"></label>
<label>Language <input name="language" value="{escape(language)}" placeholder="Python"></label>
<label>Framework <input name="framework" value="{escape(framework)}" placeholder="FastAPI"></label>
<label>Ability <input name="ability" value="{escape(ability)}" placeholder="Email Routing"></label>
<label>Capability <input name="capability" value="{escape(capability)}" placeholder="Classification"></label>
</div>
<div class="actions">
<button type="submit">Search</button>
@@ -930,7 +936,7 @@ def render_ability_map(ability_map: dict) -> str:
)
capabilities.append(
f"""
<li>
<li id="capability-{capability['id']}">
<strong>{escape(capability['name'])}</strong>
<p class="muted">{escape(capability['description'])}</p>
<ul>{features}{evidence}</ul>
@@ -939,7 +945,7 @@ def render_ability_map(ability_map: dict) -> str:
)
items.append(
f"""
<li>
<li id="ability-{ability['id']}">
<strong>{escape(ability['name'])}</strong>
<p class="muted">{escape(ability['description'])}</p>
<ul>{''.join(capabilities)}</ul>
@@ -949,6 +955,15 @@ def render_ability_map(ability_map: dict) -> str:
return f'<div class="tree"><ul>{"".join(items)}</ul></div>'
def search_result_href(result: dict) -> str:
href = f"/ui/repos/{result['repository_id']}"
if result.get("capability_id"):
return f"{href}#capability-{result['capability_id']}"
if result.get("ability_id"):
return f"{href}#ability-{result['ability_id']}"
return href
def render_sources(source_refs: list[dict]) -> str:
if not source_refs:
return ""

View File

@@ -161,6 +161,8 @@ def test_search_filters_by_status_language_and_framework(tmp_path):
status="indexed",
language="Python",
framework="FastAPI",
ability="Repository Usefulness",
capability="Repository Structure",
)
wrong_language_results = service.search(
"repository",
@@ -168,10 +170,18 @@ def test_search_filters_by_status_language_and_framework(tmp_path):
language="TypeScript",
framework="FastAPI",
)
wrong_capability_results = service.search(
"repository",
status="indexed",
language="Python",
framework="FastAPI",
capability="Email Routing",
)
assert results
assert {result.repository_name for result in results} == {"Filterable"}
assert wrong_language_results == []
assert wrong_capability_results == []
def test_register_repository_imports_metadata_when_name_is_omitted(tmp_path):

View File

@@ -215,7 +215,12 @@ def test_api_analysis_run_loop(tmp_path):
filtered_search_response = client.get(
"/search",
params={"q": "frontend", "status": "indexed"},
params={
"q": "frontend",
"status": "indexed",
"ability": "Frontend",
"capability": "Frontend Stack",
},
)
assert filtered_search_response.status_code == 200
assert filtered_search_response.json()
@@ -315,10 +320,17 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
assert search_response.status_code == 200
assert "UI Repo" in search_response.text
assert "Field" in search_response.text
assert "#capability-" in search_response.text or "#ability-" in search_response.text
filtered_search_response = client.get(
"/ui/search",
params={"q": "repository", "status": "indexed", "language": "Python"},
params={
"q": "repository",
"status": "indexed",
"language": "Python",
"ability": "Repository Usefulness",
"capability": "Repository",
},
)
assert filtered_search_response.status_code == 200
assert "UI Repo" in filtered_search_response.text