Articles: Paginierung, Timeline-Buckets, Sources-Summary-Endpunkt
Backend:
- GET /{id}/articles paginiert jetzt per limit/offset (Default 500,
Max 1000) und unterstuetzt optionalen search-Parameter (LIKE ueber
headline/source/content). Response-Shape: {total, articles}.
- Neuer Endpunkt GET /{id}/articles/sources-summary liefert pro Quelle
{source, article_count, languages} sowie language_counts gesamt —
serverseitige Aggregation, unabhaengig von Artikel-Paginierung.
- Neuer Endpunkt GET /{id}/articles/timeline-buckets?granularity=hour|day|week|month
aggregiert Artikel + Snapshot-Counts pro Zeitbucket (fuer spaetere
Timeline-Zaehler ueber die volle Historie).
- database.py: Index idx_articles_incident_collected auf
(incident_id, collected_at DESC) fuer schnelleres ORDER BY + Pagination.
Frontend:
- api.js: getArticles({limit, offset, search}),
getArticlesSourcesSummary(), getArticlesTimelineBuckets().
- app.js: loadIncidentDetail laedt erste Seite (500 Artikel), startet
_loadSourcesSummary parallel und zieht restliche Artikel
batchweise (500er Bloecke) im Hintergrund nach, bis _currentArticlesTotal
erreicht ist. rerenderTimeline nach jedem Batch.
- components.js: renderSourceOverviewFromSummary(data) rendert aus
Aggregat-Daten (ersetzt clientseitige Zaehlung ueber geladene Artikel).
Hintergrund: /articles lieferte bei der Iran-Lage 22 MB (17.286 Artikel
mit SELECT *). Die Erstantwort sinkt auf ~650 KB (500 Artikel), weitere
werden progressiv im Hintergrund nachgeladen. Quellenuebersicht zeigt
dank Aggregat-Endpunkt sofort alle Quellen + Sprachen komplett.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -99,8 +99,20 @@ const API = {
|
||||
return this._request('DELETE', `/incidents/${id}`);
|
||||
},
|
||||
|
||||
getArticles(incidentId) {
|
||||
return this._request('GET', `/incidents/${incidentId}/articles`);
|
||||
getArticles(incidentId, { limit = 500, offset = 0, search = null } = {}) {
|
||||
const params = new URLSearchParams();
|
||||
params.set('limit', String(limit));
|
||||
params.set('offset', String(offset));
|
||||
if (search) params.set('search', search);
|
||||
return this._request('GET', `/incidents/${incidentId}/articles?${params.toString()}`);
|
||||
},
|
||||
|
||||
getArticlesSourcesSummary(incidentId) {
|
||||
return this._request('GET', `/incidents/${incidentId}/articles/sources-summary`);
|
||||
},
|
||||
|
||||
getArticlesTimelineBuckets(incidentId, granularity = 'day') {
|
||||
return this._request('GET', `/incidents/${incidentId}/articles/timeline-buckets?granularity=${encodeURIComponent(granularity)}`);
|
||||
},
|
||||
|
||||
getFactChecks(incidentId) {
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren