Geoparsing als Hintergrund-Task mit Fortschrittsanzeige

- Endpunkt startet async Background-Task statt synchron zu warten
- Batch-Verarbeitung (50 Artikel pro Batch)
- Neuer Status-Endpunkt GET /incidents/{id}/geoparse-status
- Frontend pollt alle 3s und zeigt Fortschritt im Button (z.B. "150/427 Artikel...")
- Kein Timeout-Problem mehr bei grossen Lagen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-03-04 22:39:53 +01:00
Ursprung 6112cd5882
Commit 2f6dd97100
3 geänderte Dateien mit 135 neuen und 34 gelöschten Zeilen

Datei anzeigen

@@ -1429,24 +1429,57 @@ const App = {
}
},
_geoparsePolling: null,
async triggerGeoparse() {
if (!this.currentIncidentId) return;
const btn = document.getElementById('geoparse-btn');
if (btn) { btn.disabled = true; btn.textContent = 'Erkennung...'; }
if (btn) { btn.disabled = true; btn.textContent = 'Wird gestartet...'; }
try {
const result = await API.triggerGeoparse(this.currentIncidentId);
UI.showToast(result.message, result.new_locations > 0 ? 'success' : 'info');
if (result.new_locations > 0) {
const locations = await API.getLocations(this.currentIncidentId).catch(() => []);
UI.renderMap(locations);
if (result.status === 'done') {
UI.showToast(result.message, 'info');
if (btn) { btn.disabled = false; btn.textContent = 'Orte erkennen'; }
return;
}
UI.showToast(result.message, 'info');
this._pollGeoparse(this.currentIncidentId);
} catch (err) {
UI.showToast('Geoparsing fehlgeschlagen: ' + err.message, 'error');
} finally {
if (btn) { btn.disabled = false; btn.textContent = 'Orte erkennen'; }
}
},
_pollGeoparse(incidentId) {
if (this._geoparsePolling) clearInterval(this._geoparsePolling);
const btn = document.getElementById('geoparse-btn');
this._geoparsePolling = setInterval(async () => {
try {
const st = await API.getGeoparseStatus(incidentId);
if (st.status === 'running') {
if (btn) btn.textContent = `${st.processed}/${st.total} Artikel...`;
} else {
clearInterval(this._geoparsePolling);
this._geoparsePolling = null;
if (btn) { btn.disabled = false; btn.textContent = 'Orte erkennen'; }
if (st.status === 'done' && st.locations > 0) {
UI.showToast(`${st.locations} Orte aus ${st.processed} Artikeln erkannt`, 'success');
const locations = await API.getLocations(incidentId).catch(() => []);
UI.renderMap(locations);
} else if (st.status === 'done') {
UI.showToast('Keine neuen Orte gefunden', 'info');
} else if (st.status === 'error') {
UI.showToast('Geoparsing fehlgeschlagen: ' + (st.error || ''), 'error');
}
}
} catch {
clearInterval(this._geoparsePolling);
this._geoparsePolling = null;
if (btn) { btn.disabled = false; btn.textContent = 'Orte erkennen'; }
}
}, 3000);
},
_formatInterval(minutes) {
if (minutes >= 10080 && minutes % 10080 === 0) {
const w = minutes / 10080;