Spaces:
Sleeping
Sleeping
{% extends "base_layout.html" %} | |
{% block title %}Case Details: {{ case.case_id_display }} - InterroGen{% endblock %} | |
{% block head_extra %} | |
<style> | |
.json-display { | |
background-color: #f8f9fa; | |
border: 1px solid #dee2e6; | |
padding: 15px; | |
border-radius: 5px; | |
white-space: pre-wrap; | |
word-wrap: break-word; | |
max-height: 400px; /* Added for scrollability */ | |
overflow-y: auto; /* Added for scrollability */ | |
} | |
.case-info-pre { | |
background-color: #f8f9fa; | |
border: 1px solid #eee; | |
padding: 10px; | |
border-radius: 4px; | |
white-space: pre-wrap; | |
word-wrap: break-word; | |
font-family: monospace; | |
} | |
</style> | |
{% endblock %} | |
{% block page_content %} | |
<div class="heading-container"> | |
<h1 class="h3">Case Details: {{ case.case_id_display }}</h1> | |
<div> | |
<a href="{{ url_for('manage_cases') }}" class="btn btn-outline-secondary"><i class="bi bi-arrow-left-circle me-1"></i>Back to Cases List</a> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-lg-7"> | |
<div class="card mb-3"> | |
<div class="card-header"> | |
<i class="bi bi-file-text me-2"></i>Case Information | |
</div> | |
<div class="card-body"> | |
<dl class="row"> | |
<dt class="col-sm-4">Case ID:</dt> | |
<dd class="col-sm-8">{{ case.case_id_display }}</dd> | |
<dt class="col-sm-4">Case Type:</dt> | |
<dd class="col-sm-8">{{ case.case_type }}</dd> | |
<dt class="col-sm-4">Suspect Name:</dt> | |
<dd class="col-sm-8">{{ case.suspect_name }}</dd> | |
<dt class="col-sm-4">Status:</dt> | |
<dd class="col-sm-8"><span class="case-status status-{{ case.status.value.lower() }}">{{ case.status.value }}</span></dd> | |
<dt class="col-sm-4">Created At:</dt> | |
<dd class="col-sm-8">{{ case.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</dd> | |
<dt class="col-sm-4">Last Updated:</dt> | |
<dd class="col-sm-8">{{ case.updated_at.strftime('%Y-%m-%d %H:%M:%S') }}</dd> | |
<dt class="col-sm-4">Country Context:</dt> | |
<dd class="col-sm-8">{{ case.country_context.name if case.country_context else 'N/A' }}</dd> | |
</dl> | |
<hr> | |
<h6>Profile Details:</h6> | |
<div class="case-info-pre">{{ case.profile_details if case.profile_details else 'Not provided' }}</div> | |
<h6 class="mt-3">Evidence Summary:</h6> | |
<div class="case-info-pre">{{ case.evidence_summary if case.evidence_summary else 'Not provided' }}</div> | |
</div> | |
</div> | |
<div class="card mb-3"> | |
<div class="card-header"> | |
<i class="bi bi-journal-richtext me-2"></i>Generated Reports for this Case | |
</div> | |
<div class="card-body"> | |
{% if case.reports %} | |
<ul class="list-group list-group-flush"> | |
{% for report in case.reports %} | |
<li class="list-group-item d-flex justify-content-between align-items-center"> | |
Report generated on {{ report.generated_at.strftime('%Y-%m-%d %H:%M') }} (Country: {{ report.report_country_context.name if report.report_country_context else 'N/A' }}) | |
<a href="{{ url_for('view_report', report_id=report.id) }}" class="btn btn-sm btn-outline-info">View Report</a> | |
</li> | |
{% endfor %} | |
</ul> | |
{% else %} | |
<p class="text-muted">No reports generated for this case yet.</p> | |
{% endif %} | |
</div> | |
</div> | |
</div> | |
<div class="col-lg-5"> | |
<div class="card mb-3"> | |
<div class="card-header"> | |
<i class="bi bi-patch-question-fill me-2"></i>Interrogation Questions | |
</div> | |
<div class="card-body"> | |
<button id="generateQuestionsBtn" class="btn btn-primary w-100 mb-3" data-case-id="{{ case.id }}"> | |
<i class="bi bi-magic me-1"></i> Generate Interrogation Questions (LLM) | |
</button> | |
<div id="questionsArea"> | |
{% if case.interrogation_sessions %} | |
{% for session in case.interrogation_sessions | sort(attribute='session_date', reverse=True) %} | |
<div class="accordion mb-2" id="accordionSession{{ session.id }}"> | |
<div class="accordion-item"> | |
<h2 class="accordion-header" id="headingSession{{ session.id }}"> | |
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSession{{ session.id }}" aria-expanded="false" aria-controls="collapseSession{{ session.id }}"> | |
Session on {{ session.session_date.strftime('%Y-%m-%d %H:%M') }} ({{ session.generated_questions | length }} Qs) | |
</button> | |
</h2> | |
<div id="collapseSession{{ session.id }}" class="accordion-collapse collapse" aria-labelledby="headingSession{{ session.id }}" data-bs-parent="#accordionSession{{ session.id }}"> | |
<div class="accordion-body"> | |
{% if session.summary_notes %} | |
<p><strong>Notes:</strong> {{ session.summary_notes }}</p> | |
{% endif %} | |
{% if session.generated_questions %} | |
<ul class="list-group list-group-flush"> | |
{% for q in session.generated_questions %} | |
<li class="list-group-item">{{ q.question_text }} <small class="text-muted">({{ q.category }})</small></li> | |
{% endfor %} | |
</ul> | |
{% else %} | |
<p class="text-muted">No questions recorded for this session.</p> | |
{% endif %} | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endfor %} | |
{% else %} | |
<p class="text-muted text-center mt-3">No interrogation sessions found. Click button to generate questions for a new session.</p> | |
{% endif %} | |
</div> | |
</div> | |
</div> | |
<div class="card mb-3"> | |
<div class="card-header"> | |
<i class="bi bi-file-earmark-medical-fill me-2"></i>Generate New Report (LLM) | |
</div> | |
<div class="card-body"> | |
<form id="generateReportForm"> | |
<div class="mb-3"> | |
<label for="country_id_report" class="form-label">Select Country for Report Recommendations: <span class="text-danger">*</span></label> | |
<select class="form-select" id="country_id_report" name="country_id" required> | |
<option value="">Select Country</option> | |
{% for country in countries %} | |
<option value="{{ country.id }}" {% if case.country_context and case.country_context.id == country.id %}selected{% endif %}>{{ country.name }}</option> | |
{% endfor %} | |
</select> | |
</div> | |
<div class="mb-3"> | |
<label for="interrogation_summary_manual" class="form-label">Additional Interrogation Summary (Optional):</label> | |
<textarea class="form-control" id="interrogation_summary_manual" name="interrogation_summary" rows="4" placeholder="Add any manual notes or summary of the interrogation to be included in the report generation prompt. Data from saved questions/answers will be automatically included."></textarea> | |
</div> | |
<button type="submit" class="btn btn-success w-100" data-case-id="{{ case.id }}"> | |
<i class="bi bi-lightning-charge-fill me-1"></i> Generate Report | |
</button> | |
</form> | |
<div id="reportResultArea" class="mt-3"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} | |
{% block scripts %} | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
const generateQuestionsBtn = document.getElementById('generateQuestionsBtn'); | |
const questionsArea = document.getElementById('questionsArea'); | |
if(generateQuestionsBtn) { | |
generateQuestionsBtn.addEventListener('click', function() { | |
const caseId = this.dataset.caseId; | |
this.disabled = true; | |
this.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Generating...'; | |
fetch(`/generate_questions/${caseId}`, { method: 'POST' }) | |
.then(response => { | |
if (!response.ok) { | |
return response.json().then(err => { throw err; }); | |
} | |
return response.json(); | |
}) | |
.then(data => { | |
if(data.error) { | |
alert('Error: ' + data.error); | |
return; | |
} | |
// Instead of replacing, we just inform and ask to refresh for simplicity now | |
// Or, ideally, dynamically add the new session and questions to the accordion | |
alert('Questions generated successfully! They have been saved to a new interrogation session. Please refresh the page to see them.'); | |
window.location.reload(); // Simple refresh to show new data | |
}) | |
.catch(error => { | |
console.error('Error generating questions:', error); | |
alert('Failed to generate questions: ' + (error.error || 'Unknown error')); | |
}) | |
.finally(() => { | |
this.disabled = false; | |
this.innerHTML = '<i class="bi bi-magic me-1"></i> Generate Interrogation Questions (LLM)'; | |
}); | |
}); | |
} | |
const generateReportForm = document.getElementById('generateReportForm'); | |
const reportResultArea = document.getElementById('reportResultArea'); | |
if(generateReportForm) { | |
generateReportForm.addEventListener('submit', function(event) { | |
event.preventDefault(); | |
const submitButton = this.querySelector('button[type="submit"]'); | |
const caseId = submitButton.dataset.caseId; | |
const countryId = document.getElementById('country_id_report').value; | |
const interrogationSummary = document.getElementById('interrogation_summary_manual').value; | |
if (!countryId) { | |
alert('Please select a country for the report recommendations.'); | |
return; | |
} | |
submitButton.disabled = true; | |
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Generating Report...'; | |
reportResultArea.innerHTML = '<div class="alert alert-info">Generating report, please wait...</div>'; | |
fetch(`/generate_report/${caseId}`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ country_id: countryId, interrogation_summary: interrogationSummary }) | |
}) | |
.then(response => { | |
if (!response.ok) { | |
return response.json().then(err => { throw err; }); | |
} | |
return response.json(); | |
}) | |
.then(data => { | |
if(data.error) { | |
reportResultArea.innerHTML = `<div class="alert alert-danger">Error: ${data.error}</div>`; | |
return; | |
} | |
let reportJsonParsed = {}; | |
try { | |
reportJsonParsed = JSON.parse(data.raw_json_report); | |
} catch (e) { | |
console.warn("Could not parse raw_json_report for pretty print", e); | |
reportJsonParsed = data.raw_json_report; // show as string if not parsable | |
} | |
reportResultArea.innerHTML = `<div class="alert alert-success">Report generated successfully! Report ID: ${data.report_id}</div> \ | |
<h6>Raw LLM Output (JSON):</h6> \ | |
<div class="json-display">${JSON.stringify(reportJsonParsed, null, 2)}</div> \ | |
<p class="mt-2"><a href="/report/${data.report_id}" class="btn btn-info mt-2"><i class="bi bi-eye-fill me-1"></i>View Formatted Report</a></p><p class="text-muted"><small>Page will refresh to show the new report in the list below.</small></p>`; | |
setTimeout(() => { window.location.reload(); }, 3000); // Refresh to update list | |
}) | |
.catch(error => { | |
console.error('Error generating report:', error); | |
reportResultArea.innerHTML = `<div class="alert alert-danger">Failed to generate report: ${(error.error || 'Unknown error')}. Check console.</div>`; | |
}) | |
.finally(() => { | |
submitButton.disabled = false; | |
submitButton.innerHTML = '<i class="bi bi-lightning-charge-fill me-1"></i> Generate Report'; | |
}); | |
}); | |
} | |
}); | |
</script> | |
{% endblock %} | |