455 lines
18 KiB
PHP
455 lines
18 KiB
PHP
<?php
|
|
require_once __DIR__ . '/db/config.php';
|
|
|
|
function get_unique_members($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT COUNT(DISTINCT m.id) AS unique_members
|
|
FROM members m
|
|
JOIN attendance a ON m.id = a.member_id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
function get_unique_members_drilldown($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT m.name, m.email, m.join_date, MAX(a.attendance_date) as last_visit
|
|
FROM members m
|
|
JOIN attendance a ON m.id = a.member_id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY m.id, m.name, m.email, m.join_date
|
|
ORDER BY m.name
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
if (isset($_GET['kpi'])) {
|
|
header('Content-Type: application/json');
|
|
$community_id = $_GET['community_id'] ?? 1;
|
|
$start_date = $_GET['start_date'] ?? date('Y-m-01');
|
|
$end_date = $_GET['end_date'] ?? date('Y-m-t');
|
|
|
|
switch ($_GET['kpi']) {
|
|
case 'unique_members':
|
|
echo json_encode(get_unique_members($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'unique_members_drilldown':
|
|
echo json_encode(get_unique_members_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'avg_class_attendance':
|
|
echo json_encode(get_avg_class_attendance($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'avg_class_attendance_drilldown':
|
|
echo json_encode(get_avg_class_attendance_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'classes_lightly_attended':
|
|
echo json_encode(get_classes_lightly_attended($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'classes_lightly_attended_drilldown':
|
|
echo json_encode(get_classes_lightly_attended_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'attendance_trend':
|
|
echo json_encode(get_attendance_trend($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'avg_visits_per_member':
|
|
echo json_encode(get_avg_visits_per_member($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'avg_visits_per_member_drilldown':
|
|
echo json_encode(get_avg_visits_per_member_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'high_frequency_members':
|
|
echo json_encode(get_high_frequency_members($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'high_frequency_members_drilldown':
|
|
echo json_encode(get_high_frequency_members_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'total_revenue':
|
|
echo json_encode(get_total_revenue($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'total_revenue_drilldown':
|
|
echo json_encode(get_total_revenue_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'revenue_per_active_member':
|
|
echo json_encode(get_revenue_per_active_member($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'revenue_per_active_member_drilldown':
|
|
echo json_encode(get_revenue_per_active_member_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'inactive_members':
|
|
echo json_encode(get_inactive_members($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'inactive_members_drilldown':
|
|
echo json_encode(get_inactive_members_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'visits_dropped':
|
|
echo json_encode(get_visits_dropped($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'attendance_trend_drilldown':
|
|
echo json_encode(get_attendance_trend_drilldown($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'attendance_over_time':
|
|
echo json_encode(get_attendance_over_time($community_id, $start_date, $end_date));
|
|
break;
|
|
case 'visit_frequency_distribution':
|
|
echo json_encode(get_visit_frequency_distribution($community_id, $start_date, $end_date));
|
|
break;
|
|
}
|
|
exit;
|
|
}
|
|
|
|
function get_avg_class_attendance($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT AVG(daily_attendance) as avg_attendance
|
|
FROM (
|
|
SELECT COUNT(member_id) as daily_attendance
|
|
FROM attendance a
|
|
JOIN members m ON a.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY a.class_id, a.attendance_date
|
|
) as daily_counts
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
$result = $stmt->fetch();
|
|
return ['avg_class_attendance' => round($result['avg_attendance'], 2)];
|
|
}
|
|
|
|
function get_avg_class_attendance_drilldown($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT c.name as class_name, DATE(a.attendance_date) as date, COUNT(a.member_id) as attendance_count
|
|
FROM attendance a
|
|
JOIN classes c ON a.class_id = c.id
|
|
JOIN members m ON a.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY c.name, DATE(a.attendance_date)
|
|
ORDER BY date, class_name
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_classes_lightly_attended($community_id, $start_date, $end_date, $threshold = 5) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT (CAST(SUM(CASE WHEN attendance_count < ? THEN 1 ELSE 0 END) AS DECIMAL(10,2)) / COUNT(*)) * 100 as percentage
|
|
FROM (
|
|
SELECT COUNT(a.member_id) as attendance_count
|
|
FROM attendance a
|
|
JOIN members m ON a.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY a.class_id, a.attendance_date
|
|
) as class_counts
|
|
""");
|
|
$stmt->execute([$threshold, $community_id, $start_date, $end_date]);
|
|
$result = $stmt->fetch();
|
|
return ['classes_lightly_attended' => round($result['percentage'], 2)];
|
|
}
|
|
|
|
function get_classes_lightly_attended_drilldown($community_id, $start_date, $end_date, $threshold = 5) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT c.name as class_name, DATE(a.attendance_date) as date, COUNT(a.member_id) as attendance_count
|
|
FROM attendance a
|
|
JOIN classes c ON a.class_id = c.id
|
|
JOIN members m ON a.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY c.name, DATE(a.attendance_date)
|
|
HAVING COUNT(a.member_id) < ?
|
|
ORDER BY date, class_name
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date, $threshold]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_attendance_trend($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
|
|
$current_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
|
|
$current_period_stmt->execute([$community_id, $start_date, $end_date]);
|
|
$current_period_count = $current_period_stmt->fetchColumn();
|
|
|
|
$period_days = (new DateTime($end_date))->diff(new DateTime($start_date))->days;
|
|
$previous_start_date = (new DateTime($start_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
|
|
$previous_end_date = (new DateTime($end_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
|
|
|
|
$previous_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
|
|
$previous_period_stmt->execute([$community_id, $previous_start_date, $previous_end_date]);
|
|
$previous_period_count = $previous_period_stmt->fetchColumn();
|
|
|
|
if ($previous_period_count == 0) {
|
|
$trend = $current_period_count > 0 ? 100 : 0;
|
|
} else {
|
|
$trend = (($current_period_count - $previous_period_count) / $previous_period_count) * 100;
|
|
}
|
|
|
|
return ['attendance_trend' => round($trend, 2)];
|
|
}
|
|
|
|
function get_avg_visits_per_member($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT AVG(visit_count) as avg_visits
|
|
FROM (
|
|
SELECT COUNT(a.id) as visit_count
|
|
FROM members m
|
|
JOIN attendance a ON m.id = a.member_id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY m.id
|
|
) as member_visits
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
$result = $stmt->fetch();
|
|
return ['avg_visits_per_member' => round($result['avg_visits'], 2)];
|
|
}
|
|
|
|
function get_avg_visits_per_member_drilldown($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT m.name, m.email, COUNT(a.id) as visit_count
|
|
FROM members m
|
|
JOIN attendance a ON m.id = a.member_id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY m.id, m.name, m.email
|
|
ORDER BY visit_count DESC
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_high_frequency_members($community_id, $start_date, $end_date, $frequency_threshold = 5) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT (CAST(SUM(CASE WHEN visit_count >= ? THEN 1 ELSE 0 END) AS DECIMAL(10,2)) / COUNT(*)) * 100 as percentage
|
|
FROM (
|
|
SELECT COUNT(a.id) as visit_count
|
|
FROM members m
|
|
JOIN attendance a ON m.id = a.member_id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY m.id
|
|
) as member_visits
|
|
""");
|
|
$stmt->execute([$frequency_threshold, $community_id, $start_date, $end_date]);
|
|
$result = $stmt->fetch();
|
|
return ['high_frequency_members' => round($result['percentage'], 2)];
|
|
}
|
|
|
|
function get_high_frequency_members_drilldown($community_id, $start_date, $end_date, $frequency_threshold = 5) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT m.name, m.email, COUNT(a.id) as visit_count
|
|
FROM members m
|
|
JOIN attendance a ON m.id = a.member_id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY m.id, m.name, m.email
|
|
HAVING visit_count >= ?
|
|
ORDER BY visit_count DESC
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date, $frequency_threshold]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_total_revenue($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT SUM(r.amount) as total_revenue
|
|
FROM revenue r
|
|
JOIN members m ON r.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND r.transaction_date BETWEEN ? AND ?
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
$result = $stmt->fetch();
|
|
return ['total_revenue' => round($result['total_revenue'], 2)];
|
|
}
|
|
|
|
function get_total_revenue_drilldown($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT m.name, m.email, r.amount, r.service_category, r.transaction_date
|
|
FROM revenue r
|
|
JOIN members m ON r.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND r.transaction_date BETWEEN ? AND ?
|
|
ORDER BY r.transaction_date DESC
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_revenue_per_active_member($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT SUM(r.amount) / COUNT(DISTINCT r.member_id) as revenue_per_active_member
|
|
FROM revenue r
|
|
JOIN members m ON r.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND r.transaction_date BETWEEN ? AND ?
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
$result = $stmt->fetch();
|
|
return ['revenue_per_active_member' => round($result['revenue_per_active_member'], 2)];
|
|
}
|
|
|
|
function get_revenue_per_active_member_drilldown($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT m.name, m.email, SUM(r.amount) as total_revenue, COUNT(r.id) as transaction_count
|
|
FROM revenue r
|
|
JOIN members m ON r.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND r.transaction_date BETWEEN ? AND ?
|
|
GROUP BY m.id, m.name, m.email
|
|
ORDER BY total_revenue DESC
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_inactive_members($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT
|
|
(1 - (COUNT(DISTINCT a.member_id) / COUNT(DISTINCT m.id))) * 100 as inactive_percentage
|
|
FROM members m
|
|
LEFT JOIN attendance a ON m.id = a.member_id AND a.attendance_date BETWEEN ? AND ?
|
|
WHERE m.community_id = ?
|
|
""");
|
|
$stmt->execute([$start_date, $end_date, $community_id]);
|
|
$result = $stmt->fetch();
|
|
return ['inactive_members' => round($result['inactive_percentage'], 2)];
|
|
}
|
|
|
|
function get_inactive_members_drilldown($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT m.name, m.email, m.join_date
|
|
FROM members m
|
|
LEFT JOIN attendance a ON m.id = a.member_id AND a.attendance_date BETWEEN ? AND ?
|
|
WHERE m.community_id = ?
|
|
GROUP BY m.id
|
|
HAVING COUNT(a.id) = 0
|
|
""");
|
|
$stmt->execute([$start_date, $end_date, $community_id]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_visits_dropped($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
|
|
$current_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
|
|
$current_period_stmt->execute([$community_id, $start_date, $end_date]);
|
|
$current_period_count = $current_period_stmt->fetchColumn();
|
|
|
|
$period_days = (new DateTime($end_date))->diff(new DateTime($start_date))->days;
|
|
$previous_start_date = (new DateTime($start_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
|
|
$previous_end_date = (new DateTime($end_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
|
|
|
|
$previous_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
|
|
$previous_period_stmt->execute([$community_id, $previous_start_date, $previous_end_date]);
|
|
$previous_period_count = $previous_period_stmt->fetchColumn();
|
|
|
|
if ($previous_period_count == 0) {
|
|
$trend = $current_period_count > 0 ? 100 : 0;
|
|
} else {
|
|
$trend = (($current_period_count - $previous_period_count) / $previous_period_count) * 100;
|
|
}
|
|
|
|
return ['visits_dropped' => round($trend, 2)];
|
|
}
|
|
|
|
function get_attendance_trend_drilldown($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
|
|
$current_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
|
|
$current_period_stmt->execute([$community_id, $start_date, $end_date]);
|
|
$current_period_count = $current_period_stmt->fetchColumn();
|
|
|
|
$period_days = (new DateTime($end_date))->diff(new DateTime($start_date))->days;
|
|
$previous_start_date = (new DateTime($start_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
|
|
$previous_end_date = (new DateTime($end_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
|
|
|
|
$previous_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
|
|
$previous_period_stmt->execute([$community_id, $previous_start_date, $previous_end_date]);
|
|
$previous_period_count = $previous_period_stmt->fetchColumn();
|
|
|
|
return [
|
|
['period' => 'Current Period', 'visits' => $current_period_count],
|
|
['period' => 'Previous Period', 'visits' => $previous_period_count]
|
|
];
|
|
}
|
|
|
|
function get_attendance_over_time($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT DATE(a.attendance_date) as date, COUNT(a.id) as attendance_count
|
|
FROM attendance a
|
|
JOIN members m ON a.member_id = m.id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY DATE(a.attendance_date)
|
|
ORDER BY date
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
$data = $stmt->fetchAll();
|
|
|
|
$labels = [];
|
|
$values = [];
|
|
foreach ($data as $row) {
|
|
$labels[] = $row['date'];
|
|
$values[] = $row['attendance_count'];
|
|
}
|
|
|
|
return ['labels' => $labels, 'data' => $values];
|
|
}
|
|
|
|
function get_visit_frequency_distribution($community_id, $start_date, $end_date) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("""
|
|
SELECT
|
|
CASE
|
|
WHEN visit_count = 1 THEN '1 visit'
|
|
WHEN visit_count BETWEEN 2 AND 4 THEN '2-4 visits'
|
|
WHEN visit_count BETWEEN 5 AND 9 THEN '5-9 visits'
|
|
ELSE '10+ visits'
|
|
END as frequency_bucket,
|
|
COUNT(*) as member_count
|
|
FROM (
|
|
SELECT COUNT(a.id) as visit_count
|
|
FROM members m
|
|
JOIN attendance a ON m.id = a.member_id
|
|
WHERE m.community_id = ?
|
|
AND a.attendance_date BETWEEN ? AND ?
|
|
GROUP BY m.id
|
|
) as member_visits
|
|
GROUP BY frequency_bucket
|
|
""");
|
|
$stmt->execute([$community_id, $start_date, $end_date]);
|
|
$data = $stmt->fetchAll();
|
|
|
|
$labels = ['1 visit', '2-4 visits', '5-9 visits', '10+ visits'];
|
|
$values = [0, 0, 0, 0];
|
|
foreach ($data as $row) {
|
|
$index = array_search($row['frequency_bucket'], $labels);
|
|
if ($index !== false) {
|
|
$values[$index] = $row['member_count'];
|
|
}
|
|
}
|
|
|
|
return ['labels' => $labels, 'data' => $values];
|
|
}
|