{% extends "base.html" %} {% block title %}Baza Wiedzy ZOPK - Panel Admina{% endblock %} {% block extra_css %} {% endblock %} {% block content %}
Ładowanie statystyk...

📋 Przegląd danych

🏆 Top 10 encji według wzmianek

Ładowanie encji...

⚡ Akcje

✅ Status weryfikacji

Ładowanie statystyk weryfikacji...
{% endblock %} {% block extra_js %} // ===== Toast & Modal System ===== function showToast(type, title, message, duration = 5000) { const container = document.getElementById('toastContainer'); const icons = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' }; const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.innerHTML = `
${icons[type] || 'ℹ️'}
${title}
${message}
`; container.appendChild(toast); if (duration > 0) { setTimeout(() => { toast.style.animation = 'slideOut 0.3s ease-out forwards'; setTimeout(() => toast.remove(), 300); }, duration); } } function showConfirm(title, message, icon = '❓') { return new Promise((resolve) => { const overlay = document.createElement('div'); overlay.className = 'modal-overlay'; overlay.innerHTML = ` `; document.body.appendChild(overlay); overlay.addEventListener('click', (e) => { if (e.target === overlay || e.target.dataset.action === 'cancel') { overlay.remove(); resolve(false); } else if (e.target.dataset.action === 'confirm') { overlay.remove(); resolve(true); } }); }); } // ===== Page Load ===== document.addEventListener('DOMContentLoaded', function() { loadStats(); }); async function loadStats() { try { const response = await fetch('/admin/zopk/knowledge/stats'); const data = await response.json(); if (data.success) { renderStats(data); renderTopEntities(data.top_entities || []); updatePipelineStatus(data); } else { document.getElementById('statsGrid').innerHTML = '
Błąd ładowania: ' + data.error + '
'; } } catch (error) { console.error('Error loading stats:', error); document.getElementById('statsGrid').innerHTML = '
Błąd połączenia
'; } } function renderStats(data) { const articles = data.articles || {}; const kb = data.knowledge_base || {}; const statsHtml = `
📄
${kb.total_chunks || 0}
Chunks
${kb.chunks_with_embeddings || 0} z embeddingami
📌
${kb.total_facts || 0}
Fakty
Wyekstraktowane informacje
🏢
${kb.total_entities || 0}
Encje
Firmy, osoby, miejsca
🔗
${kb.total_relations || 0}
Relacje
Powiązania między encjami
📰
${articles.extracted || 0}/${articles.scraped || 0}
Artykuły przetworzone
${articles.pending_extract || 0} oczekuje
🧲
${kb.chunks_with_embeddings || 0}/${kb.total_chunks || 0}
Embeddingi
${kb.chunks_without_embeddings || 0} do wygenerowania
`; document.getElementById('statsGrid').innerHTML = statsHtml; } function renderTopEntities(entities) { if (!entities.length) { document.getElementById('topEntities').innerHTML = '
Brak encji w bazie
'; return; } const html = entities.map(e => `
${e.type} ${e.name} ${e.mentions}×
`).join(''); document.getElementById('topEntities').innerHTML = html; } function updatePipelineStatus(data) { const articles = data.articles || {}; const kb = data.knowledge_base || {}; let status = 'success'; let text = 'Pipeline OK'; if (articles.pending_extract > 0) { status = 'running'; text = `${articles.pending_extract} artykułów czeka na ekstrakcję`; } else if (kb.chunks_without_embeddings > 0) { status = 'running'; text = `${kb.chunks_without_embeddings} chunków bez embeddingów`; } document.getElementById('pipelineStatus').className = 'pipeline-status ' + status; document.getElementById('pipelineStatus').innerHTML = ` ${status === 'success' ? '✅' : '⏳'} ${text} `; } async function runExtraction() { const confirmed = await showConfirm('Ekstrakcja wiedzy', 'Uruchomić ekstrakcję wiedzy z artykułów?', '🔄'); if (!confirmed) return; try { const response = await fetch('/admin/zopk/knowledge/extract', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token() }}' }, body: JSON.stringify({ limit: 50 }) }); const data = await response.json(); if (data.success !== false) { showToast('success', 'Ekstrakcja zakończona', data.message || 'Przetworzono artykuły'); } else { showToast('error', 'Błąd ekstrakcji', data.error || 'Nieznany błąd'); } loadStats(); } catch (error) { showToast('error', 'Błąd połączenia', error.message); } } async function generateEmbeddings() { const confirmed = await showConfirm('Generowanie embeddingów', 'Wygenerować embeddingi dla chunków bez wektorów?', '🧲'); if (!confirmed) return; try { const response = await fetch('/admin/zopk/knowledge/embeddings', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token() }}' }, body: JSON.stringify({ limit: 100 }) }); const data = await response.json(); if (data.success !== false) { showToast('success', 'Embeddingi wygenerowane', data.message || 'Operacja zakończona'); } else { showToast('error', 'Błąd generowania', data.error || 'Nieznany błąd'); } loadStats(); } catch (error) { showToast('error', 'Błąd połączenia', error.message); } } async function autoVerifyEntities() { const confirmed = await showConfirm('Auto-weryfikacja encji', 'Zweryfikować automatycznie encje z minimum 5 wzmiankami?', '✅'); if (!confirmed) return; try { const response = await fetch('/api/zopk/knowledge/auto-verify/entities', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token() }}' }, body: JSON.stringify({ min_mentions: 5, limit: 100 }) }); const data = await response.json(); if (data.success) { showToast('success', 'Encje zweryfikowane', `Zweryfikowano ${data.verified_count} encji z ≥5 wzmiankami`); loadStats(); loadVerificationStats(); } else { showToast('error', 'Błąd weryfikacji', data.error); } } catch (error) { showToast('error', 'Błąd połączenia', error.message); } } async function autoVerifyFacts() { const confirmed = await showConfirm('Auto-weryfikacja faktów', 'Zweryfikować automatycznie fakty z pewnością ≥70%?', '📌'); if (!confirmed) return; try { const response = await fetch('/api/zopk/knowledge/auto-verify/facts', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token() }}' }, body: JSON.stringify({ min_importance: 0.7, limit: 200 }) }); const data = await response.json(); if (data.success) { showToast('success', 'Fakty zweryfikowane', `Zweryfikowano ${data.verified_count} faktów z pewnością ≥70%`); loadStats(); loadVerificationStats(); } else { showToast('error', 'Błąd weryfikacji', data.error); } } catch (error) { showToast('error', 'Błąd połączenia', error.message); } } // Track undone verifications for summary let undoneCount = 0; async function learnFromVerified() { const confirmed = await showConfirm( 'Uczenie z weryfikacji', 'Szukać faktów podobnych do już zweryfikowanych?

System znajdzie fakty z ≥80% podobieństwem tekstowym do zweryfikowanych wzorców i automatycznie je zweryfikuje.', '🧠' ); if (!confirmed) return; try { const response = await fetch('/api/zopk/knowledge/auto-verify/similar', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token() }}' }, body: JSON.stringify({ min_similarity: 0.8, limit: 100 }) }); const data = await response.json(); if (data.success) { if (data.verified_count > 0) { // Show modal with results showSuggestionsModal(data); } else { showToast('warning', 'Brak nowych faktów', 'Nie znaleziono podobnych faktów do weryfikacji. Zweryfikuj więcej faktów ręcznie aby stworzyć wzorce.', 8000); } loadStats(); loadVerificationStats(); } else { showToast('error', 'Błąd uczenia', data.error); } } catch (error) { showToast('error', 'Błąd połączenia', error.message); } } async function loadVerificationStats() { try { const response = await fetch('/api/zopk/knowledge/dashboard-stats'); const data = await response.json(); if (data.success) { renderVerificationStats(data); } } catch (error) { console.error('Error loading verification stats:', error); } } function renderVerificationStats(data) { // API returns: data.entities, data.facts, data.chunks (each with .total, .verified) const entities = data.entities || {}; const facts = data.facts || {}; const chunks = data.chunks || {}; // Relations not yet implemented in API const relations = {total: 0, verified: 0}; const html = `
🏢
${entities.verified || 0}/${entities.total || 0}
Encje zweryfikowane
${(entities.total || 0) - (entities.verified || 0)} oczekuje
📌
${facts.verified || 0}/${facts.total || 0}
Fakty zweryfikowane
${(facts.total || 0) - (facts.verified || 0)} oczekuje
📄
${chunks.verified || 0}/${chunks.total || 0}
Chunks zweryfikowane
${(chunks.total || 0) - (chunks.verified || 0)} oczekuje
🔗
${relations.verified || 0}/${relations.total || 0}
Relacje zweryfikowane
${(relations.total || 0) - (relations.verified || 0)} oczekuje
`; document.getElementById('verificationStats').innerHTML = html; } // Load verification stats on page load document.addEventListener('DOMContentLoaded', function() { loadVerificationStats(); }); // ===== Suggestions Modal Functions ===== function showSuggestionsModal(data) { undoneCount = 0; document.getElementById('suggestionsTotal').textContent = data.verified_count; document.getElementById('suggestionsPatterns').textContent = data.learned_from; document.getElementById('suggestionsUndone').textContent = '0'; const listHtml = data.verified_facts.map(fact => `
${fact.fact_type || 'fakt'} ${fact.similarity}% podobieństwa
${escapeHtml(fact.fact_text)}
Wzorzec: ${escapeHtml(fact.pattern_text?.substring(0, 150) || '')}${fact.pattern_text?.length > 150 ? '...' : ''}
`).join(''); document.getElementById('suggestionsList').innerHTML = listHtml || '

Wszystkie fakty zostały zweryfikowane

'; document.getElementById('suggestionsModal').style.display = 'flex'; showToast('success', 'Uczenie zakończone!', `Zweryfikowano ${data.verified_count} faktów. Przejrzyj wyniki i cofnij błędne.`, 5000); } function closeSuggestionsModal() { document.getElementById('suggestionsModal').style.display = 'none'; loadStats(); loadVerificationStats(); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text || ''; return div.innerHTML; } async function undoVerification(factId) { try { const response = await fetch(`/api/zopk/knowledge/facts/${factId}/verify`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token() }}' }, body: JSON.stringify({ is_verified: false }) }); const data = await response.json(); if (data.success) { const card = document.getElementById(`suggestion-${factId}`); if (card) { card.classList.add('rejected'); card.querySelector('.suggestion-actions').innerHTML = '✓ Weryfikacja cofnięta'; } undoneCount++; document.getElementById('suggestionsUndone').textContent = undoneCount; showToast('info', 'Cofnięto weryfikację', 'Fakt został oznaczony jako niezweryfikowany'); } else { showToast('error', 'Błąd', data.error); } } catch (error) { showToast('error', 'Błąd połączenia', error.message); } } // Close modal on overlay click document.getElementById('suggestionsModal')?.addEventListener('click', function(e) { if (e.target === this) { closeSuggestionsModal(); } }); {% endblock %}