-
Notifications
You must be signed in to change notification settings - Fork 1
/
relecensus-cities.go
183 lines (166 loc) · 4.87 KB
/
relecensus-cities.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package apiary
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"github.com/jackc/pgx/v4"
)
// CityMembership gives the membership (and population) statistics for some
// aggregation of denominations in a given year.
type CityMembership struct {
Year int `json:"year"`
Group string `json:"group"`
City string `json:"city"`
State string `json:"state"`
Denominations int `json:"denominations"`
Churches int `json:"churches"`
Members int `json:"members"`
Population1926 NullInt64 `json:"population_1926"`
Lon float64 `json:"lon"`
Lat float64 `json:"lat"`
}
// RelCensusCityMembershipHandler returns the statistics for all the cities for a single
// denomination in a single year. It must be filtered by year and denomination.
func (s *Server) RelCensusCityMembershipHandler() http.HandlerFunc {
queryDenomination := `
SELECT m.year, m.denomination,
c.city, c.state,
1::integer AS denominations,
m.churches, m.members_total,
p.pop_est_1926,
ST_X(c.geometry) AS lon, ST_Y(c.geometry) AS lat
FROM relcensus.membership_city m
LEFT JOIN relcensus.cities_25K c ON m.city = c.city AND m.state = c.state
LEFT JOIN relcensus.popplaces_1926 p ON c.place_id = p.place_id
WHERE year = $1 AND denomination = $2
ORDER BY state, city;
`
queryFamily := `
SELECT
d.year,
d.family_relec,
c.city, c.state,
d.denominations,
d.churches,
d.members_total,
p.pop_est_1926,
ST_X(c.geometry) AS lon, ST_Y(c.geometry) AS lat
FROM
(
SELECT
m.year,
d.family_relec,
m.city, m.state,
count(m.denomination) AS denominations,
sum(m.churches) AS churches,
sum(m.members_total) AS members_total
FROM relcensus.membership_city m
LEFT JOIN relcensus.denominations d ON m.denomination = d.name
WHERE m.year = $1 AND d.family_relec = $2 AND m.churches IS NOT NULL
GROUP BY m.year, d.family_relec, m.city, m.state
) d
LEFT JOIN relcensus.cities_25k c ON d.city = c.city AND d.state = c.state
LEFT JOIN relcensus.popplaces_1926 p ON c.place_id = p.place_id
ORDER BY c.state, c.city;
`
queryAll := `
SELECT
d.year,
'All denominations' AS group,
c.city, c.state,
d.denominations,
d.churches,
d.members_total,
p.pop_est_1926,
ST_X(c.geometry) AS lon, ST_Y(c.geometry) AS lat
FROM
(
SELECT
m.year,
m.city, m.state,
count(m.denomination) AS denominations,
sum(m.churches) AS churches,
sum(m.members_total) AS members_total
FROM relcensus.membership_city m
WHERE m.year = $1
GROUP BY m.year, m.city, m.state
) d
LEFT JOIN relcensus.cities_25k c ON d.city = c.city AND d.state = c.state
LEFT JOIN relcensus.popplaces_1926 p ON c.place_id = p.place_id
ORDER BY c.state, c.city;
`
return func(w http.ResponseWriter, r *http.Request) {
year := r.URL.Query().Get("year")
denomination := r.URL.Query().Get("denomination")
denominationFamily := r.URL.Query().Get("denominationFamily")
// Year must be provided
if year == "" {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
// Year must be an integer
yearInt, err := strconv.Atoi(year)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
// Year must be one of the following
switch yearInt {
case 1906:
case 1916:
case 1926:
case 1936:
default:
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
// Only allow one of denomination or denominationFamily to be set
if denomination != "" && denominationFamily != "" {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
results := make([]CityMembership, 0)
var row CityMembership
var rows pgx.Rows
// We've already done the error checking for the call to the API, so we can
// just use the right query as necessary.
switch {
case denomination != "":
rows, err = s.DB.Query(context.TODO(), queryDenomination, yearInt, denomination)
case denominationFamily != "":
rows, err = s.DB.Query(context.TODO(), queryFamily, yearInt, denominationFamily)
case denomination == "" && denominationFamily == "":
rows, err = s.DB.Query(context.TODO(), queryAll, yearInt)
}
if err != nil {
log.Println(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(
&row.Year,
&row.Group,
&row.City, &row.State,
&row.Denominations, &row.Churches, &row.Members,
&row.Population1926,
&row.Lon, &row.Lat)
if err != nil {
log.Println(err)
log.Println(row)
}
results = append(results, row)
}
err = rows.Err()
if err != nil {
log.Println(err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
response, _ := json.Marshal(results)
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, string(response))
}
}