remove most subtitles
This commit is contained in:
parent
d8f2e03d36
commit
947ef8e833
7 changed files with 13 additions and 46 deletions
|
|
@ -55,7 +55,6 @@ def admin_sidebar(*, current_path: str) -> Renderable:
|
||||||
h.p(
|
h.p(
|
||||||
class_="text-xs font-semibold uppercase tracking-[0.24em] text-amber-300"
|
class_="text-xs font-semibold uppercase tracking-[0.24em] text-amber-300"
|
||||||
)["Republisher"],
|
)["Republisher"],
|
||||||
h.p(class_="text-sm text-slate-300")["Admin spike"],
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
h.nav(class_="mt-10 space-y-2")[
|
h.nav(class_="mt-10 space-y-2")[
|
||||||
|
|
@ -147,7 +146,7 @@ def page_shell(
|
||||||
current_path: str,
|
current_path: str,
|
||||||
eyebrow: str,
|
eyebrow: str,
|
||||||
title: str,
|
title: str,
|
||||||
description: str,
|
description: str | None = None,
|
||||||
actions: Node | None = None,
|
actions: Node | None = None,
|
||||||
content: Node,
|
content: Node,
|
||||||
) -> Renderable:
|
) -> Renderable:
|
||||||
|
|
@ -190,7 +189,7 @@ def table_section(
|
||||||
*,
|
*,
|
||||||
eyebrow: str | None = None,
|
eyebrow: str | None = None,
|
||||||
title: str,
|
title: str,
|
||||||
subtitle: str,
|
subtitle: str | None = None,
|
||||||
headers: tuple[str, ...],
|
headers: tuple[str, ...],
|
||||||
rows: tuple[tuple[Node, ...], ...],
|
rows: tuple[tuple[Node, ...], ...],
|
||||||
actions: Node | None = None,
|
actions: Node | None = None,
|
||||||
|
|
@ -217,7 +216,7 @@ def table_section(
|
||||||
class_="text-xs font-semibold uppercase tracking-[0.22em] text-amber-600"
|
class_="text-xs font-semibold uppercase tracking-[0.22em] text-amber-600"
|
||||||
)[eyebrow],
|
)[eyebrow],
|
||||||
h.h2(class_="mt-1 text-xl font-semibold text-slate-950")[title],
|
h.h2(class_="mt-1 text-xl font-semibold text-slate-950")[title],
|
||||||
h.p(class_="mt-1 text-sm text-slate-600")[subtitle],
|
subtitle and h.p(class_="mt-1 text-sm text-slate-600")[subtitle],
|
||||||
],
|
],
|
||||||
actions,
|
actions,
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,6 @@ def dashboard_header() -> Renderable:
|
||||||
h.h1(class_="text-3xl font-semibold tracking-tight text-slate-950")[
|
h.h1(class_="text-3xl font-semibold tracking-tight text-slate-950")[
|
||||||
"Republisher"
|
"Republisher"
|
||||||
],
|
],
|
||||||
h.p(class_="mt-1 text-sm text-slate-600")[
|
|
||||||
"Operational status and live executions."
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
h.div(class_="flex flex-wrap gap-2")[
|
h.div(class_="flex flex-wrap gap-2")[
|
||||||
header_action_link(href="/sources/create", label="Create source"),
|
header_action_link(href="/sources/create", label="Create source"),
|
||||||
|
|
@ -98,7 +95,6 @@ def operational_snapshot(*, snapshot: Mapping[str, str] | None = None) -> Render
|
||||||
"Operational snapshot"
|
"Operational snapshot"
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
h.p(class_="text-xs text-slate-500")["Live values from the database"],
|
|
||||||
],
|
],
|
||||||
h.dl(class_="grid gap-3 md:grid-cols-2 xl:grid-cols-4")[
|
h.dl(class_="grid gap-3 md:grid-cols-2 xl:grid-cols-4")[
|
||||||
stat_card(
|
stat_card(
|
||||||
|
|
@ -156,9 +152,6 @@ def running_executions_table(
|
||||||
h.h2(class_="mt-1 text-xl font-semibold text-slate-950")[
|
h.h2(class_="mt-1 text-xl font-semibold text-slate-950")[
|
||||||
"Running executions"
|
"Running executions"
|
||||||
],
|
],
|
||||||
h.p(class_="mt-1 text-sm text-slate-600")[
|
|
||||||
"Dashboard keeps only the in-flight executions visible here. The full run history lives on the Runs page."
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
muted_action_link(href="/runs", label="Open runs"),
|
muted_action_link(href="/runs", label="Open runs"),
|
||||||
],
|
],
|
||||||
|
|
@ -232,7 +225,6 @@ def published_feeds_table(
|
||||||
return table_section(
|
return table_section(
|
||||||
eyebrow="Published feeds",
|
eyebrow="Published feeds",
|
||||||
title="Published feeds",
|
title="Published feeds",
|
||||||
subtitle="Per-source public feed paths under /feeds, with current availability and disk usage.",
|
|
||||||
headers=("Source", "Feed URL", "Status", "Last updated", "Disk usage"),
|
headers=("Source", "Feed URL", "Status", "Last updated", "Disk usage"),
|
||||||
rows=rows,
|
rows=rows,
|
||||||
actions=muted_action_link(href="/sources", label="Manage sources"),
|
actions=muted_action_link(href="/sources", label="Manage sources"),
|
||||||
|
|
|
||||||
|
|
@ -206,13 +206,11 @@ def runs_page(
|
||||||
current_path="/runs",
|
current_path="/runs",
|
||||||
eyebrow="Execution control",
|
eyebrow="Execution control",
|
||||||
title="Runs",
|
title="Runs",
|
||||||
description="Running executions first, then the schedule queue, then completed history. Logs are routed through app URLs instead of direct file serving.",
|
|
||||||
actions=muted_action_link(href="/sources", label="Back to sources"),
|
actions=muted_action_link(href="/sources", label="Back to sources"),
|
||||||
content=(
|
content=(
|
||||||
table_section(
|
table_section(
|
||||||
eyebrow="Live work",
|
eyebrow="Live work",
|
||||||
title="Running job executions",
|
title="Running job executions",
|
||||||
subtitle="Operators can inspect the live log stream, request a graceful stop, and escalate to a hard kill after the 15 second deadline if needed.",
|
|
||||||
headers=(
|
headers=(
|
||||||
"Source",
|
"Source",
|
||||||
"Execution",
|
"Execution",
|
||||||
|
|
@ -226,7 +224,6 @@ def runs_page(
|
||||||
table_section(
|
table_section(
|
||||||
eyebrow="Queue",
|
eyebrow="Queue",
|
||||||
title="Upcoming jobs",
|
title="Upcoming jobs",
|
||||||
subtitle="Scheduled work shows enable or disable state, run-now affordances, and destructive delete controls. Deleting removes the source-linked job and its execution history.",
|
|
||||||
headers=(
|
headers=(
|
||||||
"Source",
|
"Source",
|
||||||
"Next run",
|
"Next run",
|
||||||
|
|
@ -240,7 +237,6 @@ def runs_page(
|
||||||
table_section(
|
table_section(
|
||||||
eyebrow="History",
|
eyebrow="History",
|
||||||
title="Completed job executions",
|
title="Completed job executions",
|
||||||
subtitle="Recent execution history keeps the summary counters visible and links back to the plain text log view.",
|
|
||||||
headers=(
|
headers=(
|
||||||
"Source",
|
"Source",
|
||||||
"Execution",
|
"Execution",
|
||||||
|
|
@ -308,7 +304,7 @@ def execution_logs_page(
|
||||||
if log_view is None:
|
if log_view is None:
|
||||||
log_view = {
|
log_view = {
|
||||||
"title": f"Job {job_id} / execution {execution_id}",
|
"title": f"Job {job_id} / execution {execution_id}",
|
||||||
"description": "Plain text log view routed through the app.",
|
"description": "",
|
||||||
"status_label": "Unavailable",
|
"status_label": "Unavailable",
|
||||||
"status_tone": "failed",
|
"status_tone": "failed",
|
||||||
"log_text": "",
|
"log_text": "",
|
||||||
|
|
@ -331,7 +327,6 @@ def execution_logs_page(
|
||||||
current_path=f"/job/{job_id}/execution/{execution_id}/logs",
|
current_path=f"/job/{job_id}/execution/{execution_id}/logs",
|
||||||
eyebrow="Execution log",
|
eyebrow="Execution log",
|
||||||
title=_text(log_view, "title"),
|
title=_text(log_view, "title"),
|
||||||
description=_text(log_view, "description"),
|
|
||||||
actions=muted_action_link(href="/runs", label="Back to runs"),
|
actions=muted_action_link(href="/runs", label="Back to runs"),
|
||||||
content=(
|
content=(
|
||||||
section_card(
|
section_card(
|
||||||
|
|
@ -344,9 +339,6 @@ def execution_logs_page(
|
||||||
h.h2(class_="mt-2 text-xl font-semibold text-slate-950")[
|
h.h2(class_="mt-2 text-xl font-semibold text-slate-950")[
|
||||||
f"/job/{job_id}/execution/{execution_id}/logs"
|
f"/job/{job_id}/execution/{execution_id}/logs"
|
||||||
],
|
],
|
||||||
h.p(class_="mt-2 text-sm text-slate-600")[
|
|
||||||
_text(log_view, "description")
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
status_badge(
|
status_badge(
|
||||||
label=_text(log_view, "status_label"),
|
label=_text(log_view, "status_label"),
|
||||||
|
|
|
||||||
|
|
@ -51,9 +51,6 @@ def shim_page(
|
||||||
h.h1(
|
h.h1(
|
||||||
class_="mt-1 text-3xl font-semibold tracking-tight text-slate-950"
|
class_="mt-1 text-3xl font-semibold tracking-tight text-slate-950"
|
||||||
)["Loading page"],
|
)["Loading page"],
|
||||||
h.p(class_="mt-1 text-sm text-slate-600")[
|
|
||||||
"Rendering the latest server view for this route."
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,6 @@ def sources_table(
|
||||||
return table_section(
|
return table_section(
|
||||||
eyebrow="Inventory",
|
eyebrow="Inventory",
|
||||||
title="Sources",
|
title="Sources",
|
||||||
subtitle="Configured feed and Pangea sources live here as tables, with clear schedule and job state visibility instead of card-based CRUD.",
|
|
||||||
headers=("Source", "Type", "Upstream", "Schedule", "Job state", "Actions"),
|
headers=("Source", "Type", "Upstream", "Schedule", "Job state", "Actions"),
|
||||||
rows=rows,
|
rows=rows,
|
||||||
actions=header_action_link(href="/sources/create", label="Create source"),
|
actions=header_action_link(href="/sources/create", label="Create source"),
|
||||||
|
|
@ -106,7 +105,6 @@ def sources_page(
|
||||||
current_path="/sources",
|
current_path="/sources",
|
||||||
eyebrow="Source management",
|
eyebrow="Source management",
|
||||||
title="Sources",
|
title="Sources",
|
||||||
description="Configured feed and Pangea sources live here as tables, with clear schedule and job state visibility instead of card-based CRUD.",
|
|
||||||
actions=header_action_link(href="/sources/create", label="Create source"),
|
actions=header_action_link(href="/sources/create", label="Create source"),
|
||||||
content=sources_table(sources=sources),
|
content=sources_table(sources=sources),
|
||||||
)
|
)
|
||||||
|
|
@ -122,11 +120,6 @@ def source_form(
|
||||||
slug = _value(source, "slug")
|
slug = _value(source, "slug")
|
||||||
title = "Source and job setup" if mode == "create" else "Edit source"
|
title = "Source and job setup" if mode == "create" else "Edit source"
|
||||||
eyebrow = "Create" if mode == "create" else "Edit"
|
eyebrow = "Create" if mode == "create" else "Edit"
|
||||||
description = (
|
|
||||||
"Create the source and its paired job record."
|
|
||||||
if mode == "create"
|
|
||||||
else "Update the existing source and its paired job record."
|
|
||||||
)
|
|
||||||
status_label = "New source" if mode == "create" else "Existing source"
|
status_label = "New source" if mode == "create" else "Existing source"
|
||||||
submit_label = "Create source" if mode == "create" else "Save changes"
|
submit_label = "Create source" if mode == "create" else "Save changes"
|
||||||
initial_signals = "{sourceType: 'pangea'}"
|
initial_signals = "{sourceType: 'pangea'}"
|
||||||
|
|
@ -143,7 +136,6 @@ def source_form(
|
||||||
class_="text-xs font-semibold uppercase tracking-[0.22em] text-amber-600"
|
class_="text-xs font-semibold uppercase tracking-[0.22em] text-amber-600"
|
||||||
)[eyebrow],
|
)[eyebrow],
|
||||||
h.h2(class_="mt-2 text-xl font-semibold text-slate-950")[title],
|
h.h2(class_="mt-2 text-xl font-semibold text-slate-950")[title],
|
||||||
h.p(class_="mt-2 max-w-3xl text-sm text-slate-600")[description],
|
|
||||||
],
|
],
|
||||||
status_badge(label=status_label, tone="scheduled"),
|
status_badge(label=status_label, tone="scheduled"),
|
||||||
],
|
],
|
||||||
|
|
@ -215,9 +207,6 @@ def source_form(
|
||||||
h.h3(class_="mt-2 text-lg font-semibold text-slate-950")[
|
h.h3(class_="mt-2 text-lg font-semibold text-slate-950")[
|
||||||
"Feed settings"
|
"Feed settings"
|
||||||
],
|
],
|
||||||
h.p(class_="mt-2 text-sm text-slate-600")[
|
|
||||||
"Shown only when the source type is set to feed."
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
h.div(class_="grid gap-4 md:grid-cols-2")[
|
h.div(class_="grid gap-4 md:grid-cols-2")[
|
||||||
input_field(
|
input_field(
|
||||||
|
|
@ -240,9 +229,6 @@ def source_form(
|
||||||
h.h3(class_="mt-2 text-lg font-semibold text-slate-950")[
|
h.h3(class_="mt-2 text-lg font-semibold text-slate-950")[
|
||||||
"Pangea settings"
|
"Pangea settings"
|
||||||
],
|
],
|
||||||
h.p(class_="mt-2 text-sm text-slate-600")[
|
|
||||||
"Shown only when the source type is set to pangea."
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
h.div(class_="grid gap-4 lg:grid-cols-3")[
|
h.div(class_="grid gap-4 lg:grid-cols-3")[
|
||||||
input_field(
|
input_field(
|
||||||
|
|
@ -414,7 +400,6 @@ def create_source_page(*, action_path: str = "/actions/sources/create") -> Rende
|
||||||
current_path="/sources/create",
|
current_path="/sources/create",
|
||||||
eyebrow="Source creation",
|
eyebrow="Source creation",
|
||||||
title="Create source",
|
title="Create source",
|
||||||
description="Create a new source and its paired job configuration.",
|
|
||||||
actions=actions,
|
actions=actions,
|
||||||
content=source_form(mode="create", action_path=action_path),
|
content=source_form(mode="create", action_path=action_path),
|
||||||
)
|
)
|
||||||
|
|
@ -434,7 +419,6 @@ def edit_source_page(
|
||||||
current_path=f"/sources/{slug}/edit",
|
current_path=f"/sources/{slug}/edit",
|
||||||
eyebrow="Source editing",
|
eyebrow="Source editing",
|
||||||
title="Edit source",
|
title="Edit source",
|
||||||
description="Update an existing source and its paired job configuration.",
|
|
||||||
actions=actions,
|
actions=actions,
|
||||||
content=source_form(mode="edit", action_path=action_path, source=source),
|
content=source_form(mode="edit", action_path=action_path, source=source),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import io
|
import io
|
||||||
|
import logging
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from repub.entrypoint import FeedNameFilter, entrypoint, logger, parse_args
|
from repub.entrypoint import FeedNameFilter, entrypoint, logger, parse_args
|
||||||
|
|
||||||
|
|
@ -32,14 +34,17 @@ def test_parse_args_uses_republisher_host_and_port_env_vars(monkeypatch) -> None
|
||||||
def test_entrypoint_rejects_invalid_republisher_port(monkeypatch) -> None:
|
def test_entrypoint_rejects_invalid_republisher_port(monkeypatch) -> None:
|
||||||
monkeypatch.setenv("REPUBLISHER_PORT", "not-a-number")
|
monkeypatch.setenv("REPUBLISHER_PORT", "not-a-number")
|
||||||
stream = io.StringIO()
|
stream = io.StringIO()
|
||||||
original_streams = [handler.stream for handler in logger.handlers]
|
handlers = [
|
||||||
for handler in logger.handlers:
|
cast(logging.StreamHandler[io.StringIO], handler) for handler in logger.handlers
|
||||||
|
]
|
||||||
|
original_streams = [handler.stream for handler in handlers]
|
||||||
|
for handler in handlers:
|
||||||
handler.stream = stream
|
handler.stream = stream
|
||||||
|
|
||||||
try:
|
try:
|
||||||
exit_code = entrypoint(["serve"])
|
exit_code = entrypoint(["serve"])
|
||||||
finally:
|
finally:
|
||||||
for handler, original_stream in zip(logger.handlers, original_streams):
|
for handler, original_stream in zip(handlers, original_streams):
|
||||||
handler.stream = original_stream
|
handler.stream = original_stream
|
||||||
|
|
||||||
assert exit_code == 2
|
assert exit_code == 2
|
||||||
|
|
|
||||||
|
|
@ -388,7 +388,6 @@ def test_render_sources_shows_table_and_create_link() -> None:
|
||||||
async def run() -> None:
|
async def run() -> None:
|
||||||
body = str(await render_sources())
|
body = str(await render_sources())
|
||||||
|
|
||||||
assert "Configured feed and Pangea sources live here as tables" in body
|
|
||||||
assert ">Sources<" in body
|
assert ">Sources<" in body
|
||||||
assert 'href="/sources/create"' in body
|
assert 'href="/sources/create"' in body
|
||||||
assert "guardian-feed" not in body
|
assert "guardian-feed" not in body
|
||||||
|
|
@ -401,7 +400,7 @@ def test_render_create_source_shows_dedicated_form_page() -> None:
|
||||||
async def run() -> None:
|
async def run() -> None:
|
||||||
body = str(await render_create_source())
|
body = str(await render_create_source())
|
||||||
|
|
||||||
assert "Create a new source and its paired job configuration." in body
|
assert ">Create source<" in body
|
||||||
assert "Source and job setup" in body
|
assert "Source and job setup" in body
|
||||||
assert "data-signals__ifmissing" in body
|
assert "data-signals__ifmissing" in body
|
||||||
assert "/actions/sources/create" in body
|
assert "/actions/sources/create" in body
|
||||||
|
|
@ -890,7 +889,6 @@ def test_render_execution_logs_uses_app_route(monkeypatch, tmp_path: Path) -> No
|
||||||
|
|
||||||
assert f"Job {job.id} / execution {execution.get_id()}" in body
|
assert f"Job {job.id} / execution {execution.get_id()}" in body
|
||||||
assert f"/job/{job.id}/execution/{execution.get_id()}/logs" in body
|
assert f"/job/{job.id}/execution/{execution.get_id()}/logs" in body
|
||||||
assert "Route: /job/" in body
|
|
||||||
assert "waiting for more log lines" in body
|
assert "waiting for more log lines" in body
|
||||||
|
|
||||||
asyncio.run(run())
|
asyncio.run(run())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue