Add settings and live sidebar counts
This commit is contained in:
parent
2a99edeec3
commit
a809bde16c
16 changed files with 696 additions and 51 deletions
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import asyncio
|
||||
import os
|
||||
import re
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any, cast
|
||||
|
|
@ -17,6 +18,8 @@ from repub.model import (
|
|||
SourceFeed,
|
||||
SourcePangea,
|
||||
create_source,
|
||||
load_max_concurrent_jobs,
|
||||
save_setting,
|
||||
)
|
||||
from repub.pages.runs import runs_page
|
||||
from repub.web import (
|
||||
|
|
@ -27,6 +30,7 @@ from repub.web import (
|
|||
render_edit_source,
|
||||
render_execution_logs,
|
||||
render_runs,
|
||||
render_settings,
|
||||
render_sources,
|
||||
)
|
||||
|
||||
|
|
@ -109,6 +113,7 @@ def test_root_get_serves_datastar_shim() -> None:
|
|||
assert '<main id="morph"' in body
|
||||
assert 'href="/sources"' in body
|
||||
assert 'href="/runs"' in body
|
||||
assert 'href="/settings"' in body
|
||||
assert "Connecting" in body
|
||||
|
||||
asyncio.run(run())
|
||||
|
|
@ -430,6 +435,94 @@ def test_render_sources_shows_table_and_create_link() -> None:
|
|||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_render_sources_shows_live_sidebar_badges(monkeypatch, tmp_path: Path) -> None:
|
||||
db_path = tmp_path / "sources-sidebar.db"
|
||||
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))
|
||||
app = create_app()
|
||||
create_source(
|
||||
name="First source",
|
||||
slug="first-source",
|
||||
source_type="feed",
|
||||
notes="",
|
||||
spider_arguments="",
|
||||
enabled=True,
|
||||
cron_minute="0",
|
||||
cron_hour="*",
|
||||
cron_day_of_month="*",
|
||||
cron_day_of_week="*",
|
||||
cron_month="*",
|
||||
feed_url="https://example.com/first.xml",
|
||||
)
|
||||
create_source(
|
||||
name="Second source",
|
||||
slug="second-source",
|
||||
source_type="feed",
|
||||
notes="",
|
||||
spider_arguments="",
|
||||
enabled=True,
|
||||
cron_minute="0",
|
||||
cron_hour="*",
|
||||
cron_day_of_month="*",
|
||||
cron_day_of_week="*",
|
||||
cron_month="*",
|
||||
feed_url="https://example.com/second.xml",
|
||||
)
|
||||
|
||||
async def run() -> None:
|
||||
body = str(await render_sources(app))
|
||||
|
||||
assert re.search(
|
||||
r'href="/sources"[^>]*>.*?<span>Sources</span>\s*<span[^>]*>2</span>',
|
||||
body,
|
||||
re.S,
|
||||
)
|
||||
assert re.search(
|
||||
r'href="/runs"[^>]*>.*?<span>Runs</span>\s*<span[^>]*>0</span>',
|
||||
body,
|
||||
re.S,
|
||||
)
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_render_dashboard_shows_live_sidebar_badges(
|
||||
monkeypatch, tmp_path: Path
|
||||
) -> None:
|
||||
db_path = tmp_path / "dashboard-sidebar.db"
|
||||
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))
|
||||
app = create_app()
|
||||
create_source(
|
||||
name="Dashboard source",
|
||||
slug="dashboard-source",
|
||||
source_type="feed",
|
||||
notes="",
|
||||
spider_arguments="",
|
||||
enabled=True,
|
||||
cron_minute="0",
|
||||
cron_hour="*",
|
||||
cron_day_of_month="*",
|
||||
cron_day_of_week="*",
|
||||
cron_month="*",
|
||||
feed_url="https://example.com/dashboard.xml",
|
||||
)
|
||||
|
||||
async def run() -> None:
|
||||
body = str(await render_dashboard(app))
|
||||
|
||||
assert re.search(
|
||||
r'href="/sources"[^>]*>.*?<span>Sources</span>\s*<span[^>]*>1</span>',
|
||||
body,
|
||||
re.S,
|
||||
)
|
||||
assert re.search(
|
||||
r'href="/runs"[^>]*>.*?<span>Runs</span>\s*<span[^>]*>0</span>',
|
||||
body,
|
||||
re.S,
|
||||
)
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_render_sources_shows_delete_action_for_each_source(
|
||||
monkeypatch, tmp_path: Path
|
||||
) -> None:
|
||||
|
|
@ -476,6 +569,8 @@ def test_render_create_source_shows_dedicated_form_page() -> None:
|
|||
assert "includeAuthors" in body
|
||||
assert "excludeMedia" in body
|
||||
assert "includeContent" in body
|
||||
assert "convertImages" in body
|
||||
assert "convertVideo" in body
|
||||
assert "TEXT_ONLY" in body
|
||||
assert "breakingnews" in body
|
||||
assert "Pangea domain" in body
|
||||
|
|
@ -512,6 +607,8 @@ def test_render_edit_source_shows_existing_values(monkeypatch, tmp_path: Path) -
|
|||
notes="Regional health alerts.",
|
||||
spider_arguments="language=en\ndownload_media=true",
|
||||
enabled=True,
|
||||
convert_images=False,
|
||||
convert_video=False,
|
||||
cron_minute="0",
|
||||
cron_hour="*/6",
|
||||
cron_day_of_month="*",
|
||||
|
|
@ -546,6 +643,28 @@ def test_render_edit_source_shows_existing_values(monkeypatch, tmp_path: Path) -
|
|||
assert "example.org" in body
|
||||
assert "Health" in body
|
||||
assert "language=en\ndownload_media=true" in body
|
||||
assert "convertImages: false" in body
|
||||
assert "convertVideo: false" in body
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_render_settings_shows_current_max_concurrent_jobs(
|
||||
monkeypatch, tmp_path: Path
|
||||
) -> None:
|
||||
db_path = tmp_path / "settings-page.db"
|
||||
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))
|
||||
create_app()
|
||||
save_setting("max_concurrent_jobs", 3)
|
||||
|
||||
async def run() -> None:
|
||||
app = create_app()
|
||||
body = str(await render_settings(app))
|
||||
|
||||
assert ">Settings<" in body
|
||||
assert "/actions/settings" in body
|
||||
assert 'value="3"' in body
|
||||
assert "Max concurrent jobs" in body
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
|
@ -602,6 +721,8 @@ def test_create_source_action_creates_pangea_source_and_job_in_database(
|
|||
assert pangea.content_type == "breakingnews"
|
||||
assert pangea.include_content is True
|
||||
assert job.enabled is True
|
||||
assert job.convert_images is True
|
||||
assert job.convert_video is True
|
||||
assert job.spider_arguments == "language=en\ndownload_media=true"
|
||||
assert job.cron_hour == "*/6"
|
||||
assert "kenya-health" in rendered_sources
|
||||
|
|
@ -713,6 +834,8 @@ def test_edit_source_action_updates_existing_source_and_job_in_database(
|
|||
"cronDayOfWeek": "*",
|
||||
"cronMonth": "*",
|
||||
"jobEnabled": False,
|
||||
"convertImages": False,
|
||||
"convertVideo": False,
|
||||
"onlyNewest": False,
|
||||
"includeAuthors": False,
|
||||
"excludeMedia": True,
|
||||
|
|
@ -737,6 +860,8 @@ def test_edit_source_action_updates_existing_source_and_job_in_database(
|
|||
assert pangea.include_authors is False
|
||||
assert pangea.exclude_media is True
|
||||
assert job.enabled is False
|
||||
assert job.convert_images is False
|
||||
assert job.convert_video is False
|
||||
assert job.spider_arguments == "language=sw\ninclude_audio=false"
|
||||
assert job.cron_hour == "2"
|
||||
assert "Kenya health desk nightly" in rendered_sources
|
||||
|
|
@ -863,6 +988,55 @@ def test_create_source_action_validates_duplicate_slug_and_pangea_type(
|
|||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_settings_action_updates_max_concurrent_jobs(
|
||||
monkeypatch, tmp_path: Path
|
||||
) -> None:
|
||||
db_path = tmp_path / "settings-action.db"
|
||||
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))
|
||||
|
||||
async def run() -> None:
|
||||
app = create_app()
|
||||
client = app.test_client()
|
||||
|
||||
response = await client.post(
|
||||
"/actions/settings",
|
||||
headers={"Datastar-Request": "true"},
|
||||
json={"maxConcurrentJobs": "3"},
|
||||
)
|
||||
body = await response.get_data(as_text=True)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "window.location = '/settings'" in body
|
||||
assert load_max_concurrent_jobs() == 3
|
||||
assert 'value="3"' in str(await render_settings(app))
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_settings_action_rejects_non_positive_max_concurrent_jobs(
|
||||
monkeypatch, tmp_path: Path
|
||||
) -> None:
|
||||
db_path = tmp_path / "settings-invalid.db"
|
||||
monkeypatch.setenv("REPUBLISHER_DB_PATH", str(db_path))
|
||||
|
||||
async def run() -> None:
|
||||
app = create_app()
|
||||
client = app.test_client()
|
||||
|
||||
response = await client.post(
|
||||
"/actions/settings",
|
||||
headers={"Datastar-Request": "true"},
|
||||
json={"maxConcurrentJobs": "0"},
|
||||
)
|
||||
body = await response.get_data(as_text=True)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Max concurrent jobs must be at least 1." in body
|
||||
assert load_max_concurrent_jobs() == 1
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_render_runs_shows_running_upcoming_and_completed_tables(
|
||||
monkeypatch, tmp_path: Path
|
||||
) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue