258 lines
5.9 KiB
Go
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()
|
|
}
|