From 30a1c4ab461eaca9b3bb0214e9d65b029ece6ced Mon Sep 17 00:00:00 2001 From: michael Date: Wed, 19 Nov 2025 15:55:10 -0600 Subject: [PATCH] Implement Delete by CSV functionality --- main.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++ templates/nav.html | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/main.py b/main.py index e0b259c..e75f9d9 100644 --- a/main.py +++ b/main.py @@ -1143,6 +1143,67 @@ async def import_agents_csv( except Exception as e: raise HTTPException(status_code=500, detail=f"Import failed: {str(e)}") +@app.post("/api/admin/agents/delete/csv") +async def delete_agents_csv( + request: Request, + file: UploadFile = File(...), + current_user: dict = Depends(require_admin) +): + """Delete agents based on CSV file""" + if not file.filename.endswith('.csv'): + raise HTTPException(status_code=400, detail="File must be a CSV") + + try: + contents = await file.read() + decoded = contents.decode('utf-8') + csv_reader = csv.DictReader(io.StringIO(decoded)) + + deleted_count = 0 + not_found_count = 0 + error_count = 0 + errors = [] + + for row_num, row in enumerate(csv_reader, start=1): + try: + agent_id = row.get("agent_id") + agent_name = row.get("agent_name") + + if agent_id and agent_id.strip(): + # Delete by ID + success = await crud.delete_agent(agent_id.strip()) + if success: + deleted_count += 1 + else: + not_found_count += 1 + elif agent_name and agent_name.strip(): + # Delete by Name (All matches) + # We need to find them first + # Using direct collection access here for efficiency/custom logic not in crud + from database import agents_collection + result = await agents_collection.delete_many({"agent_name": agent_name.strip()}) + if result.deleted_count > 0: + deleted_count += result.deleted_count + else: + not_found_count += 1 + else: + # Skip empty rows + continue + + except Exception as e: + error_count += 1 + errors.append(f"Row {row_num}: {str(e)}") + + return JSONResponse({ + "success": True, + "deleted": deleted_count, + "not_found": not_found_count, + "errors": error_count, + "error_details": errors[:10] + }) + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Delete failed: {str(e)}") + # Agent Collector API Endpoints (for compatibility with agent_collector app) @app.post("/agents") async def create_agent_collector( diff --git a/templates/nav.html b/templates/nav.html index 15865ae..8b6c510 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -37,6 +37,11 @@ Import CSV + + Delete by CSV + + {% endif %} {% endif %} @@ -235,4 +240,43 @@ // Reset input input.value = ''; } + + async function deleteCsv(input) { + if (!input.files || !input.files[0]) return; + + if (!confirm("WARNING: This will permanently delete all agents found in the CSV file. Are you sure you want to proceed?")) { + input.value = ''; + return; + } + + const file = input.files[0]; + const formData = new FormData(); + formData.append('file', file); + + try { + const response = await fetch('{{ base_path }}/api/admin/agents/delete/csv', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + if (response.ok && result.success) { + let msg = `Deletion complete!\nDeleted: ${result.deleted}\nNot Found: ${result.not_found}\nErrors: ${result.errors}`; + if (result.errors > 0) { + msg += `\n\nFirst few errors:\n${result.error_details.join('\n')}`; + } + alert(msg); + window.location.reload(); + } else { + alert('Deletion failed: ' + (result.detail || 'Unknown error')); + } + } catch (error) { + console.error('Error deleting via CSV:', error); + alert('Error deleting via CSV: ' + error.message); + } + + // Reset input + input.value = ''; + } \ No newline at end of file