557 lines
25 KiB
HTML
557 lines
25 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Pharmacy Statistics - Pharmacy{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="content">
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h4>Pharmacy Statistics Dashboard</h4>
|
|
<h6>Comprehensive pharmacy operations analytics and performance metrics</h6>
|
|
</div>
|
|
<div class="page-btn">
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fa fa-download"></i> Export Reports
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#"><i class="fa fa-file-pdf"></i> PDF Report</a></li>
|
|
<li><a class="dropdown-item" href="#"><i class="fa fa-file-excel"></i> Excel Report</a></li>
|
|
<li><a class="dropdown-item" href="#"><i class="fa fa-file-csv"></i> CSV Data</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Key Performance Indicators -->
|
|
<div class="row">
|
|
<div class="col-xl-3 col-sm-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="dash-widget-header">
|
|
<span class="dash-widget-icon text-primary border-primary">
|
|
<i class="fa fa-pills"></i>
|
|
</span>
|
|
<div class="dash-count">
|
|
<h3>{{ stats.prescriptions_today|default:0 }}</h3>
|
|
</div>
|
|
</div>
|
|
<div class="dash-widget-info">
|
|
<h6 class="text-muted">Prescriptions Today</h6>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-primary" style="width: {{ stats.prescriptions_progress|default:0 }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-sm-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="dash-widget-header">
|
|
<span class="dash-widget-icon text-success border-success">
|
|
<i class="fa fa-check-circle"></i>
|
|
</span>
|
|
<div class="dash-count">
|
|
<h3>{{ stats.dispensed_today|default:0 }}</h3>
|
|
</div>
|
|
</div>
|
|
<div class="dash-widget-info">
|
|
<h6 class="text-muted">Medications Dispensed</h6>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-success" style="width: {{ stats.dispensed_progress|default:0 }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-sm-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="dash-widget-header">
|
|
<span class="dash-widget-icon text-warning border-warning">
|
|
<i class="fa fa-exclamation-triangle"></i>
|
|
</span>
|
|
<div class="dash-count">
|
|
<h3>{{ stats.low_stock_items|default:0 }}</h3>
|
|
</div>
|
|
</div>
|
|
<div class="dash-widget-info">
|
|
<h6 class="text-muted">Low Stock Alerts</h6>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-warning" style="width: {{ stats.stock_alert_level|default:0 }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-sm-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="dash-widget-header">
|
|
<span class="dash-widget-icon text-info border-info">
|
|
<i class="fa fa-dollar-sign"></i>
|
|
</span>
|
|
<div class="dash-count">
|
|
<h3>${{ stats.revenue_today|default:0|floatformat:0 }}</h3>
|
|
</div>
|
|
</div>
|
|
<div class="dash-widget-info">
|
|
<h6 class="text-muted">Revenue Today</h6>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-info" style="width: {{ stats.revenue_progress|default:0 }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Time Period Selector -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Analytics Period</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="get" class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Time Period</label>
|
|
<select name="period" class="form-select">
|
|
<option value="today" {% if request.GET.period == 'today' %}selected{% endif %}>Today</option>
|
|
<option value="week" {% if request.GET.period == 'week' %}selected{% endif %}>This Week</option>
|
|
<option value="month" {% if request.GET.period == 'month' %}selected{% endif %}>This Month</option>
|
|
<option value="quarter" {% if request.GET.period == 'quarter' %}selected{% endif %}>This Quarter</option>
|
|
<option value="year" {% if request.GET.period == 'year' %}selected{% endif %}>This Year</option>
|
|
<option value="custom" {% if request.GET.period == 'custom' %}selected{% endif %}>Custom Range</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Start Date</label>
|
|
<input type="date" name="start_date" class="form-control" value="{{ request.GET.start_date }}">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">End Date</label>
|
|
<input type="date" name="end_date" class="form-control" value="{{ request.GET.end_date }}">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label"> </label>
|
|
<button type="submit" class="btn btn-primary d-block">
|
|
<i class="fa fa-search"></i> Update Analytics
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts Row -->
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Prescription Trends</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="prescriptionTrendsChart" height="300"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Top Medications</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="topMedicationsChart" height="300"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Detailed Statistics -->
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Pharmacy Performance Metrics</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Metric</th>
|
|
<th>Current Period</th>
|
|
<th>Previous Period</th>
|
|
<th>Change</th>
|
|
<th>Target</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><strong>Total Prescriptions</strong></td>
|
|
<td>{{ metrics.total_prescriptions|default:0 }}</td>
|
|
<td>{{ metrics.prev_total_prescriptions|default:0 }}</td>
|
|
<td>
|
|
<span class="badge bg-{{ metrics.prescriptions_trend_color }}">
|
|
{{ metrics.prescriptions_change|default:0 }}%
|
|
</span>
|
|
</td>
|
|
<td>{{ metrics.prescriptions_target|default:0 }}</td>
|
|
<td>
|
|
<span class="badge bg-{{ metrics.prescriptions_status_color }}">
|
|
{{ metrics.prescriptions_status|default:"On Track" }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Dispensing Accuracy</strong></td>
|
|
<td>{{ metrics.dispensing_accuracy|default:99.5 }}%</td>
|
|
<td>{{ metrics.prev_dispensing_accuracy|default:99.2 }}%</td>
|
|
<td>
|
|
<span class="badge bg-{{ metrics.accuracy_trend_color }}">
|
|
+{{ metrics.accuracy_change|default:0.3 }}%
|
|
</span>
|
|
</td>
|
|
<td>99.0%</td>
|
|
<td>
|
|
<span class="badge bg-success">Excellent</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Average Wait Time</strong></td>
|
|
<td>{{ metrics.avg_wait_time|default:12 }} min</td>
|
|
<td>{{ metrics.prev_avg_wait_time|default:15 }} min</td>
|
|
<td>
|
|
<span class="badge bg-success">-3 min</span>
|
|
</td>
|
|
<td>15 min</td>
|
|
<td>
|
|
<span class="badge bg-success">Good</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Stock Turnover Rate</strong></td>
|
|
<td>{{ metrics.stock_turnover|default:8.5 }}x</td>
|
|
<td>{{ metrics.prev_stock_turnover|default:7.8 }}x</td>
|
|
<td>
|
|
<span class="badge bg-success">+0.7x</span>
|
|
</td>
|
|
<td>8.0x</td>
|
|
<td>
|
|
<span class="badge bg-success">Excellent</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Revenue per Prescription</strong></td>
|
|
<td>${{ metrics.revenue_per_rx|default:45.50 }}</td>
|
|
<td>${{ metrics.prev_revenue_per_rx|default:42.30 }}</td>
|
|
<td>
|
|
<span class="badge bg-success">+$3.20</span>
|
|
</td>
|
|
<td>${{ metrics.revenue_target|default:40.00 }}</td>
|
|
<td>
|
|
<span class="badge bg-success">Above Target</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Prescription Categories</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="prescriptionCategoriesChart" height="250"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inventory and Financial Analytics -->
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Inventory Status</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-success">{{ inventory.in_stock|default:1250 }}</h4>
|
|
<p class="text-muted">Items In Stock</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-warning">{{ inventory.low_stock|default:45 }}</h4>
|
|
<p class="text-muted">Low Stock Items</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-danger">{{ inventory.out_of_stock|default:8 }}</h4>
|
|
<p class="text-muted">Out of Stock</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-info">{{ inventory.expiring_soon|default:23 }}</h4>
|
|
<p class="text-muted">Expiring Soon</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<canvas id="inventoryStatusChart" height="150"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Financial Summary</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-primary">${{ financial.total_revenue|default:125000|floatformat:0 }}</h4>
|
|
<p class="text-muted">Total Revenue</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-success">${{ financial.gross_profit|default:45000|floatformat:0 }}</h4>
|
|
<p class="text-muted">Gross Profit</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-info">${{ financial.cost_of_goods|default:80000|floatformat:0 }}</h4>
|
|
<p class="text-muted">Cost of Goods</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-warning">{{ financial.profit_margin|default:36 }}%</h4>
|
|
<p class="text-muted">Profit Margin</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<canvas id="revenueChart" height="150"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Activity -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Recent Pharmacy Activity</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Time</th>
|
|
<th>Activity</th>
|
|
<th>Patient/Item</th>
|
|
<th>Pharmacist</th>
|
|
<th>Status</th>
|
|
<th>Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for activity in recent_activities %}
|
|
<tr>
|
|
<td>{{ activity.timestamp|date:"H:i" }}</td>
|
|
<td>{{ activity.activity_type|title }}</td>
|
|
<td>{{ activity.description }}</td>
|
|
<td>{{ activity.pharmacist.get_full_name|default:"System" }}</td>
|
|
<td>
|
|
<span class="badge bg-{{ activity.status_color }}">{{ activity.status|title }}</span>
|
|
</td>
|
|
<td>
|
|
{% if activity.amount %}
|
|
${{ activity.amount|floatformat:2 }}
|
|
{% else %}
|
|
-
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="6" class="text-center text-muted">No recent activity</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Prescription Trends Chart
|
|
const prescriptionCtx = document.getElementById('prescriptionTrendsChart').getContext('2d');
|
|
new Chart(prescriptionCtx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
|
datasets: [{
|
|
label: 'Prescriptions',
|
|
data: [45, 52, 48, 61, 55, 42, 38],
|
|
borderColor: 'rgb(75, 192, 192)',
|
|
backgroundColor: 'rgba(75, 192, 192, 0.1)',
|
|
tension: 0.1
|
|
}, {
|
|
label: 'Dispensed',
|
|
data: [42, 49, 45, 58, 52, 39, 35],
|
|
borderColor: 'rgb(54, 162, 235)',
|
|
backgroundColor: 'rgba(54, 162, 235, 0.1)',
|
|
tension: 0.1
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Top Medications Chart
|
|
const topMedsCtx = document.getElementById('topMedicationsChart').getContext('2d');
|
|
new Chart(topMedsCtx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: ['Lisinopril', 'Metformin', 'Amlodipine', 'Atorvastatin', 'Omeprazole'],
|
|
datasets: [{
|
|
label: 'Prescriptions',
|
|
data: [85, 72, 68, 61, 55],
|
|
backgroundColor: [
|
|
'rgba(255, 99, 132, 0.8)',
|
|
'rgba(54, 162, 235, 0.8)',
|
|
'rgba(255, 205, 86, 0.8)',
|
|
'rgba(75, 192, 192, 0.8)',
|
|
'rgba(153, 102, 255, 0.8)'
|
|
]
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Prescription Categories Chart
|
|
const categoriesCtx = document.getElementById('prescriptionCategoriesChart').getContext('2d');
|
|
new Chart(categoriesCtx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: ['Cardiovascular', 'Diabetes', 'Antibiotics', 'Pain Management', 'Mental Health'],
|
|
datasets: [{
|
|
data: [30, 25, 20, 15, 10],
|
|
backgroundColor: [
|
|
'#FF6384',
|
|
'#36A2EB',
|
|
'#FFCE56',
|
|
'#4BC0C0',
|
|
'#9966FF'
|
|
]
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false
|
|
}
|
|
});
|
|
|
|
// Inventory Status Chart
|
|
const inventoryCtx = document.getElementById('inventoryStatusChart').getContext('2d');
|
|
new Chart(inventoryCtx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: ['In Stock', 'Low Stock', 'Out of Stock', 'Expiring'],
|
|
datasets: [{
|
|
data: [{{ inventory.in_stock|default:1250 }}, {{ inventory.low_stock|default:45 }}, {{ inventory.out_of_stock|default:8 }}, {{ inventory.expiring_soon|default:23 }}],
|
|
backgroundColor: ['#28a745', '#ffc107', '#dc3545', '#17a2b8']
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false
|
|
}
|
|
});
|
|
|
|
// Revenue Chart
|
|
const revenueCtx = document.getElementById('revenueChart').getContext('2d');
|
|
new Chart(revenueCtx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'],
|
|
datasets: [{
|
|
label: 'Revenue',
|
|
data: [28000, 32000, 31000, 34000],
|
|
borderColor: '#007bff',
|
|
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
|
tension: 0.1
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
ticks: {
|
|
callback: function(value) {
|
|
return '$' + value.toLocaleString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Auto-refresh every 5 minutes
|
|
setInterval(function() {
|
|
location.reload();
|
|
}, 300000);
|
|
</script>
|
|
{% endblock %}
|
|
|