Skip to content

Commit 884e831

Browse files
committed
feat: restructure province COVID-19 endpoints with nested JSON format
- Add new ProvinceCaseResponse DTOs with nested daily/cumulative/statistics structure - Transform Indonesian field names to English equivalents (hari_ke->day, kasus_baru->daily, etc.) - Update province endpoints to use new transformation logic - Include province-specific fields (PersonUnderObservation, PersonUnderSupervision) - Add comprehensive unit tests for transformation functions - Maintain backward compatibility by preserving province information in response - Follow same pattern as national endpoints for consistency
1 parent 2eb9d0e commit 884e831

File tree

3 files changed

+603
-4
lines changed

3 files changed

+603
-4
lines changed

internal/handler/covid_handler.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ func (h *CovidHandler) GetProvinceCases(w http.ResponseWriter, r *http.Request)
9090
writeErrorResponse(w, http.StatusInternalServerError, err.Error())
9191
return
9292
}
93-
writeSuccessResponse(w, cases)
93+
// Transform to new response structure
94+
responseData := models.TransformProvinceCaseSliceToResponse(cases)
95+
writeSuccessResponse(w, responseData)
9496
return
9597
}
9698

@@ -99,7 +101,9 @@ func (h *CovidHandler) GetProvinceCases(w http.ResponseWriter, r *http.Request)
99101
writeErrorResponse(w, http.StatusInternalServerError, err.Error())
100102
return
101103
}
102-
writeSuccessResponse(w, cases)
104+
// Transform to new response structure
105+
responseData := models.TransformProvinceCaseSliceToResponse(cases)
106+
writeSuccessResponse(w, responseData)
103107
return
104108
}
105109

@@ -112,7 +116,9 @@ func (h *CovidHandler) GetProvinceCases(w http.ResponseWriter, r *http.Request)
112116
writeErrorResponse(w, http.StatusInternalServerError, err.Error())
113117
return
114118
}
115-
writeSuccessResponse(w, cases)
119+
// Transform to new response structure
120+
responseData := models.TransformProvinceCaseSliceToResponse(cases)
121+
writeSuccessResponse(w, responseData)
116122
return
117123
}
118124

@@ -122,7 +128,9 @@ func (h *CovidHandler) GetProvinceCases(w http.ResponseWriter, r *http.Request)
122128
return
123129
}
124130

125-
writeSuccessResponse(w, cases)
131+
// Transform to new response structure
132+
responseData := models.TransformProvinceCaseSliceToResponse(cases)
133+
writeSuccessResponse(w, responseData)
126134
}
127135

128136
func (h *CovidHandler) HealthCheck(w http.ResponseWriter, r *http.Request) {
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package models
2+
3+
import "time"
4+
5+
// ProvinceCaseResponse represents the structured response for province COVID-19 case data
6+
type ProvinceCaseResponse struct {
7+
Day int64 `json:"day"`
8+
Date time.Time `json:"date"`
9+
Daily ProvinceDailyCases `json:"daily"`
10+
Cumulative ProvinceCumulativeCases `json:"cumulative"`
11+
Statistics ProvinceCaseStatistics `json:"statistics"`
12+
Province *Province `json:"province,omitempty"`
13+
}
14+
15+
// ProvinceDailyCases represents new cases for a single day in a province
16+
type ProvinceDailyCases struct {
17+
Positive int64 `json:"positive"`
18+
Recovered int64 `json:"recovered"`
19+
Deceased int64 `json:"deceased"`
20+
Active int64 `json:"active"`
21+
PersonUnderObservation int64 `json:"person_under_observation"`
22+
FinishedPersonUnderObservation int64 `json:"finished_person_under_observation"`
23+
PersonUnderSupervision int64 `json:"person_under_supervision"`
24+
FinishedPersonUnderSupervision int64 `json:"finished_person_under_supervision"`
25+
}
26+
27+
// ProvinceCumulativeCases represents total cases accumulated over time in a province
28+
type ProvinceCumulativeCases struct {
29+
Positive int64 `json:"positive"`
30+
Recovered int64 `json:"recovered"`
31+
Deceased int64 `json:"deceased"`
32+
Active int64 `json:"active"`
33+
PersonUnderObservation int64 `json:"person_under_observation"`
34+
ActivePersonUnderObservation int64 `json:"active_person_under_observation"`
35+
FinishedPersonUnderObservation int64 `json:"finished_person_under_observation"`
36+
PersonUnderSupervision int64 `json:"person_under_supervision"`
37+
ActivePersonUnderSupervision int64 `json:"active_person_under_supervision"`
38+
FinishedPersonUnderSupervision int64 `json:"finished_person_under_supervision"`
39+
}
40+
41+
// ProvinceCaseStatistics contains calculated statistics and metrics for province data
42+
type ProvinceCaseStatistics struct {
43+
Percentages CasePercentages `json:"percentages"`
44+
ReproductionRate *ReproductionRate `json:"reproduction_rate,omitempty"`
45+
}
46+
47+
// TransformToResponse converts a ProvinceCase model to the response format
48+
func (pc *ProvinceCase) TransformToResponse(date time.Time) ProvinceCaseResponse {
49+
// Calculate active cases
50+
dailyActive := pc.Positive - pc.Recovered - pc.Deceased
51+
cumulativeActive := pc.CumulativePositive - pc.CumulativeRecovered - pc.CumulativeDeceased
52+
53+
// Calculate active under observation and supervision
54+
activePersonUnderObservation := pc.CumulativePersonUnderObservation - pc.CumulativeFinishedPersonUnderObservation
55+
activePersonUnderSupervision := pc.CumulativePersonUnderSupervision - pc.CumulativeFinishedPersonUnderSupervision
56+
57+
// Build response
58+
response := ProvinceCaseResponse{
59+
Day: pc.Day,
60+
Date: date,
61+
Daily: ProvinceDailyCases{
62+
Positive: pc.Positive,
63+
Recovered: pc.Recovered,
64+
Deceased: pc.Deceased,
65+
Active: dailyActive,
66+
PersonUnderObservation: pc.PersonUnderObservation,
67+
FinishedPersonUnderObservation: pc.FinishedPersonUnderObservation,
68+
PersonUnderSupervision: pc.PersonUnderSupervision,
69+
FinishedPersonUnderSupervision: pc.FinishedPersonUnderSupervision,
70+
},
71+
Cumulative: ProvinceCumulativeCases{
72+
Positive: pc.CumulativePositive,
73+
Recovered: pc.CumulativeRecovered,
74+
Deceased: pc.CumulativeDeceased,
75+
Active: cumulativeActive,
76+
PersonUnderObservation: pc.CumulativePersonUnderObservation,
77+
ActivePersonUnderObservation: activePersonUnderObservation,
78+
FinishedPersonUnderObservation: pc.CumulativeFinishedPersonUnderObservation,
79+
PersonUnderSupervision: pc.CumulativePersonUnderSupervision,
80+
ActivePersonUnderSupervision: activePersonUnderSupervision,
81+
FinishedPersonUnderSupervision: pc.CumulativeFinishedPersonUnderSupervision,
82+
},
83+
Statistics: ProvinceCaseStatistics{
84+
Percentages: calculatePercentages(pc.CumulativePositive, pc.CumulativeRecovered, pc.CumulativeDeceased, cumulativeActive),
85+
},
86+
Province: pc.Province,
87+
}
88+
89+
// Add reproduction rate if available
90+
if pc.Rt != nil && pc.RtUpper != nil && pc.RtLower != nil {
91+
response.Statistics.ReproductionRate = &ReproductionRate{
92+
Value: *pc.Rt,
93+
UpperBound: *pc.RtUpper,
94+
LowerBound: *pc.RtLower,
95+
}
96+
}
97+
98+
return response
99+
}
100+
101+
// TransformProvinceCaseWithDateToResponse converts a ProvinceCaseWithDate model to the response format
102+
func (pcd *ProvinceCaseWithDate) TransformToResponse() ProvinceCaseResponse {
103+
return pcd.ProvinceCase.TransformToResponse(pcd.Date)
104+
}
105+
106+
// TransformProvinceCaseSliceToResponse converts a slice of ProvinceCaseWithDate models to response format
107+
func TransformProvinceCaseSliceToResponse(cases []ProvinceCaseWithDate) []ProvinceCaseResponse {
108+
responses := make([]ProvinceCaseResponse, len(cases))
109+
for i, c := range cases {
110+
responses[i] = c.TransformToResponse()
111+
}
112+
return responses
113+
}

0 commit comments

Comments
 (0)