Files
cpone_dashboard/cpone-dashboard/menu/abnormal/query.go
2026-04-30 14:27:01 +07:00

258 lines
5.9 KiB
Go

package abnormal
import (
"cpone-dashboard/db"
)
type AbnormalSummary struct {
Total int
Abnormal int
Normal int
AbnormalRate int // percentage, 0-100
}
type AgeChartData struct {
Labels []string
Abnormal []int
}
type GenderChartData struct {
Labels []string
Abnormal []int
}
type DeptChartData struct {
Labels []string
Abnormal []int
}
// GetTotalPatients returns total active patients for the project.
func GetTotalPatients(mcuID int) (int, error) {
var total int
err := db.DB.QueryRow(`
SELECT COUNT(*)
FROM mcu_patient
WHERE Mcu_PatientMcuID = ?
AND Mcu_PatientIsActive = 'Y'
`, mcuID).Scan(&total)
return total, err
}
// GetAbnormalCount returns distinct patients with at least one kelainan.
// If group is non-empty, counts only patients with kelainan in that group.
func GetAbnormalCount(mcuID int, group string) (int, error) {
var count int
var err error
if group == "" {
err = db.DB.QueryRow(`
SELECT COUNT(DISTINCT M_PatientID)
FROM kelainan_details
WHERE Mgm_McuID = ?
`, mcuID).Scan(&count)
} else {
err = db.DB.QueryRow(`
SELECT COUNT(DISTINCT M_PatientID)
FROM kelainan_details
WHERE Mgm_McuID = ?
AND Mcu_KelainanGroupSummaryName = ?
`, mcuID, group).Scan(&count)
}
return count, err
}
// GetAbnormalGroups returns distinct kelainan group names for a project.
func GetAbnormalGroups(mcuID int) ([]string, error) {
rows, err := db.DB.Query(`
SELECT DISTINCT Mcu_KelainanGroupSummaryName
FROM kelainan_details
WHERE Mgm_McuID = ?
AND Mcu_KelainanGroupSummaryName IS NOT NULL
AND Mcu_KelainanGroupSummaryName != ''
ORDER BY Mcu_KelainanGroupSummaryName
`, mcuID)
if err != nil {
return nil, err
}
defer rows.Close()
var groups []string
for rows.Next() {
var g string
if err := rows.Scan(&g); err != nil {
continue
}
groups = append(groups, g)
}
return groups, rows.Err()
}
// GetAgeChartData returns abnormal patient counts by age bucket.
func GetAgeChartData(mcuID int, group string) (AgeChartData, error) {
buckets := []string{"<30", "30-39", "40-49", "50+"}
counts := make(map[string]int)
var query string
var args []any
if group == "" {
query = `
SELECT
CASE
WHEN AgePatient < 30 THEN '<30'
WHEN AgePatient < 40 THEN '30-39'
WHEN AgePatient < 50 THEN '40-49'
ELSE '50+'
END AS age_group,
COUNT(DISTINCT M_PatientID) AS cnt
FROM kelainan_details
WHERE Mgm_McuID = ?
AND AgePatient IS NOT NULL
GROUP BY age_group`
args = []any{mcuID}
} else {
query = `
SELECT
CASE
WHEN AgePatient < 30 THEN '<30'
WHEN AgePatient < 40 THEN '30-39'
WHEN AgePatient < 50 THEN '40-49'
ELSE '50+'
END AS age_group,
COUNT(DISTINCT M_PatientID) AS cnt
FROM kelainan_details
WHERE Mgm_McuID = ?
AND Mcu_KelainanGroupSummaryName = ?
AND AgePatient IS NOT NULL
GROUP BY age_group`
args = []any{mcuID, group}
}
rows, err := db.DB.Query(query, args...)
if err != nil {
return AgeChartData{}, err
}
defer rows.Close()
for rows.Next() {
var label string
var cnt int
if err := rows.Scan(&label, &cnt); err != nil {
continue
}
counts[label] = cnt
}
data := AgeChartData{Labels: buckets, Abnormal: make([]int, len(buckets))}
for i, b := range buckets {
data.Abnormal[i] = counts[b]
}
return data, rows.Err()
}
// GetGenderChartData returns abnormal patient counts by gender.
func GetGenderChartData(mcuID int, group string) (GenderChartData, error) {
labels := []string{"Male", "Female"}
counts := make(map[string]int)
var query string
var args []any
if group == "" {
query = `
SELECT
CASE WHEN LOWER(M_PatientGender) = 'male' THEN 'Male' ELSE 'Female' END AS gender,
COUNT(DISTINCT M_PatientID) AS cnt
FROM kelainan_details
WHERE Mgm_McuID = ?
AND M_PatientGender IS NOT NULL
GROUP BY gender`
args = []any{mcuID}
} else {
query = `
SELECT
CASE WHEN LOWER(M_PatientGender) = 'male' THEN 'Male' ELSE 'Female' END AS gender,
COUNT(DISTINCT M_PatientID) AS cnt
FROM kelainan_details
WHERE Mgm_McuID = ?
AND Mcu_KelainanGroupSummaryName = ?
AND M_PatientGender IS NOT NULL
GROUP BY gender`
args = []any{mcuID, group}
}
rows, err := db.DB.Query(query, args...)
if err != nil {
return GenderChartData{}, err
}
defer rows.Close()
for rows.Next() {
var label string
var cnt int
if err := rows.Scan(&label, &cnt); err != nil {
continue
}
counts[label] = cnt
}
data := GenderChartData{Labels: labels, Abnormal: make([]int, len(labels))}
for i, l := range labels {
data.Abnormal[i] = counts[l]
}
return data, rows.Err()
}
// GetDeptChartData returns top-10 departments by abnormal patient count.
func GetDeptChartData(mcuID int, group string) (DeptChartData, error) {
var query string
var args []any
if group == "" {
query = `
SELECT
COALESCE(
NULLIF(TRIM(M_PatientDepartement), ''),
NULLIF(TRIM(M_PatientDivisi), ''),
NULLIF(TRIM(M_PatientPosisi), ''),
'-'
) AS dept,
COUNT(DISTINCT M_PatientID) AS cnt
FROM kelainan_details
WHERE Mgm_McuID = ?
GROUP BY dept
ORDER BY cnt DESC
LIMIT 10`
args = []any{mcuID}
} else {
query = `
SELECT
COALESCE(
NULLIF(TRIM(M_PatientDepartement), ''),
NULLIF(TRIM(M_PatientDivisi), ''),
NULLIF(TRIM(M_PatientPosisi), ''),
'-'
) AS dept,
COUNT(DISTINCT M_PatientID) AS cnt
FROM kelainan_details
WHERE Mgm_McuID = ?
AND Mcu_KelainanGroupSummaryName = ?
GROUP BY dept
ORDER BY cnt DESC
LIMIT 10`
args = []any{mcuID, group}
}
rows, err := db.DB.Query(query, args...)
if err != nil {
return DeptChartData{}, err
}
defer rows.Close()
var data DeptChartData
for rows.Next() {
var label string
var cnt int
if err := rows.Scan(&label, &cnt); err != nil {
continue
}
data.Labels = append(data.Labels, label)
data.Abnormal = append(data.Abnormal, cnt)
}
return data, rows.Err()
}