Browse Source

first commit

Chris Mahoney 4 months ago
commit
eebe6971ec
6 changed files with 329 additions and 0 deletions
  1. 114
    0
      api.go
  2. 8
    0
      go.mod
  3. 4
    0
      go.sum
  4. 20
    0
      main.go
  5. BIN
      soups
  6. 183
    0
      sql.go

+ 114
- 0
api.go View File

@@ -0,0 +1,114 @@
1
+package main
2
+
3
+import (
4
+	"net/http"
5
+	"github.com/gorilla/mux"
6
+	"fmt"
7
+	"encoding/json"
8
+)
9
+
10
+type SpeciesResp struct {
11
+	Name string
12
+	FirstFound int
13
+	LastFound int
14
+	Population int
15
+	Examples []string
16
+	Hashes []string
17
+}
18
+
19
+// Returns first and last found of species, a list of "example soups" for the user to see in browser and the complete list of soups recorded in the db
20
+func GetSpecies(w http.ResponseWriter, r *http.Request) {
21
+	vars := mux.Vars(r)
22
+
23
+	species := vars["species"]
24
+	genus := vars["genus"]
25
+	family := vars["family"]
26
+
27
+	// Start collecting data from sql db
28
+	apgcode := family + genus + "_" + species
29
+	life, err := SQLGetLife(apgcode)
30
+
31
+	if err != nil {
32
+		panic(err)
33
+	}
34
+
35
+	samples, err := SQLGetSamples(apgcode)
36
+
37
+	if err != nil {
38
+		panic(err)
39
+	}
40
+
41
+	// Examples should be the first found and the lastfound,
42
+	// if the first found is the last found than skip it
43
+	var examples []string
44
+	if len(samples) == 1 {
45
+		examples = append(examples, samples[0].Hash)
46
+	} else {
47
+		examples = append(examples, samples[0].Hash)
48
+		examples = append(examples, samples[len(samples) - 1].Hash)
49
+	}
50
+
51
+	// Get list of all found hashes
52
+	hashes := make([]string, len(samples))
53
+	for i, s := range samples {
54
+		hashes[i] = s.Hash
55
+	}
56
+
57
+	ret := SpeciesResp{
58
+		Name: life.Apgcode,
59
+		FirstFound: life.FirstFound,
60
+		LastFound: life.LastFound,
61
+		Population: life.Population,
62
+		Examples: examples,
63
+		Hashes: hashes,
64
+	}
65
+
66
+	b, err := json.Marshal(ret)
67
+	if err != nil {
68
+		panic(err)
69
+	}
70
+	fmt.Fprintf(w, "%s", string(b))
71
+}
72
+
73
+// Returns list of species in a genus ordered by population
74
+func GetGenus(w http.ResponseWriter, r *http.Request) {
75
+	vars := mux.Vars(r)
76
+
77
+	genus := vars["genus"]
78
+	family := vars["family"]
79
+
80
+	apgcode := family + genus
81
+	ret, err := SQLGetGenus(apgcode)
82
+	if err != nil {
83
+		panic(err)
84
+	}
85
+
86
+	// No processing is needed here so we just send the json
87
+
88
+	b, err := json.Marshal(ret)
89
+	if err != nil {
90
+		panic(err)
91
+	}
92
+	fmt.Fprintf(w, "%s", string(b))
93
+}
94
+
95
+func GetFamily(w http.ResponseWriter, r *http.Request) {
96
+	vars := mux.Vars(r)
97
+
98
+	family := vars["family"]
99
+
100
+	ret, err := SQLGetFamily(family)
101
+	if err != nil {
102
+		panic(err)
103
+	}
104
+
105
+	// No processing is needed here so we just send the json
106
+
107
+	b, err := json.Marshal(ret)
108
+	if err != nil {
109
+		panic(err)
110
+	}
111
+	fmt.Fprintf(w, "%s", string(b))
112
+}
113
+
114
+

+ 8
- 0
go.mod View File

@@ -0,0 +1,8 @@
1
+module soups
2
+
3
+go 1.13
4
+
5
+require (
6
+	github.com/go-sql-driver/mysql v1.5.0
7
+	github.com/gorilla/mux v1.7.4
8
+)

+ 4
- 0
go.sum View File

@@ -0,0 +1,4 @@
1
+github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
2
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
3
+github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
4
+github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=

+ 20
- 0
main.go View File

@@ -0,0 +1,20 @@
1
+package main
2
+
3
+import (
4
+	"net/http"
5
+	"github.com/gorilla/mux"
6
+)
7
+
8
+func main() {
9
+	// Create mux handler
10
+	r := mux.NewRouter()
11
+
12
+	r.HandleFunc("/census/{family}/{genus}/{species}", GetSpecies)
13
+	r.HandleFunc("/census/{family}/{genus}", GetGenus)
14
+	r.HandleFunc("/census/{family}", GetFamily)
15
+
16
+	//r.HandleFunc("/census", Cenus)
17
+
18
+	http.ListenAndServe(":8080", r)
19
+}
20
+

BIN
soups View File


+ 183
- 0
sql.go View File

@@ -0,0 +1,183 @@
1
+package main
2
+
3
+import (
4
+	"database/sql"
5
+	_ "github.com/go-sql-driver/mysql"
6
+)
7
+
8
+type Life struct {
9
+	Apgcode string
10
+	Population int
11
+	FirstFound int
12
+	LastFound int
13
+}
14
+
15
+type Sample struct {
16
+	Apgcode string
17
+	Id int
18
+	Hash string
19
+}
20
+
21
+// Connect to database
22
+func dbConnect() (db *sql.DB) {
23
+	if err != nil {
24
+		panic(err)
25
+	}
26
+
27
+	return db
28
+}
29
+
30
+// Get Life record from census
31
+func SQLGetLife(code string) (*Life, error) {
32
+	db := dbConnect()
33
+	defer db.Close()
34
+
35
+	var apgcode string
36
+	var population, firstFound, lastFound int
37
+	row := db.QueryRow("SELECT * FROM census WHERE apgcode=?", code)
38
+	err := row.Scan(&apgcode, &population, &firstFound, &lastFound)
39
+
40
+	if err != nil {
41
+		return nil, err
42
+	}
43
+
44
+	var life = Life {
45
+		apgcode,
46
+		population,
47
+		firstFound,
48
+		lastFound,
49
+	}
50
+
51
+	return &life, nil
52
+}
53
+
54
+// Get array of Sample records order by id
55
+func SQLGetSamples(code string) ([]Sample, error) {
56
+	db := dbConnect()
57
+	defer db.Close()
58
+
59
+	rows, err := db.Query("SELECT * FROM samples WHERE apgcode=? order by id", code)
60
+	defer rows.Close()
61
+
62
+	if err != nil {
63
+		return nil, err
64
+	}
65
+
66
+	var samples []Sample
67
+	var sample = Sample{}
68
+
69
+	// Iterate over rows
70
+	for rows.Next() {
71
+		err = rows.Scan(&sample.Apgcode, &sample.Id, &sample.Hash)
72
+		if err != nil {
73
+			return nil, err
74
+		}
75
+		samples = append(samples, sample)
76
+	}
77
+
78
+	return samples, nil
79
+}
80
+
81
+type Genus struct {
82
+	Name string
83
+	Population int
84
+	Species []Life
85
+}
86
+
87
+// Get array of lifes of a given genus
88
+func SQLGetGenus(code string) (*Genus, error) {
89
+	db := dbConnect()
90
+	defer db.Close()
91
+
92
+	rows, err := db.Query("SELECT * FROM census WHERE apgcode LIKE ? ORDER BY population DESC, firstFound", code + "%")
93
+	defer rows.Close()
94
+	if err != nil {
95
+		return nil, err
96
+	}
97
+
98
+	var genus = Genus {
99
+		Name: code,
100
+	}
101
+
102
+	// Iterate over rows
103
+	for rows.Next() {
104
+		var life = Life{}
105
+
106
+		err = rows.Scan(&life.Apgcode, &life.Population, &life.FirstFound, &life.LastFound)
107
+		if err != nil {
108
+			panic(err)
109
+		}
110
+
111
+		genus.Species = append(genus.Species, life)
112
+	}
113
+
114
+	// Get population of genus
115
+	genus.Population = getPopulationOfCode(code + "%")
116
+
117
+	return &genus, nil
118
+}
119
+
120
+type GenusProxy struct {
121
+	Name string
122
+	Count int
123
+}
124
+
125
+type Family struct {
126
+	Name string
127
+	Genera []GenusProxy
128
+}
129
+
130
+// Get array of genus in the family, each genus should have the count of unique species
131
+func SQLGetFamily(code string) (*Family, error) {
132
+	db := dbConnect()
133
+	defer db.Close()
134
+
135
+	// Returns list of genera and their count of species below them, ordered by genus and formatted nicely
136
+	// TLDR I'm a freaking god and here is my SQL to prove it
137
+	rows, err := db.Query("SELECT SUBSTRING_INDEX(apgcode, \"_\", 1) AS a, COUNT(apgcode) FROM census WHERE apgcode LIKE ? GROUP BY a ORDER BY SUBSTR(a, LENGTH(?)-1, LENGTH(a)-(LENGTH(?)-1))", code + "%", code, code)
138
+	defer rows.Close()
139
+
140
+	if err != nil {
141
+		panic(err)
142
+	}
143
+
144
+	var family = Family {
145
+		Name: code,
146
+	}
147
+
148
+	for rows.Next() {
149
+		var proxy GenusProxy
150
+		rows.Scan(&proxy.Name, &proxy.Count)
151
+		family.Genera = append(family.Genera, proxy)
152
+	}
153
+
154
+	return &family, nil
155
+}
156
+
157
+// Totals the population of species matching code
158
+func getPopulationOfCode(code string) (pop int) {
159
+	db := dbConnect()
160
+	defer db.Close()
161
+
162
+	err := db.QueryRow("SELECT SUM(population) FROM census WHERE apgcode LIKE ?", code).Scan(&pop)
163
+
164
+	if err != nil {
165
+		return 0
166
+	}
167
+
168
+	return pop
169
+}
170
+
171
+// Get the count of unique species of matching code
172
+func getCountOfCode(code string) (count int) {
173
+	db := dbConnect()
174
+	defer db.Close()
175
+
176
+	err := db.QueryRow("SELECT COUNT(population) FROM census WHERE apgcode LIKE ?", code).Scan(&count)
177
+
178
+	if err != nil {
179
+		return 0
180
+	}
181
+
182
+	return count
183
+}

Loading…
Cancel
Save