app module

get_connection()[source]

Open a SQLite connection using the database path from config.toml.

Reads db_file from the [settings] section of config.toml. Falls back to 'headlines.db' if the config cannot be loaded.

Returns:

A two-element tuple containing:

  • An open database connection with row_factory set to sqlite3.Row for dict-style column access.

  • The resolved database file path as a string.

Return type:

tuple[sqlite3.Connection, str]

index()[source]

Render the main headlines page.

Queries all headlines joined with their parent feed, groups them by feed title in Python, and passes the grouped structure to the index.html template.

Returns:

Rendered HTML response for the / route.

Return type:

str

feeds()[source]

Render the feeds page listing all RSS sources with headline counts.

Queries all feeds with a count of their associated headlines using a LEFT JOIN so feeds with zero articles still appear.

Returns:

Rendered HTML response for the /feeds route.

Return type:

str

runs()[source]

Render the run history page showing the last 50 aggregator runs.

Returns:

Rendered HTML response for the /runs route.

Return type:

str

trigger_run()[source]

Fire the aggregator in a background thread and return immediately.

Uses _run_lock to ensure only one run can be in progress at a time. The frontend polls /status to detect completion. This route is used by the Jinja2 frontend — the API equivalent lives at /api/v1/run.

Returns:

A JSON response with HTTP 202 in both cases:

  • {'status': 'started'} if the run was successfully launched.

  • {'status': 'already_running'} if a run is already in progress.

Return type:

tuple[flask.Response, int]

status()[source]

Return the current aggregator run state as JSON.

Polled every 3 seconds by the frontend JavaScript to update the status indicator in the navigation bar. The API equivalent lives at /api/v1/status.

Returns:

A JSON response with HTTP 200:

{'running': true}  # aggregator is currently running
{'running': false} # aggregator is idle

Return type:

tuple[flask.Response, int]