Skip to content
Snippets Groups Projects
main.go 11.9 KiB
Newer Older
Benjamin skinstad's avatar
Benjamin skinstad committed
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math"
Benjamin skinstad's avatar
Benjamin skinstad committed
	"runtime"
	"strconv"
	"strings"
	"time"

	//	"io/ioutil"
	"net/http"
)

//struct for holding the species array in country
Benjamin skinstad's avatar
Benjamin skinstad committed
type arraySpeciesJSON struct {
	ArryJson []speciesJSON2 `json:"results"`
}

//just a small struct used to make it easier to get year
Benjamin skinstad's avatar
Benjamin skinstad committed
type specisYearJson struct {

	Key float64  `json:"key"`
}

//struct for json respnce in country method
Benjamin skinstad's avatar
Benjamin skinstad committed
type countryJSON struct {
	Code 				string `json:"code"`
	Countryname 		string `json:"countryname"`
	CountryFlag 		string `json:"countryflag"`
	Species 			[]speciesJSON2 `json:"species"`
	SpeciesKey 			[]specisYearJson `json:"speciesKey"`

}

//due to stupid implementation by me i had to make an identical struct with an float responce in year, hey if it works it works
// the initial json was made when my understanding of json was poor, something that imporved over time, but i am afraid that trying to improve the json
//to the other type might break it

Benjamin skinstad's avatar
Benjamin skinstad committed
type speciesJSON2 struct {
	Key					float64 `json:"key"`
	Kingdom				string `json:"kingdom"`
	Phylum				string `json:"phylum"`
	Order				string `json:"order"`
	Family				string `json:"family"`
	Genus				string `json:"genus"`
	ScientificName		string `json:"scientificName"`
	CanonicalName		string `json:"canonicalName"`
	Year				float64 `json:"year"`
}

type speciesJSON struct {
	Key					float64 `json:"key"`
	Kingdom				string `json:"kingdom"`
	Phylum				string `json:"phylum"`
	Order				string `json:"order"`
	Family				string `json:"family"`
	Genus				string `json:"genus"`
	ScientificName		string `json:"scientificName"`
	CanonicalName		string `json:"canonicalName"`
	Year				string `json:"year"`
}

//json struct for diagnose
Benjamin skinstad's avatar
Benjamin skinstad committed
type diagJSON struct {
	Gbif 				int `json:"gbif"`
	Rescountries		int `json:"restcountries"`
Benjamin skinstad's avatar
Benjamin skinstad committed
	Version 			string `json:"version"`
	Uptime 				float64 `json:"uptime"`

}

Benjamin skinstad's avatar
Benjamin skinstad committed
var(
	StartTime = time.Time{} //for uptime

	//filter for url methods
Benjamin skinstad's avatar
Benjamin skinstad committed
	countryFilter 	= "/conservation/v1/country/"
	specisFilter 	= "/conservation/v1/species/"
	diagFilter 		= "/conservation/v1/diag/"
)


func main() {


	//starts the uptime counter
Benjamin skinstad's avatar
Benjamin skinstad committed
	StartTime = time.Now()


	//to handle random / and undefined behavior
Benjamin skinstad's avatar
Benjamin skinstad committed
	http.HandleFunc("/", undefined)

	//handels the functions based on user input, the 2. parameter is the global string used for filtering
Benjamin skinstad's avatar
Benjamin skinstad committed
	http.HandleFunc(countryFilter, country)
	http.HandleFunc(specisFilter, species)
	http.HandleFunc(diagFilter, diag)

	//tries to get the port from the port number from the env file
	port := os.Getenv("PORT")

	//if it is unable to find the port variable it forces one
	if port == "" {
		port = "8080"
	}

	//opens and continus listens on said port
	log.Fatal(http.ListenAndServe(":"+port, nil))
Benjamin skinstad's avatar
Benjamin skinstad committed


}

func undefined(w http.ResponseWriter, r *http.Request){


	io.WriteString(w, "please use /conservation/v1/country/ /conservation/v1/species/ /conservation/v1/diag/")
Benjamin skinstad's avatar
Benjamin skinstad committed
}


func country(w http.ResponseWriter, r *http.Request){


	var input = r.URL.String() //takes in an raw url string to keep the question mark

Benjamin skinstad's avatar
Benjamin skinstad committed

	filtered := strings.Replace(input,countryFilter, "", -1) //removes the unessesary parts of the url
	countryCode := string(filtered[0:2]) 		//saves away country parameter, its always 2 letters
Benjamin skinstad's avatar
Benjamin skinstad committed

	filterNumber := strings.Replace(filtered,countryCode+"&limit=", "", -1)  //removes more of the url to only get the filter number
Benjamin skinstad's avatar
Benjamin skinstad committed

	//makes it possible to not send in an limit, sets the value to 10 if no limit has been given
	//it also now doesnt accept negative numbers or letters , default is size is 10 if not proper limit has been given
	{
		testInt, _ := strconv.Atoi(filterNumber)
		if testInt <= 0{
			filterNumber = "10"
		}
	}
	resp, err := http.Get("https://restcountries.eu/rest/v2/alpha/"+countryCode) //get full cpountry json responce
Benjamin skinstad's avatar
Benjamin skinstad committed
	if err != nil {
		print("error\n")
		log.Fatalln(err)

	}

	{ //
		tempLimit, _ :=strconv.Atoi(filterNumber) 	//checks that the input number is not over 300, the api does not support 300+ in limit
													//sets it to 300 if its to big
		if tempLimit > 300 {
			filterNumber ="300"
		}
	}

	//gets the species part of the json

	respSpecis, err := http.Get("http://api.gbif.org/v1/occurrence/search?country="+countryCode+"&"+"limit="+filterNumber)
Benjamin skinstad's avatar
Benjamin skinstad committed
		if err != nil {
			print("error\n")
			log.Fatalln(err)

		}

//	defer respSpecis.Body.Close()
//	bodySpecis, err := ioutil.ReadAll(respSpecis.Body)

//	if err != nil {
//		print("error\n")
//		log.Fatalln(err)

//	}


//	var infoSpecis map[string]interface{}
//	json.Unmarshal([]byte(bodySpecis),&infoSpecis)



	//	numberResponce := infoSpecis["limit"]

	//	var intNumberResponce int = int(numberResponce.(float64))



//		var countryJsonArray []speciesJSON

	/*	var objmap map[string]*json.RawMessage
		//err = json.Unmarshal(bodySpecis, &objmap)


		var s interface{}

		err = json.Unmarshal(*objmap["results"], &s)

*/

		//err = json.Unmarshal(s[1], &test)


	//	str := fmt.Sprintf("%v", s)

	//	var test map[string]interface{}



	//	io.WriteString(w,string(str))

	//	str := fmt.Sprintf("%v", respSpecis.Body)

	//	println(str)
Benjamin skinstad's avatar
Benjamin skinstad committed
	var spectest arraySpeciesJSON


		err = json.NewDecoder(respSpecis.Body).Decode(&spectest) //decodes the json into spectest


Benjamin skinstad's avatar
Benjamin skinstad committed

		if err != nil {
			print("error\n")
			log.Fatalln(err)

		}

	var countryJsonArray []speciesJSON2
	var yearJsonArray []specisYearJson
	var duplicateCheck  = "" 			//builds an string with all entires and checks for dupes after
	intFilternumber, _ := strconv.Atoi(filterNumber) //go has some werid problems with conversion of types
Benjamin skinstad's avatar
Benjamin skinstad committed

	for i:=0; i<intFilternumber ;i++ {

		keyTest := fmt.Sprintf("%f", spectest.ArryJson[i].Key) //gets the key


		if strings.Contains(duplicateCheck, keyTest) { //check if key already exists, go out of loop if it does
			duplicateCheck +=  keyTest+","
Benjamin skinstad's avatar
Benjamin skinstad committed


			key            := spectest.ArryJson[i].Key
			kingdom        := spectest.ArryJson[i].Kingdom
			phylum         := spectest.ArryJson[i].Phylum
			order          := spectest.ArryJson[i].Order
			family         := spectest.ArryJson[i].Family
Benjamin skinstad's avatar
Benjamin skinstad committed
			genus          := spectest.ArryJson[i].Genus
			scientificName := spectest.ArryJson[i].ScientificName
			canonicalName  := spectest.ArryJson[i].CanonicalName
			year           := spectest.ArryJson[i].Year



			tempJson := speciesJSON2{float64(key),kingdom, phylum,order,family,
				genus,scientificName,canonicalName,year}

			tempYearJson := specisYearJson{key}

			countryJsonArray =append(countryJsonArray,tempJson)
			yearJsonArray =append(yearJsonArray,tempYearJson)


	}





	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		print("error\n")
		log.Fatalln(err)

	}

	var info map[string]interface{} //as mentioned in the start of the document this is my bad implementation of json, but since it works im afraid to fix it properly to the other
									//implementation.
Benjamin skinstad's avatar
Benjamin skinstad committed
	json.Unmarshal([]byte(body),&info)

Benjamin skinstad's avatar
Benjamin skinstad committed


	tempCode 		:= info["alpha2Code"]
	tempCountryname := info["name"]
	tempCountryFlag := info["flag"]



	//this is so i can collapse this test /
Benjamin skinstad's avatar
Benjamin skinstad committed
	{
		if tempCode == nil {
			tempCode = "NA"
		}
		if tempCountryname == nil {
			tempCountryname = "NA"
		}
		if tempCountryFlag == nil {
			tempCountryFlag = "NA"
		}

	}


	createdJson := countryJSON{tempCode.(string),tempCountryname.(string),tempCountryFlag.(string),
		countryJsonArray,yearJsonArray}



	e,err := json.Marshal(createdJson)


//	fmt.Fprint(w,(json.MarshalIndent(string(e), "", "    ")))

	w.Header().Add("content-type", "application/json")

Benjamin skinstad's avatar
Benjamin skinstad committed
	io.WriteString(w,string(e))

}

func species(w http.ResponseWriter, r *http.Request){
	//io.WriteString(w, "\nspecies\n\n")
Benjamin skinstad's avatar
Benjamin skinstad committed

	var input = r.URL.Path[:]
	filtred := strings.Replace(input,specisFilter, "", -1) //filter out the paramteter
Benjamin skinstad's avatar
Benjamin skinstad committed

	resp,err := http.Get("http://api.gbif.org/v1/species/"+filtred) //runs api call with that paramter
	if resp.StatusCode == 404 {
		io.WriteString(w,"404") //if input is invalid we do an 404 check and writes that out on the page if that is the case
Benjamin skinstad's avatar
Benjamin skinstad committed

Benjamin skinstad's avatar
Benjamin skinstad committed

		if err != nil {
			print("error\n")
			log.Fatalln(err)
Benjamin skinstad's avatar
Benjamin skinstad committed

Benjamin skinstad's avatar
Benjamin skinstad committed

		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
Benjamin skinstad's avatar
Benjamin skinstad committed

		if err != nil {
			print("error\n")
			log.Fatalln(err)
		}
Benjamin skinstad's avatar
Benjamin skinstad committed

		var info map[string]interface{}		//this is an shit way of doing json, it works for easier structures but not with nested, which is the reasons i go away from it later on
		json.Unmarshal([]byte(body), &info)

		tempKey := info["key"]
		tempNameKey := info["nameKey"]
		tempKingdom := info["kingdom"]
		tempPhylum := info["phylum"]
		tempOrder := info["order"]
		tempFamily := info["family"]
		tempGenus := info["genus"]
		tempScientificName := info["scientificName"]
		tempCanonicalName := info["canonicalName"]
		var namekeyInt int = int(tempNameKey.(float64))

		//this is so i can collapse this test
		{
			if tempKey == nil {
				tempKey = "NA"
			}
			if tempKingdom == nil {
				tempKingdom = "NA"
			}
			if tempPhylum == nil {
				tempPhylum = "NA"
			}
			if tempFamily == nil {
				tempFamily = "NA"
			}
			if tempGenus == nil {
				tempGenus = "NA"
			}
			if tempScientificName == nil {
				tempScientificName = "NA"
			}

			if tempCanonicalName == nil {
				tempCanonicalName = "NA"
			}
			if namekeyInt == 0 {
				namekeyInt = 0

			}
Benjamin skinstad's avatar
Benjamin skinstad committed

		respYear, err := http.Get("http://api.gbif.org/v1/species/" + strconv.Itoa(namekeyInt) + "/name") //uses the namekey from the species call to find year in /name
Benjamin skinstad's avatar
Benjamin skinstad committed

		if err != nil {
			print("error\n")
			log.Fatalln(err)
		defer respYear.Body.Close()
		bodyYear, err := ioutil.ReadAll(respYear.Body)
Benjamin skinstad's avatar
Benjamin skinstad committed

		if err != nil {
			print("error\n")
			log.Fatalln(err)
		}
Benjamin skinstad's avatar
Benjamin skinstad committed

		var infoYear map[string]interface{}
		json.Unmarshal([]byte(bodyYear), &infoYear) //again this poor implementation of json, but it works
Benjamin skinstad's avatar
Benjamin skinstad committed

		tempYear := infoYear["year"]
Benjamin skinstad's avatar
Benjamin skinstad committed

		createdJson := speciesJSON{tempKey.(float64), tempKingdom.(string), tempPhylum.(string), tempOrder.(string), tempFamily.(string),
			tempGenus.(string), tempScientificName.(string), tempCanonicalName.(string), tempYear.(string)}
		e, err := json.Marshal(createdJson)
		w.Header().Add("content-type", "application/json")
Benjamin skinstad's avatar
Benjamin skinstad committed

		io.WriteString(w, string(e))
Benjamin skinstad's avatar
Benjamin skinstad committed
	}
func diag(w http.ResponseWriter, r *http.Request){
Benjamin skinstad's avatar
Benjamin skinstad committed

	gbifCode := 0 		//variable to hold responce code from gbif api
	rescountriesCode := 0	 //variable to hold responce code from restcountries api
Benjamin skinstad's avatar
Benjamin skinstad committed

	version := runtime.Version() 		//get current go version
	serverRuntime := time.Since(StartTime).Seconds() 	//global variable introdused, it starts counting when main is ran, and calculates time since that happend
	serverRuntime = math.Round(serverRuntime*100)/100	//makes it into secounds with 2 decimales, change the rounding to make it into pure secounds. i prefered it with 2 decimales
	/*
	* this test does a empty get request to the gbif api main page, and based on the responce saves the responce code
	* if the site is unreachable the status code is sat to 503 (service unaviable)
	*/
	respGbif,err := http.Get("http://api.gbif.org/")
	if respGbif == nil {
		gbifCode = 503
	}else{
		if err != nil{
			gbifCode = respGbif.StatusCode
Benjamin skinstad's avatar
Benjamin skinstad committed

		}else{
			gbifCode = respGbif.StatusCode
		}
	}
Benjamin skinstad's avatar
Benjamin skinstad committed

	/*
	* this test does a empty get request to the respRestcountries api main page, and based on the responce saves the responce code
	* if the site is unreachable the status code is sat to 503 (service unaviable)
	 */
Benjamin skinstad's avatar
Benjamin skinstad committed

	respRestcountries,err := http.Get("http://restcountries.eu/")
Benjamin skinstad's avatar
Benjamin skinstad committed

	if respRestcountries == nil {
		rescountriesCode = 503
	}else {
Benjamin skinstad's avatar
Benjamin skinstad committed

		if err != nil{
			rescountriesCode = respRestcountries.StatusCode
		}else{
			rescountriesCode = respRestcountries.StatusCode
Benjamin skinstad's avatar
Benjamin skinstad committed

Benjamin skinstad's avatar
Benjamin skinstad committed

	// creates an json bassed on the variables above
	createdJson := diagJSON{gbifCode,rescountriesCode,version,serverRuntime}
	e,err := json.Marshal(createdJson)
Benjamin skinstad's avatar
Benjamin skinstad committed

	if err != nil {
		print("error\n")
		log.Fatalln(err)
	//sets the content type to json to make prettier output
	w.Header().Add("content-type", "application/json")
Benjamin skinstad's avatar
Benjamin skinstad committed

	//outputs the json to screen
	io.WriteString(w,string(e))
}

func fouroFour(w http.ResponseWriter, r *http.Request){
Benjamin skinstad's avatar
Benjamin skinstad committed

Benjamin skinstad's avatar
Benjamin skinstad committed
	io.WriteString(w,string(e))