Skip to content

Commit 3328e32

Browse files
committed
feat(upload): first working version
1 parent 3e31253 commit 3328e32

File tree

2 files changed

+121
-13
lines changed

2 files changed

+121
-13
lines changed

main.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ def analyses_status():
4444
return JSONResponse({"error": str(e)}, status_code=500)
4545

4646

47+
@app.post("/analyses/{analysis_id}/delete")
48+
def delete_analysis_endpoint(analysis_id: int):
49+
try:
50+
delete_analysis(DATABASE, analysis_id)
51+
return JSONResponse({"deleted": True})
52+
except Exception as e:
53+
return JSONResponse({"deleted": False, "error": str(e)}, status_code=500)
54+
55+
4756
@app.post("/analyze")
4857
def analyze(background_tasks: BackgroundTasks):
4958
local_path = CFG.get("local_path")

templates/index.html

Lines changed: 112 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
.controls-area { display:flex; gap:.5rem; align-items:center; }
2323
.empty-state { height:62vh; display:flex; align-items:center; justify-content:center; color:#6c757d; }
2424
.btn-sm-icon { padding:.25rem .5rem; font-size:.85rem; }
25+
/* Progress area styles */
26+
#indexProgress { margin-bottom: 0.75rem; min-height: 2rem; }
27+
.progress-info { font-size: .95rem; color: #333; display:flex; align-items:center; gap: 0.5rem; }
2528
</style>
2629
</head>
2730
<body>
@@ -54,7 +57,7 @@ <h5 class="card-title">Analyses</h5>
5457
{% if analyses %}
5558
<ul class="list-group list-group-flush">
5659
{% for a in analyses %}
57-
<li class="list-group-item d-flex justify-content-between align-items-start">
60+
<li class="list-group-item d-flex justify-content-between align-items-start analysis-item" data-analysis-id="{{ a.id }}">
5861
<div class="ms-2 me-auto">
5962
<div class="fw-bold">{{ a.name }} — ID: {{ a.id }}</div>
6063
<div class="small text-muted">{{ a.path }} — <em>{{ a.created_at }}</em></div>
@@ -97,6 +100,11 @@ <h5 class="card-title mb-0">Chat</h5>
97100
</div>
98101
</div>
99102

103+
<!-- Indexing verification / progress area (English) -->
104+
<div id="indexProgress" class="mb-2" aria-live="polite">
105+
<span class="text-muted">Indexing status not available.</span>
106+
</div>
107+
100108
<div id="chatWindow" class="chat-window mb-3" aria-live="polite"></div>
101109

102110
<div class="mt-auto">
@@ -370,18 +378,96 @@ <h5 class="card-title mb-0">Chat</h5>
370378
renderChat();
371379
}
372380

381+
// --- Analysis status polling and UI update (English text) ---
382+
const POLL_INTERVAL = 5000;
383+
384+
async function fetchAnalysesStatus(){
385+
try{
386+
const resp = await fetch("/analyses/status");
387+
if(!resp.ok) return null;
388+
return await resp.json();
389+
} catch(e){
390+
console.warn("Failed to fetch analyses status:", e);
391+
return null;
392+
}
393+
}
394+
395+
function updateAnalysesListUI(analyses){
396+
document.querySelectorAll(".analysis-item").forEach(li => {
397+
const aid = parseInt(li.getAttribute("data-analysis-id"));
398+
const a = (analyses || []).find(x => x.id === aid);
399+
if(a){
400+
// update badge and counts in the left list (if present)
401+
const statusBadge = li.querySelector(".badge");
402+
if(statusBadge){
403+
statusBadge.textContent = a.status;
404+
}
405+
const fileCountSpan = li.querySelector(".text-muted");
406+
// we won't try to be too invasive; ensure the left panel reflects counts by reloading page for complex updates
407+
}
408+
});
409+
}
410+
411+
function renderProgressForSelectedAnalysis(analyses){
412+
const aidInput = document.getElementById("analysis_id");
413+
const out = document.getElementById("indexProgress");
414+
if(!aidInput || !out) return;
415+
const aid = parseInt(aidInput.value || 0) || null;
416+
if(!aid){
417+
out.innerHTML = '<span class="text-muted">Select an Analysis ID to view progress.</span>';
418+
return;
419+
}
420+
const a = (analyses || []).find(x => x.id === aid);
421+
if(!a){
422+
out.innerHTML = '<span class="text-muted">Analysis not found.</span>';
423+
return;
424+
}
425+
426+
if(a.status === "running"){
427+
const fc = a.file_count || 0;
428+
const ec = a.embedding_count || 0;
429+
out.innerHTML = `<div class="progress-info"><strong>Indexing in progress</strong> — ${fc} files processed <small class="text-muted">(${ec} embeddings)</small><div class="spinner-small ms-2" aria-hidden="true"></div></div>`;
430+
} else if(a.status === "pending"){
431+
out.innerHTML = `<span class="text-muted">Analysis queued (status: ${a.status}).</span>`;
432+
} else if(a.status === "completed"){
433+
out.innerHTML = `<span class="text-success">Indexing completed — ${a.file_count || 0} files processed, ${a.embedding_count || 0} embeddings.</span>`;
434+
} else if(a.status === "failed"){
435+
out.innerHTML = `<span class="text-danger">Indexing failed.</span>`;
436+
} else {
437+
out.innerHTML = `<span class="text-muted">Status: ${a.status}${a.file_count || 0} files processed.</span>`;
438+
}
439+
}
440+
441+
let _pollHandle = null;
442+
async function startPolling(){
443+
const analyses = await fetchAnalysesStatus();
444+
if(analyses){
445+
updateAnalysesListUI(analyses);
446+
renderProgressForSelectedAnalysis(analyses);
447+
}
448+
if(_pollHandle) clearInterval(_pollHandle);
449+
_pollHandle = setInterval(async () => {
450+
const analyses = await fetchAnalysesStatus();
451+
if(analyses){
452+
updateAnalysesListUI(analyses);
453+
renderProgressForSelectedAnalysis(analyses);
454+
}
455+
}, POLL_INTERVAL);
456+
}
457+
458+
// --- Search only: show results in chat window (transient) ---
373459
async function searchOnly(){
374460
const text = document.getElementById("chatInput").value.trim();
375461
if(!text) return;
376462
const aid = parseInt(document.getElementById("analysis_id").value || 0) || 0;
377463
const top_k = parseInt(document.getElementById("top_k").value || 5);
378464
try{
379465
const win = document.getElementById("chatWindow");
380-
// clear previous transient search results (keep history preserved)
381466
const header = document.createElement("div");
382467
header.className = "mb-2 muted-small";
383468
header.textContent = "Search results";
384469
win.appendChild(header);
470+
385471
const resp = await fetch("/search", {
386472
method: "POST",
387473
headers: { "Content-Type":"application/json" },
@@ -401,7 +487,9 @@ <h5 class="card-title mb-0">Chat</h5>
401487
data.forEach(d=>{
402488
const item = document.createElement("div");
403489
item.className = "card mb-2 p-2";
404-
item.innerHTML = `<div class="small"><strong>${DOMPurify.sanitize(d.path)}</strong></div><div class="muted-small">score: ${d.score !== undefined && d.score !== null ? parseFloat(d.score).toFixed(4) : "n/a"}</div>`;
490+
const path = DOMPurify.sanitize(d.path || "(unknown)");
491+
const score = (d.score !== undefined && d.score !== null) ? Number(d.score).toFixed(4) : "n/a";
492+
item.innerHTML = `<div class="small"><strong>${path}</strong></div><div class="muted-small">score: ${score}</div>`;
405493
win.appendChild(item);
406494
});
407495
} else {
@@ -421,17 +509,28 @@ <h5 class="card-title mb-0">Chat</h5>
421509
}
422510
}
423511

424-
document.getElementById("sendBtn").addEventListener("click", sendMessage);
425-
document.getElementById("clearBtn").addEventListener("click", clearHistory);
426-
document.getElementById("searchBtn").addEventListener("click", searchOnly);
427-
document.getElementById("chatInput").addEventListener("keydown", function(e){
428-
if(e.key === "Enter" && !e.shiftKey){
429-
e.preventDefault();
430-
sendMessage();
431-
}
432-
});
512+
// Wire up event handlers after DOM loaded
513+
window.addEventListener("load", () => {
514+
document.getElementById("sendBtn").addEventListener("click", sendMessage);
515+
document.getElementById("clearBtn").addEventListener("click", clearHistory);
516+
document.getElementById("searchBtn").addEventListener("click", searchOnly);
517+
document.getElementById("chatInput").addEventListener("keydown", function(e){
518+
if(e.key === "Enter" && !e.shiftKey){
519+
e.preventDefault();
520+
sendMessage();
521+
}
522+
});
523+
// click on analysis items sets analysis id
524+
document.querySelectorAll(".analysis-item").forEach(li=>{
525+
li.addEventListener("click", ()=>{
526+
const aid = li.getAttribute("data-analysis-id");
527+
document.getElementById("analysis_id").value = aid;
528+
});
529+
});
433530

434-
renderChat();
531+
renderChat();
532+
startPolling();
533+
});
435534
</script>
436535
</body>
437536
</html>

0 commit comments

Comments
 (0)