From 48ee1298e9991755355fef990252ed6f3c417385 Mon Sep 17 00:00:00 2001 From: michael Date: Fri, 5 Sep 2025 14:04:18 -0500 Subject: [PATCH] added risk factor to quality audit functionality - risk factor is required when cheking quality audit box - both these fields are admin only --- crud.py | 6 ++ main.py | 21 ++++++- models.py | 3 + templates/admin/dashboard.html | 75 ++++++++++++++++++++++- templates/agent_management.html | 105 +++++++++++++++++++++++++++++++- templates/agent_register.html | 53 +++++++++++++++- 6 files changed, 258 insertions(+), 5 deletions(-) diff --git a/crud.py b/crud.py index 04e0b2b..b196d4b 100644 --- a/crud.py +++ b/crud.py @@ -147,6 +147,8 @@ async def create_agent(agent_data: dict, user_id: str): agent_doc["quality_audit_updated_at"] = None if "quality_audit_updated_by_name" not in agent_doc: agent_doc["quality_audit_updated_by_name"] = None + if "risk_factor" not in agent_doc: + agent_doc["risk_factor"] = None result = await agents_collection.insert_one(agent_doc) agent_doc["_id"] = result.inserted_id return agent_doc @@ -223,6 +225,10 @@ async def update_agent(agent_id: str, update_data: dict, user_id: str = None, ad update_data["quality_audit_updated_by"] = admin_user_info.get("user_id") update_data["quality_audit_updated_at"] = datetime.utcnow().isoformat() update_data["quality_audit_updated_by_name"] = admin_user_info.get("user_name", admin_user_info.get("email")) + + # If Quality Audit is being unchecked, clear Risk Factor + if update_data["quality_audit_status"] is False: + update_data["risk_factor"] = None update_data["updated_at"] = datetime.utcnow() result = await agents_collection.update_one( diff --git a/main.py b/main.py index 6246658..3af05b5 100644 --- a/main.py +++ b/main.py @@ -141,6 +141,7 @@ def create_agent_response(agent: dict) -> models.AiAgentResponse: quality_audit_updated_by=agent.get("quality_audit_updated_by"), quality_audit_updated_at=agent.get("quality_audit_updated_at"), quality_audit_updated_by_name=agent.get("quality_audit_updated_by_name"), + risk_factor=agent.get("risk_factor"), created_by=agent["created_by"] ) @@ -466,7 +467,8 @@ async def agent_register_form( agent_tags: str = Form(None), agent_userbase: str = Form(None), agent_capabilities: str = Form(None), - quality_audit_status: bool = Form(False) + quality_audit_status: bool = Form(False), + risk_factor: int = Form(None) ): try: # Get user from cookie - require authentication @@ -505,9 +507,18 @@ async def agent_register_form( agent_data["quality_audit_updated_by"] = user_id agent_data["quality_audit_updated_at"] = datetime.utcnow().isoformat() agent_data["quality_audit_updated_by_name"] = current_user.get("full_name", current_user.get("email")) + + # Validate Risk Factor when Quality Audit is checked + if risk_factor is None or not (1 <= risk_factor <= 5): + context = get_template_context(request, current_user) + context["error"] = "Risk Factor (1-5) is required when Quality Audit is checked." + return templates.TemplateResponse("agent_register.html", context) + + agent_data["risk_factor"] = risk_factor else: # Non-admin or quality audit not checked agent_data["quality_audit_status"] = False + agent_data["risk_factor"] = None # Remove None values agent_data = {k: v for k, v in agent_data.items() if v is not None} @@ -749,6 +760,14 @@ async def update_agent(agent_id: str, agent: models.AiAgentCreate, current_user: admin_user_info = None agent_data = agent.model_dump() + # Validate Risk Factor when Quality Audit is checked (admin only) + if current_user.get("is_admin") and agent_data.get("quality_audit_status"): + if agent_data.get("risk_factor") is None or not (1 <= agent_data.get("risk_factor", 0) <= 5): + raise HTTPException( + status_code=422, + detail="Risk Factor (1-5) is required when Quality Audit is checked." + ) + if current_user.get("is_admin"): admin_user_info = { "user_id": str(current_user["_id"]), diff --git a/models.py b/models.py index 2de2c90..2f7851a 100644 --- a/models.py +++ b/models.py @@ -23,6 +23,7 @@ class AiAgent(BaseModel): quality_audit_updated_by: str | None = Field(default=None, title="Admin user ID who updated quality audit") quality_audit_updated_at: str | None = Field(default=None, title="Quality audit last update timestamp") quality_audit_updated_by_name: str | None = Field(default=None, title="Admin user name who updated quality audit") + risk_factor: int | None = Field(default=None, title="Risk factor rating (1-5)", ge=1, le=5) @@ -71,6 +72,7 @@ class AiAgentCreate(BaseModel): quality_audit_updated_by: Optional[str] = None quality_audit_updated_at: Optional[str] = None quality_audit_updated_by_name: Optional[str] = None + risk_factor: Optional[int] = Field(default=None, ge=1, le=5) class AiAgentResponse(BaseModel): agent_id: str @@ -93,6 +95,7 @@ class AiAgentResponse(BaseModel): quality_audit_updated_by: Optional[str] = None quality_audit_updated_at: Optional[str] = None quality_audit_updated_by_name: Optional[str] = None + risk_factor: Optional[int] = None created_by: str # Agent Collector API Models (for compatibility with agent_collector app) diff --git a/templates/admin/dashboard.html b/templates/admin/dashboard.html index 920a3ed..0e8cc87 100644 --- a/templates/admin/dashboard.html +++ b/templates/admin/dashboard.html @@ -306,7 +306,7 @@
- +
+ +
@@ -639,6 +664,7 @@ async function showAgentDetails(agentId) { 'Audited' : 'Not Audited' } + ${agent.quality_audit_status && agent.risk_factor ? getRiskFactorBadge(agent.risk_factor) : ''}
@@ -828,6 +854,8 @@ async function showEditModal() { // Handle Quality Audit field const qualityAuditCheckbox = document.getElementById('editQualityAuditStatus'); const qualityAuditSection = document.getElementById('editQualityAuditSection'); + const riskFactorSection = document.getElementById('editRiskFactorSection'); + const riskFactor = document.getElementById('editRiskFactor'); if (currentUserIsAdmin) { qualityAuditSection.style.display = 'block'; @@ -844,8 +872,19 @@ async function showEditModal() { noteHtml += '

No quality audit changes recorded yet.'; } document.getElementById('editQualityAuditNote').innerHTML = noteHtml; + + // Handle Risk Factor field + riskFactor.value = agent.risk_factor || ''; + if (agent.quality_audit_status) { + riskFactorSection.style.display = 'block'; + riskFactor.required = true; + } else { + riskFactorSection.style.display = 'none'; + riskFactor.required = false; + } } else { qualityAuditSection.style.display = 'none'; + riskFactorSection.style.display = 'none'; } // Hide details modal if it's open, then show edit modal @@ -884,9 +923,16 @@ async function updateAgent(e) { agent_capabilities: document.getElementById('editAgentCapabilities').value.split(',').map(s => s.trim()).filter(s => s) }; - // Add Quality Audit status if user is admin + // Add Quality Audit status and Risk Factor if user is admin if (currentUserIsAdmin) { agentData.quality_audit_status = document.getElementById('editQualityAuditStatus').checked; + const riskFactorValue = document.getElementById('editRiskFactor').value; + agentData.risk_factor = riskFactorValue ? parseInt(riskFactorValue) : null; + } + + // Validate the form before sending + if (!validateEditForm()) { + return; } console.log('Sending agent update data:', agentData); @@ -1000,6 +1046,31 @@ function formatDate(dateString) { return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); } +function getRiskFactorLabel(riskFactor) { + const labels = { + 1: '1 - Very Low Risk', + 2: '2 - Low Risk', + 3: '3 - Medium Risk', + 4: '4 - High Risk', + 5: '5 - Very High Risk' + }; + return labels[riskFactor] || 'Not Set'; +} + +function getRiskFactorBadge(riskFactor) { + if (!riskFactor) return ''; + + const colors = { + 1: 'success', // Very Low Risk - Green + 2: 'info', // Low Risk - Blue + 3: 'warning', // Medium Risk - Yellow + 4: 'orange', // High Risk - Orange (we'll style this) + 5: 'danger' // Very High Risk - Red + }; + + return `Risk ${riskFactor}`; +} + function showSuccess(message) { // Simple alert - could be replaced with a toast notification alert(message); @@ -1010,6 +1081,36 @@ function showError(message) { alert('Error: ' + message); } +function toggleEditRiskFactor() { + const qualityAuditStatus = document.getElementById('editQualityAuditStatus'); + const riskFactorSection = document.getElementById('editRiskFactorSection'); + const riskFactor = document.getElementById('editRiskFactor'); + + if (qualityAuditStatus.checked && currentUserIsAdmin) { + riskFactorSection.style.display = 'block'; + riskFactor.required = true; + } else { + riskFactorSection.style.display = 'none'; + riskFactor.required = false; + if (!qualityAuditStatus.checked) { + riskFactor.value = ''; + } + } +} + +function validateEditForm() { + const qualityAuditStatus = document.getElementById('editQualityAuditStatus'); + const riskFactor = document.getElementById('editRiskFactor'); + + // Validate Risk Factor if Quality Audit is checked and user is admin + if (currentUserIsAdmin && qualityAuditStatus.checked && (!riskFactor.value || riskFactor.value === '')) { + showError('Risk Factor is required when Quality Audit is checked!'); + return false; + } + + return true; +} + function editAgent(agentId) { console.log('Edit button clicked for agent:', agentId); console.log('Current agents array:', agents); diff --git a/templates/agent_register.html b/templates/agent_register.html index a2a07f2..ab31c08 100644 --- a/templates/agent_register.html +++ b/templates/agent_register.html @@ -175,7 +175,8 @@ name="quality_audit_status" value="true" id="qualityAuditStatus" - {% if not current_user.is_admin %}disabled{% endif %}> + {% if not current_user.is_admin %}disabled{% endif %} + onchange="toggleRiskFactor()">