added risk factor to quality audit functionality - risk factor is required when cheking quality audit box - both these fields are admin only
This commit is contained in:
parent
f811854198
commit
48ee1298e9
6 changed files with 258 additions and 5 deletions
6
crud.py
6
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(
|
||||
|
|
|
|||
21
main.py
21
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"]),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@
|
|||
|
||||
<div class="mb-3" id="editQualityAuditSection">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="editQualityAuditStatus">
|
||||
<input class="form-check-input" type="checkbox" id="editQualityAuditStatus" onchange="toggleAdminRiskFactor()">
|
||||
<label class="form-check-label" for="editQualityAuditStatus">
|
||||
<i class="fas fa-certificate me-2"></i>Quality Audit
|
||||
<span class="badge bg-warning text-dark ms-2">Admin Only</span>
|
||||
|
|
@ -316,6 +316,25 @@
|
|||
Check this box if the agent has passed quality audit review.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="editRiskFactorSection" style="display: none;">
|
||||
<label for="editRiskFactor" class="form-label">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>Risk Factor
|
||||
<span class="badge bg-warning text-dark ms-2">Admin Only</span>
|
||||
<span class="text-danger">*</span>
|
||||
</label>
|
||||
<select class="form-select" id="editRiskFactor">
|
||||
<option value="">Select Risk Level</option>
|
||||
<option value="1">1 - Very Low Risk</option>
|
||||
<option value="2">2 - Low Risk</option>
|
||||
<option value="3">3 - Medium Risk</option>
|
||||
<option value="4">4 - High Risk</option>
|
||||
<option value="5">5 - Very High Risk</option>
|
||||
</select>
|
||||
<div class="form-text" id="editRiskFactorNote">
|
||||
Required when Quality Audit is checked. Select the appropriate risk level for this agent.
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
@ -421,6 +440,11 @@
|
|||
.status-Inactive { background-color: #f8d7da; color: #721c24; }
|
||||
.status-Deprecated { background-color: #e2e3e5; color: #41464b; }
|
||||
|
||||
.bg-orange {
|
||||
background-color: #fd7e14 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.stat-card {
|
||||
text-align: center;
|
||||
|
|
@ -769,6 +793,32 @@ function formatDate(dateString) {
|
|||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
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 toggleAdminRiskFactor() {
|
||||
const qualityAuditStatus = document.getElementById('editQualityAuditStatus');
|
||||
const riskFactorSection = document.getElementById('editRiskFactorSection');
|
||||
const riskFactor = document.getElementById('editRiskFactor');
|
||||
|
||||
if (qualityAuditStatus.checked) {
|
||||
riskFactorSection.style.display = 'block';
|
||||
riskFactor.required = true;
|
||||
} else {
|
||||
riskFactorSection.style.display = 'none';
|
||||
riskFactor.required = false;
|
||||
riskFactor.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showSuccess(message) {
|
||||
alert(message);
|
||||
}
|
||||
|
|
@ -870,6 +920,19 @@ async function editAgentAdmin(agentId) {
|
|||
}
|
||||
document.getElementById('editQualityAuditNote').innerHTML = noteHtml;
|
||||
|
||||
// Handle Risk Factor field
|
||||
const riskFactorSection = document.getElementById('editRiskFactorSection');
|
||||
const riskFactor = document.getElementById('editRiskFactor');
|
||||
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;
|
||||
}
|
||||
|
||||
// Show edit modal
|
||||
const modal = new bootstrap.Modal(document.getElementById('editAgentModal'));
|
||||
modal.show();
|
||||
|
|
@ -900,6 +963,16 @@ async function handleEditAgentSubmit(e) {
|
|||
quality_audit_status: document.getElementById('editQualityAuditStatus').checked
|
||||
};
|
||||
|
||||
// Add Risk Factor
|
||||
const riskFactorValue = document.getElementById('editRiskFactor').value;
|
||||
agentData.risk_factor = riskFactorValue ? parseInt(riskFactorValue) : null;
|
||||
|
||||
// Validate Risk Factor if Quality Audit is checked
|
||||
if (agentData.quality_audit_status && (!agentData.risk_factor || agentData.risk_factor < 1 || agentData.risk_factor > 5)) {
|
||||
showError('Risk Factor is required when Quality Audit is checked!');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`{{ base_path }}/api/agents/${agentId}`, {
|
||||
method: 'PUT',
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@
|
|||
|
||||
<div class="mb-3" id="editQualityAuditSection">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="editQualityAuditStatus">
|
||||
<input class="form-check-input" type="checkbox" id="editQualityAuditStatus" onchange="toggleEditRiskFactor()">
|
||||
<label class="form-check-label" for="editQualityAuditStatus">
|
||||
<i class="fas fa-certificate me-2"></i>Quality Audit
|
||||
<span class="badge bg-warning text-dark ms-2">Admin Only</span>
|
||||
|
|
@ -315,6 +315,25 @@
|
|||
Only administrators can modify quality audit status.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="editRiskFactorSection" style="display: none;">
|
||||
<label for="editRiskFactor" class="form-label">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>Risk Factor
|
||||
<span class="badge bg-warning text-dark ms-2">Admin Only</span>
|
||||
<span class="text-danger">*</span>
|
||||
</label>
|
||||
<select class="form-select" id="editRiskFactor">
|
||||
<option value="">Select Risk Level</option>
|
||||
<option value="1">1 - Very Low Risk</option>
|
||||
<option value="2">2 - Low Risk</option>
|
||||
<option value="3">3 - Medium Risk</option>
|
||||
<option value="4">4 - High Risk</option>
|
||||
<option value="5">5 - Very High Risk</option>
|
||||
</select>
|
||||
<div class="form-text" id="editRiskFactorNote">
|
||||
Required when Quality Audit is checked. Select the appropriate risk level for this agent.
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
@ -351,6 +370,11 @@
|
|||
.status-Inactive { background-color: #f8d7da; color: #721c24; }
|
||||
.status-Deprecated { background-color: #e2e3e5; color: #41464b; }
|
||||
|
||||
.bg-orange {
|
||||
background-color: #fd7e14 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.agent-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
|
@ -562,6 +586,7 @@ function displayAgents(agentsToShow) {
|
|||
</span>
|
||||
${agent.agent_version ? `<span class="badge bg-light text-dark">v${agent.agent_version}</span>` : ''}
|
||||
${agent.quality_audit_status ? '<span class="badge bg-success" title="Quality Audited"><i class="fas fa-certificate"></i></span>' : ''}
|
||||
${agent.quality_audit_status && agent.risk_factor ? getRiskFactorBadge(agent.risk_factor) : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
|
|
@ -639,6 +664,7 @@ async function showAgentDetails(agentId) {
|
|||
'<span class="badge bg-success"><i class="fas fa-certificate me-1"></i>Audited</span>' :
|
||||
'<span class="badge bg-secondary">Not Audited</span>'
|
||||
}
|
||||
${agent.quality_audit_status && agent.risk_factor ? getRiskFactorBadge(agent.risk_factor) : ''}
|
||||
</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -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 += '<br><br><small class="text-muted"><i class="fas fa-info-circle me-1"></i>No quality audit changes recorded yet.</small>';
|
||||
}
|
||||
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 `<span class="badge bg-${colors[riskFactor] || 'secondary'} ms-1" title="${getRiskFactorLabel(riskFactor)}">Risk ${riskFactor}</span>`;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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()">
|
||||
<label class="form-check-label" for="qualityAuditStatus">
|
||||
<i class="fas fa-certificate me-2"></i>Quality Audit
|
||||
{% if current_user.is_admin %}
|
||||
|
|
@ -194,6 +195,32 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4" id="riskFactorSection" style="display: none;">
|
||||
<label for="riskFactor" class="form-label">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>Risk Factor
|
||||
<span class="badge bg-warning text-dark ms-2">Admin Only</span>
|
||||
<span class="text-danger">*</span>
|
||||
</label>
|
||||
<select name="risk_factor"
|
||||
class="form-select"
|
||||
id="riskFactor"
|
||||
{% if not current_user.is_admin %}disabled{% endif %}>
|
||||
<option value="">Select Risk Level</option>
|
||||
<option value="1">1 - Very Low Risk</option>
|
||||
<option value="2">2 - Low Risk</option>
|
||||
<option value="3">3 - Medium Risk</option>
|
||||
<option value="4">4 - High Risk</option>
|
||||
<option value="5">5 - Very High Risk</option>
|
||||
</select>
|
||||
<div class="form-text">
|
||||
{% if current_user.is_admin %}
|
||||
Required when Quality Audit is checked. Select the appropriate risk level for this agent.
|
||||
{% else %}
|
||||
Only administrators can set risk factor levels.
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mb-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="fas fa-robot me-2"></i>Register Agent
|
||||
|
|
@ -260,9 +287,26 @@
|
|||
// Simple form validation and duplicate submission prevention
|
||||
let formSubmitted = false;
|
||||
|
||||
function toggleRiskFactor() {
|
||||
const qualityAuditStatus = document.getElementById('qualityAuditStatus');
|
||||
const riskFactorSection = document.getElementById('riskFactorSection');
|
||||
const riskFactor = document.getElementById('riskFactor');
|
||||
|
||||
if (qualityAuditStatus.checked) {
|
||||
riskFactorSection.style.display = 'block';
|
||||
riskFactor.required = true;
|
||||
} else {
|
||||
riskFactorSection.style.display = 'none';
|
||||
riskFactor.required = false;
|
||||
riskFactor.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('agentForm').addEventListener('submit', function(e) {
|
||||
const agentName = document.getElementById('agentName').value.trim();
|
||||
const agentTool = document.getElementById('agentTool').value.trim();
|
||||
const qualityAuditStatus = document.getElementById('qualityAuditStatus');
|
||||
const riskFactor = document.getElementById('riskFactor');
|
||||
|
||||
if (!agentName) {
|
||||
e.preventDefault();
|
||||
|
|
@ -276,6 +320,13 @@ document.getElementById('agentForm').addEventListener('submit', function(e) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Validate Risk Factor if Quality Audit is checked
|
||||
if (qualityAuditStatus.checked && (!riskFactor.value || riskFactor.value === '')) {
|
||||
e.preventDefault();
|
||||
alert('Risk Factor is required when Quality Audit is checked!');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent duplicate submissions
|
||||
if (formSubmitted) {
|
||||
e.preventDefault();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue