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

562 lines
23 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit Report{% else %}Create Report{% endif %} - Analytics{% endblock %}
{% block css %}
<link href="{% static 'plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/codemirror/lib/codemirror.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/codemirror/theme/material.css' %}" rel="stylesheet" />
{% 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">{% if object %}Edit Report{% else %}Create Report{% endif %}</li>
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
{% if object %}Edit Report{% else %}Create New Report{% endif %}
<small>{% if object %}{{ object.name }}{% else %}Analytics Report{% endif %}</small>
</h1>
<!-- END page-header -->
<form method="post" id="report-form">
{% csrf_token %}
<div class="row">
<div class="col-xl-8">
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Basic Information</h4>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label class="form-label">Report Name <span class="text-danger">*</span></label>
{{ form.name }}
{% if form.name.errors %}
<div class="text-danger">{{ form.name.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Status <span class="text-danger">*</span></label>
{{ form.status }}
{% if form.status.errors %}
<div class="text-danger">{{ form.status.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
{{ form.description }}
{% if form.description.errors %}
<div class="text-danger">{{ form.description.errors.0 }}</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Category <span class="text-danger">*</span></label>
{{ form.category }}
{% if form.category.errors %}
<div class="text-danger">{{ form.category.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Type <span class="text-danger">*</span></label>
{{ form.type }}
{% if form.type.errors %}
<div class="text-danger">{{ form.type.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Output Formats</label>
{{ form.output_formats }}
{% if form.output_formats.errors %}
<div class="text-danger">{{ form.output_formats.errors.0 }}</div>
{% endif %}
<div class="form-text">Select one or more output formats for the report</div>
</div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Query Configuration</h4>
</div>
<div class="panel-body">
<div class="mb-3">
<label class="form-label">SQL Query <span class="text-danger">*</span></label>
<textarea id="sql-editor" name="sql_query" class="form-control" rows="15">{{ form.sql_query.value|default:"" }}</textarea>
{% if form.sql_query.errors %}
<div class="text-danger">{{ form.sql_query.errors.0 }}</div>
{% endif %}
<div class="form-text">Write the SQL query that will generate the report data</div>
</div>
<div class="d-flex gap-2 mb-3">
<button type="button" class="btn btn-outline-primary" onclick="validateQuery()">
<i class="fa fa-check me-2"></i>Validate Query
</button>
<button type="button" class="btn btn-outline-info" onclick="testQuery()">
<i class="fa fa-play me-2"></i>Test Query
</button>
<button type="button" class="btn btn-outline-secondary" onclick="formatQuery()">
<i class="fa fa-code me-2"></i>Format Query
</button>
</div>
<div id="query-results" class="mt-3" style="display: none;">
<h6>Query Results Preview</h6>
<div id="results-content"></div>
</div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse" id="schedule-panel" style="display: none;">
<div class="panel-heading">
<h4 class="panel-title">Schedule Configuration</h4>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Frequency</label>
{{ form.schedule_frequency }}
{% if form.schedule_frequency.errors %}
<div class="text-danger">{{ form.schedule_frequency.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Time</label>
{{ form.schedule_time }}
{% if form.schedule_time.errors %}
<div class="text-danger">{{ form.schedule_time.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="row" id="weekly-options" style="display: none;">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Day of Week</label>
{{ form.schedule_day_of_week }}
{% if form.schedule_day_of_week.errors %}
<div class="text-danger">{{ form.schedule_day_of_week.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="row" id="monthly-options" style="display: none;">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Day of Month</label>
{{ form.schedule_day_of_month }}
{% if form.schedule_day_of_month.errors %}
<div class="text-danger">{{ form.schedule_day_of_month.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="form-check">
{{ form.schedule_is_active }}
<label class="form-check-label" for="{{ form.schedule_is_active.id_for_label }}">
Schedule is active
</label>
</div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Advanced Configuration</h4>
</div>
<div class="panel-body">
<div class="mb-3">
<label class="form-label">Configuration (JSON)</label>
<textarea id="config-editor" name="configuration" class="form-control" rows="10">{{ form.configuration.value|default:"" }}</textarea>
{% if form.configuration.errors %}
<div class="text-danger">{{ form.configuration.errors.0 }}</div>
{% endif %}
<div class="form-text">Additional configuration options in JSON format</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Timeout (seconds)</label>
{{ form.timeout }}
{% if form.timeout.errors %}
<div class="text-danger">{{ form.timeout.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Max Records</label>
{{ form.max_records }}
{% if form.max_records.errors %}
<div class="text-danger">{{ form.max_records.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="form-check mb-3">
{{ form.email_on_completion }}
<label class="form-check-label" for="{{ form.email_on_completion.id_for_label }}">
Send email notification on completion
</label>
</div>
<div class="form-check">
{{ form.email_on_failure }}
<label class="form-check-label" for="{{ form.email_on_failure.id_for_label }}">
Send email notification on failure
</label>
</div>
</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">Form Actions</h4>
</div>
<div class="panel-body">
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary btn-lg">
<i class="fa fa-save me-2"></i>
{% if object %}Update Report{% else %}Create Report{% endif %}
</button>
{% if object %}
<button type="submit" name="save_and_continue" class="btn btn-success">
<i class="fa fa-save me-2"></i>Save & Continue Editing
</button>
{% else %}
<button type="submit" name="save_and_add_another" class="btn btn-info">
<i class="fa fa-plus me-2"></i>Save & Add Another
</button>
{% endif %}
<button type="submit" name="save_and_test" class="btn btn-warning">
<i class="fa fa-play me-2"></i>Save & Test Generate
</button>
<a href="{% if object %}{% url 'analytics:report_detail' object.pk %}{% else %}{% url 'analytics:report_list' %}{% endif %}" class="btn btn-secondary">
<i class="fa fa-times me-2"></i>Cancel
</a>
{% if object %}
<hr>
<a href="{% url 'analytics:report_delete' object.pk %}" class="btn btn-danger">
<i class="fa fa-trash me-2"></i>Delete Report
</a>
{% endif %}
</div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Quick Templates</h4>
</div>
<div class="panel-body">
<div class="d-grid gap-2">
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('patient_summary')">
Patient Summary
</button>
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('financial_report')">
Financial Report
</button>
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('clinical_metrics')">
Clinical Metrics
</button>
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('operational_stats')">
Operational Stats
</button>
</div>
</div>
</div>
<!-- END panel -->
{% if object %}
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Report Information</h4>
</div>
<div class="panel-body">
<table class="table table-borderless">
<tr>
<td class="fw-bold">Created:</td>
<td>{{ object.created_at|date:"M d, Y" }}</td>
</tr>
<tr>
<td class="fw-bold">Last Modified:</td>
<td>{{ object.updated_at|date:"M d, Y H:i" }}</td>
</tr>
<tr>
<td class="fw-bold">Executions:</td>
<td>{{ object.execution_count }}</td>
</tr>
<tr>
<td class="fw-bold">Success Rate:</td>
<td>{{ object.success_rate }}%</td>
</tr>
</table>
</div>
</div>
<!-- END panel -->
{% endif %}
</div>
</div>
</form>
{% endblock %}
{% block js %}
<script src="{% static 'plugins/select2/dist/js/select2.min.js' %}"></script>
<script src="{% static 'plugins/codemirror/lib/codemirror.js' %}"></script>
<script src="{% static 'plugins/codemirror/mode/sql/sql.js' %}"></script>
<script src="{% static 'plugins/codemirror/mode/javascript/javascript.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize Select2
$('.select2').select2({
theme: 'bootstrap-5',
width: '100%'
});
// Initialize CodeMirror for SQL
var sqlEditor = CodeMirror.fromTextArea(document.getElementById('sql-editor'), {
mode: 'text/x-sql',
theme: 'material',
lineNumbers: true,
autoCloseBrackets: true,
matchBrackets: true,
indentWithTabs: true,
smartIndent: true
});
// Initialize CodeMirror for JSON config
var configEditor = CodeMirror.fromTextArea(document.getElementById('config-editor'), {
mode: 'application/json',
theme: 'material',
lineNumbers: true,
autoCloseBrackets: true,
matchBrackets: true,
indentWithTabs: false,
indentUnit: 2
});
// Show/hide schedule panel based on type
function toggleSchedulePanel() {
var type = $('#id_type').val();
if (type === 'SCHEDULED') {
$('#schedule-panel').show();
} else {
$('#schedule-panel').hide();
}
}
// Show/hide schedule options based on frequency
function toggleScheduleOptions() {
var frequency = $('#id_schedule_frequency').val();
$('#weekly-options, #monthly-options').hide();
if (frequency === 'WEEKLY') {
$('#weekly-options').show();
} else if (frequency === 'MONTHLY') {
$('#monthly-options').show();
}
}
// Event handlers
$('#id_type').on('change', toggleSchedulePanel);
$('#id_schedule_frequency').on('change', toggleScheduleOptions);
// Initialize visibility
toggleSchedulePanel();
toggleScheduleOptions();
// Form validation
$('#report-form').on('submit', function(e) {
var isValid = true;
var errors = [];
// Required field validation
if (!$('#id_name').val().trim()) {
errors.push('Report name is required');
isValid = false;
}
if (!sqlEditor.getValue().trim()) {
errors.push('SQL query is required');
isValid = false;
}
// JSON validation for configuration
var configValue = configEditor.getValue().trim();
if (configValue) {
try {
JSON.parse(configValue);
} catch (e) {
errors.push('Configuration must be valid JSON');
isValid = false;
}
}
if (!isValid) {
e.preventDefault();
toastr.error('Please correct the following errors:<br>' + errors.join('<br>'));
}
});
// Update form fields before submission
$('#report-form').on('submit', function() {
$('#id_sql_query').val(sqlEditor.getValue());
$('#id_configuration').val(configEditor.getValue());
});
});
function validateQuery() {
var query = $('.CodeMirror')[0].CodeMirror.getValue();
if (!query.trim()) {
toastr.warning('Please enter a SQL query first');
return;
}
$.ajax({
url: '{% url "analytics:validate_query" %}',
method: 'POST',
data: {
'query': query,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.valid) {
toastr.success('Query is valid');
} else {
toastr.error('Query validation failed: ' + response.error);
}
},
error: function() {
toastr.error('Failed to validate query');
}
});
}
function testQuery() {
var query = $('.CodeMirror')[0].CodeMirror.getValue();
if (!query.trim()) {
toastr.warning('Please enter a SQL query first');
return;
}
$.ajax({
url: '{% url "analytics:test_query" %}',
method: 'POST',
data: {
'query': query,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
beforeSend: function() {
toastr.info('Testing query...');
},
success: function(response) {
if (response.success) {
$('#results-content').html(response.html);
$('#query-results').show();
toastr.success('Query executed successfully. ' + response.record_count + ' records returned.');
} else {
toastr.error('Query test failed: ' + response.error);
}
},
error: function() {
toastr.error('Failed to test query');
}
});
}
function formatQuery() {
var query = $('.CodeMirror')[0].CodeMirror.getValue();
if (!query.trim()) {
toastr.warning('Please enter a SQL query first');
return;
}
// Simple SQL formatting (basic implementation)
var formatted = query
.replace(/\bSELECT\b/gi, '\nSELECT')
.replace(/\bFROM\b/gi, '\nFROM')
.replace(/\bWHERE\b/gi, '\nWHERE')
.replace(/\bGROUP BY\b/gi, '\nGROUP BY')
.replace(/\bORDER BY\b/gi, '\nORDER BY')
.replace(/\bHAVING\b/gi, '\nHAVING')
.replace(/\bJOIN\b/gi, '\nJOIN')
.replace(/\bLEFT JOIN\b/gi, '\nLEFT JOIN')
.replace(/\bRIGHT JOIN\b/gi, '\nRIGHT JOIN')
.replace(/\bINNER JOIN\b/gi, '\nINNER JOIN');
$('.CodeMirror')[0].CodeMirror.setValue(formatted);
toastr.success('Query formatted');
}
function loadTemplate(templateName) {
$.ajax({
url: '{% url "analytics:query_template" %}',
data: {
'template': templateName
},
success: function(response) {
if (response.success) {
$('.CodeMirror')[0].CodeMirror.setValue(response.query);
toastr.success('Template loaded');
} else {
toastr.error('Failed to load template');
}
},
error: function() {
toastr.error('Failed to load template');
}
});
}
</script>
{% endblock %}