Browse Source

first commit

master
Chris Mahoney 2 years ago
commit
eebe6971ec
  1. 114
      api.go
  2. 8
      go.mod
  3. 4
      go.sum
  4. 20
      main.go
  5. BIN
      soups
  6. 183
      sql.go

114
api.go

@ -0,0 +1,114 @@
package main
import (
"net/http"
"github.com/gorilla/mux"
"fmt"
"encoding/json"
)
type SpeciesResp struct {
Name string
FirstFound int
LastFound int
Population int
Examples []string
Hashes []string
}
// 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
func GetSpecies(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
species := vars["species"]
genus := vars["genus"]
family := vars["family"]
// Start collecting data from sql db
apgcode := family + genus + "_" + species
life, err := SQLGetLife(apgcode)
if err != nil {
panic(err)
}
samples, err := SQLGetSamples(apgcode)
if err != nil {
panic(err)
}
// Examples should be the first found and the lastfound,
// if the first found is the last found than skip it
var examples []string
if len(samples) == 1 {
examples = append(examples, samples[0].Hash)
} else {
examples = append(examples, samples[0].Hash)
examples = append(examples, samples[len(samples) - 1].Hash)
}
// Get list of all found hashes
hashes := make([]string, len(samples))
for i, s := range samples {
hashes[i] = s.Hash
}
ret := SpeciesResp{
Name: life.Apgcode,
FirstFound: life.FirstFound,
LastFound: life.LastFound,
Population: life.Population,
Examples: examples,
Hashes: hashes,
}
b, err := json.Marshal(ret)
if err != nil {
panic(err)
}
fmt.Fprintf(w, "%s", string(b))
}
// Returns list of species in a genus ordered by population
func GetGenus(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
genus := vars["genus"]
family := vars["family"]
apgcode := family + genus
ret, err := SQLGetGenus(apgcode)
if err != nil {
panic(err)
}
// No processing is needed here so we just send the json
b, err := json.Marshal(ret)
if err != nil {
panic(err)
}
fmt.Fprintf(w, "%s", string(b))
}
func GetFamily(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
family := vars["family"]
ret, err := SQLGetFamily(family)
if err != nil {
panic(err)
}
// No processing is needed here so we just send the json
b, err := json.Marshal(ret)
if err != nil {
panic(err)
}
fmt.Fprintf(w, "%s", string(b))
}

8
go.mod

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

4
go.sum

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

20
main.go

@ -0,0 +1,20 @@
package main
import (
"net/http"
"github.com/gorilla/mux"
)
func main() {
// Create mux handler
r := mux.NewRouter()
r.HandleFunc("/census/{family}/{genus}/{species}", GetSpecies)
r.HandleFunc("/census/{family}/{genus}", GetGenus)
r.HandleFunc("/census/{family}", GetFamily)
//r.HandleFunc("/census", Cenus)
http.ListenAndServe(":8080", r)
}

BIN
soups

183
sql.go

@ -0,0 +1,183 @@
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
type Life struct {
Apgcode string
Population int
FirstFound int
LastFound int
}
type Sample struct {
Apgcode string
Id int
Hash string
}
// Connect to database
func dbConnect() (db *sql.DB) {
if err != nil {
panic(err)
}
return db
}
// Get Life record from census
func SQLGetLife(code string) (*Life, error) {
db := dbConnect()
defer db.Close()
var apgcode string
var population, firstFound, lastFound int
row := db.QueryRow("SELECT * FROM census WHERE apgcode=?", code)
err := row.Scan(&apgcode, &population, &firstFound, &lastFound)
if err != nil {
return nil, err
}
var life = Life {
apgcode,
population,
firstFound,
lastFound,
}
return &life, nil
}
// Get array of Sample records order by id
func SQLGetSamples(code string) ([]Sample, error) {
db := dbConnect()
defer db.Close()
rows, err := db.Query("SELECT * FROM samples WHERE apgcode=? order by id", code)
defer rows.Close()
if err != nil {
return nil, err
}
var samples []Sample
var sample = Sample{}
// Iterate over rows
for rows.Next() {
err = rows.Scan(&sample.Apgcode, &sample.Id, &sample.Hash)
if err != nil {
return nil, err
}
samples = append(samples, sample)
}
return samples, nil
}
type Genus struct {
Name string
Population int
Species []Life
}
// Get array of lifes of a given genus
func SQLGetGenus(code string) (*Genus, error) {
db := dbConnect()
defer db.Close()
rows, err := db.Query("SELECT * FROM census WHERE apgcode LIKE ? ORDER BY population DESC, firstFound", code + "%")
defer rows.Close()
if err != nil {
return nil, err
}
var genus = Genus {
Name: code,
}
// Iterate over rows
for rows.Next() {
var life = Life{}
err = rows.Scan(&life.Apgcode, &life.Population, &life.FirstFound, &life.LastFound)
if err != nil {
panic(err)
}
genus.Species = append(genus.Species, life)
}
// Get population of genus
genus.Population = getPopulationOfCode(code + "%")
return &genus, nil
}
type GenusProxy struct {
Name string
Count int
}
type Family struct {
Name string
Genera []GenusProxy
}
// Get array of genus in the family, each genus should have the count of unique species
func SQLGetFamily(code string) (*Family, error) {
db := dbConnect()
defer db.Close()
// Returns list of genera and their count of species below them, ordered by genus and formatted nicely
// TLDR I'm a freaking god and here is my SQL to prove it
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)
defer rows.Close()
if err != nil {
panic(err)
}
var family = Family {
Name: code,
}
for rows.Next() {
var proxy GenusProxy
rows.Scan(&proxy.Name, &proxy.Count)
family.Genera = append(family.Genera, proxy)
}
return &family, nil
}
// Totals the population of species matching code
func getPopulationOfCode(code string) (pop int) {
db := dbConnect()
defer db.Close()
err := db.QueryRow("SELECT SUM(population) FROM census WHERE apgcode LIKE ?", code).Scan(&pop)
if err != nil {
return 0
}
return pop
}
// Get the count of unique species of matching code
func getCountOfCode(code string) (count int) {
db := dbConnect()
defer db.Close()
err := db.QueryRow("SELECT COUNT(population) FROM census WHERE apgcode LIKE ?", code).Scan(&count)
if err != nil {
return 0
}
return count
}
Loading…
Cancel
Save