Marwan Alwali fd2f7259c0 update
2025-08-14 18:05:05 +03:00

438 lines
19 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{{ object.name }} - Analytics Reports{% endblock %}
{% block content %}
<!-- BEGIN breadcrumb -->
<ol class="breadcrumb float-xl-end">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'analytics:dashboard' %}">Analytics</a></li>
<li class="breadcrumb-item"><a href="{% url 'analytics:report_list' %}">Reports</a></li>
<li class="breadcrumb-item active">{{ object.name }}</li>
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
{{ object.name }}
<small>{{ object.get_category_display }} Report</small>
</h1>
<!-- END page-header -->
<div class="row">
<div class="col-xl-8">
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Report Information</h4>
<div class="panel-heading-btn">
<button class="btn btn-xs btn-success me-2" onclick="generateReport()">
<i class="fa fa-play"></i> Generate
</button>
<a href="{% url 'analytics:report_update' object.report_id %}" class="btn btn-xs btn-primary me-2">
<i class="fa fa-edit"></i> Edit
</a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td class="fw-bold" width="150">Name:</td>
<td>{{ object.name }}</td>
</tr>
<tr>
<td class="fw-bold">Description:</td>
<td>{{ object.description|default:"No description" }}</td>
</tr>
<tr>
<td class="fw-bold">Category:</td>
<td>
<span class="badge bg-primary">{{ object.get_category_display }}</span>
</td>
</tr>
<tr>
<td class="fw-bold">Type:</td>
<td>
<i class="fa fa-{% if object.type == 'SCHEDULED' %}clock{% elif object.type == 'ON_DEMAND' %}play{% else %}bolt{% endif %} me-2"></i>
{{ object.get_type_display }}
</td>
</tr>
<tr>
<td class="fw-bold">Status:</td>
<td>
<span class="badge bg-{% if object.status == 'ACTIVE' %}success{% elif object.status == 'DRAFT' %}warning{% else %}secondary{% endif %}">
{{ object.get_status_display }}
</span>
</td>
</tr>
</table>
</div>
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td class="fw-bold" width="150">Created:</td>
<td>{{ object.created_at|date:"M d, Y H:i" }}</td>
</tr>
<tr>
<td class="fw-bold">Created By:</td>
<td>{{ object.created_by.get_full_name|default:"System" }}</td>
</tr>
<tr>
<td class="fw-bold">Last Generated:</td>
<td>
{% if object.last_generated %}
{{ object.last_generated|date:"M d, Y H:i" }}
<br><small class="text-muted">{{ object.last_generated|timesince }} ago</small>
{% else %}
<span class="text-muted">Never generated</span>
{% endif %}
</td>
</tr>
<tr>
<td class="fw-bold">Next Run:</td>
<td>
{% if object.next_run %}
{{ object.next_run|date:"M d, Y H:i" }}
<br><small class="text-muted">{{ object.next_run|timeuntil }} from now</small>
{% else %}
<span class="text-muted">Not scheduled</span>
{% endif %}
</td>
</tr>
<tr>
<td class="fw-bold">Format:</td>
<td>
{% for format in object.output_formats.all %}
<span class="badge bg-info me-1">{{ format.name }}</span>
{% empty %}
<span class="text-muted">No formats specified</span>
{% endfor %}
</td>
</tr>
</table>
</div>
</div>
<!-- Report Configuration -->
{% if object.configuration %}
<div class="mt-4">
<h6>Report Configuration</h6>
<div class="bg-light p-3 rounded">
<pre class="mb-0"><code>{{ object.configuration_formatted }}</code></pre>
</div>
</div>
{% endif %}
<!-- SQL Query -->
{% if object.sql_query %}
<div class="mt-4">
<h6>SQL Query</h6>
<div class="bg-dark p-3 rounded">
<pre class="mb-0 text-light"><code>{{ object.sql_query }}</code></pre>
</div>
</div>
{% endif %}
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Recent Executions</h4>
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>
</div>
</div>
<div class="panel-body">
{% if recent_executions %}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Execution Time</th>
<th>Status</th>
<th>Duration</th>
<th>Records</th>
<th>Size</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for execution in recent_executions %}
<tr>
<td>{{ execution.started_at|date:"M d, Y H:i:s" }}</td>
<td>
<span class="badge bg-{% if execution.status == 'COMPLETED' %}success{% elif execution.status == 'FAILED' %}danger{% elif execution.status == 'RUNNING' %}warning{% else %}secondary{% endif %}">
{{ execution.get_status_display }}
</span>
</td>
<td>{{ execution.duration|default:"-" }}</td>
<td>{{ execution.record_count|default:"-" }}</td>
<td>{{ execution.file_size_formatted|default:"-" }}</td>
<td>
{% if execution.output_file %}
<a href="{{ execution.output_file.url }}" class="btn btn-xs btn-outline-primary" target="_blank">
<i class="fa fa-download"></i> Download
</a>
{% endif %}
{% if execution.status == 'FAILED' %}
<button class="btn btn-xs btn-outline-danger" onclick="viewError({{ execution.id }})">
<i class="fa fa-exclamation-triangle"></i> Error
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-4 text-muted">
<i class="fa fa-history fa-3x mb-3"></i>
<p>No execution history found for this report.</p>
<button class="btn btn-primary" onclick="generateReport()">
<i class="fa fa-play me-2"></i>Generate First Report
</button>
</div>
{% endif %}
</div>
</div>
<!-- END panel -->
</div>
<div class="col-xl-4">
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Quick Actions</h4>
</div>
<div class="panel-body">
<div class="d-grid gap-2">
<button class="btn btn-success" onclick="generateReport()">
<i class="fa fa-play me-2"></i>Generate Report
</button>
<a href="{% url 'analytics:report_update' object.pk %}" class="btn btn-primary">
<i class="fa fa-edit me-2"></i>Edit Report
</a>
{% if object.type == 'SCHEDULED' %}
<button class="btn btn-warning" onclick="scheduleReport()">
<i class="fa fa-clock me-2"></i>Update Schedule
</button>
{% endif %}
<button class="btn btn-info" onclick="duplicateReport()">
<i class="fa fa-copy me-2"></i>Duplicate Report
</button>
<button class="btn btn-outline-secondary" onclick="exportReport()">
<i class="fa fa-download me-2"></i>Export Definition
</button>
<hr>
<a href="{% url 'analytics:report_delete' object.pk %}" class="btn btn-danger">
<i class="fa fa-trash me-2"></i>Delete Report
</a>
</div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Report Statistics</h4>
</div>
<div class="panel-body">
<div class="row text-center">
<div class="col-6 mb-3">
<div class="fs-24px fw-bold text-primary">{{ object.execution_count }}</div>
<div class="small text-muted">Total Executions</div>
</div>
<div class="col-6 mb-3">
<div class="fs-24px fw-bold text-success">{{ object.success_rate }}%</div>
<div class="small text-muted">Success Rate</div>
</div>
<div class="col-6 mb-3">
<div class="fs-24px fw-bold text-info">{{ object.avg_duration }}</div>
<div class="small text-muted">Avg Duration</div>
</div>
<div class="col-6 mb-3">
<div class="fs-24px fw-bold text-warning">{{ object.avg_records }}</div>
<div class="small text-muted">Avg Records</div>
</div>
</div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Schedule Information</h4>
</div>
<div class="panel-body">
{% if object.schedule %}
<table class="table table-sm table-borderless">
<tr>
<td class="text-muted">Frequency:</td>
<td>{{ object.schedule.get_frequency_display }}</td>
</tr>
<tr>
<td class="text-muted">Time:</td>
<td>{{ object.schedule.time|time:"H:i" }}</td>
</tr>
{% if object.schedule.day_of_week %}
<tr>
<td class="text-muted">Day of Week:</td>
<td>{{ object.schedule.get_day_of_week_display }}</td>
</tr>
{% endif %}
{% if object.schedule.day_of_month %}
<tr>
<td class="text-muted">Day of Month:</td>
<td>{{ object.schedule.day_of_month }}</td>
</tr>
{% endif %}
<tr>
<td class="text-muted">Active:</td>
<td>
<span class="badge bg-{{ object.schedule.is_active|yesno:'success,secondary' }}">
{{ object.schedule.is_active|yesno:'Yes,No' }}
</span>
</td>
</tr>
</table>
{% else %}
<div class="text-center py-3 text-muted">
<i class="fa fa-clock fa-2x mb-2"></i>
<p class="mb-0">No schedule configured.</p>
{% if object.type == 'SCHEDULED' %}
<button class="btn btn-sm btn-outline-primary mt-2" onclick="scheduleReport()">
Add Schedule
</button>
{% endif %}
</div>
{% endif %}
</div>
</div>
<!-- END panel -->
</div>
</div>
{% endblock %}
{% block js %}
<script>
function generateReport() {
if (confirm('Generate this report now?')) {
$.ajax({
url: '{% url "analytics:execute_report" object.pk %}',
method: 'POST',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
beforeSend: function() {
toastr.info('Starting report generation...');
},
success: function(response) {
if (response.success) {
toastr.success('Report generation started successfully');
// Refresh page after a delay to show updated status
setTimeout(function() {
location.reload();
}, 2000);
} else {
toastr.error('Failed to generate report: ' + (response.error || 'Unknown error'));
}
},
error: function() {
toastr.error('An error occurred while generating the report');
}
});
}
}
function scheduleReport() {
// Implementation for scheduling
toastr.info('Schedule management functionality will be implemented');
}
{#function duplicateReport() {#}
{# if (confirm('Create a copy of this report?')) {#}
{# $.ajax({#}
{# url: '{% url "analytics:report_duplicate" object.pk %}',#}
{# method: 'POST',#}
{# data: {#}
{# 'csrfmiddlewaretoken': '{{ csrf_token }}'#}
{# },#}
{# success: function(response) {#}
{# if (response.success) {#}
{# toastr.success('Report duplicated successfully');#}
{# window.location.href = response.redirect_url;#}
{# } else {#}
{# toastr.error('Failed to duplicate report');#}
{# }#}
{# },#}
{# error: function() {#}
{# toastr.error('An error occurred while duplicating the report');#}
{# }#}
{# });#}
{# }#}
{# }#}
{#function exportReport() {#}
{# window.location.href = '{% url "analytics:report_export_definition" object.pk %}';#}
{# }#}
{#function viewError(executionId) {#}
{# $.ajax({#}
{# url: '{% url "analytics:execution_error" 0 %}'.replace('0', executionId),#}
{# success: function(response) {#}
{# // Show error details in a modal#}
{# var modal = $('<div class="modal fade" tabindex="-1">' +#}
{# '<div class="modal-dialog modal-lg">' +#}
{# '<div class="modal-content">' +#}
{# '<div class="modal-header">' +#}
{# '<h5 class="modal-title">Execution Error</h5>' +#}
{# '<button type="button" class="btn-close" data-bs-dismiss="modal"></button>' +#}
{# '</div>' +#}
{# '<div class="modal-body">' +#}
{# '<pre>' + response.error_message + '</pre>' +#}
{# '</div>' +#}
{# '<div class="modal-footer">' +#}
{# '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>' +#}
{# '</div>' +#}
{# '</div>' +#}
{# '</div>' +#}
{# '</div>');#}
{# #}
{# $('body').append(modal);#}
{# modal.modal('show');#}
{# modal.on('hidden.bs.modal', function() {#}
{# modal.remove();#}
{# });#}
{# },#}
{# error: function() {#}
{# toastr.error('Failed to load error details');#}
{# }#}
{# });#}
{# }#}
// Auto-refresh if report is currently running
{% if object.is_running %}
setInterval(function() {
location.reload();
}, 10000); // Refresh every 10 seconds
{% endif %}
</script>
{% endblock %}