Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/top10 #3

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion _examples/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func main() {
url := ctftime.GetUrl("events", nil)
fmt.Printf("[==>] Requesting %s ...\n", url)
events := ctftime.GetAPIData("events", nil)
for idx, event := range events.([]ctftime.Event) {
for idx, event := range events.(ctftime.Events) {
fmt.Printf("[event%d]\n", idx)
fmt.Printf("%#v\n", event)
}
Expand Down
42 changes: 42 additions & 0 deletions _examples/top10.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"fmt"

"github.com/g0tiu5a/ctftime"
)

func GetTop10() {
url := ctftime.GetUrl("top10", nil)
fmt.Printf("[==>] Requesting %s ...\n", url)

top10s := ctftime.GetAPIData("top10", nil)
for key, top10 := range top10s.(ctftime.Top10s) {
fmt.Printf("[%s]", key)
for idx, team := range top10 {
fmt.Printf(" [%d] %#v\n", idx, team)
}
fmt.Printf("\n")
}
}

func Get2017Top10() {
ctx := ctftime.APIContext{
"year": "2017",
}
url := ctftime.GetUrl("top10", ctx)
fmt.Printf("[==>] Requesting %s ...\n", url)

top10 := ctftime.GetAPIData("top10", ctx)
for idx, team := range top10.(ctftime.Top10) {
fmt.Printf(" [%d] %#v\n", idx, team)
}
}

func main() {
fmt.Printf("[*] Trying All year's top10 api ...\n")
GetTop10()

fmt.Printf("[*] Trying 2017's top10 api ...\n")
Get2017Top10()
}
15 changes: 5 additions & 10 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type apiClient interface {
GetAPIData() interface{}
}

type apiContext map[string]interface{}
type apiClientFactory func(ctx apiContext) apiClient
type APIContext map[string]interface{}
type apiClientFactory func(ctx APIContext) apiClient

var apiClientFactories = make(map[string]apiClientFactory)

Expand All @@ -27,12 +27,7 @@ func registerAPIClient(name string, factory apiClientFactory) {
apiClientFactories[name] = factory
}

// この関数はパッケージがimportされた時に呼び出されます
func init() {
registerAPIClient("events", newEventsAPIClient)
}

func newAPIClient(name string, ctx apiContext) apiClient {
func newAPIClient(name string, ctx APIContext) apiClient {
clientFactory, ok := apiClientFactories[name]
if !ok {
log.Panicf("Invalid API Client name!")
Expand All @@ -41,12 +36,12 @@ func newAPIClient(name string, ctx apiContext) apiClient {
return clientFactory(ctx)
}

func GetUrl(name string, ctx apiContext) string {
func GetUrl(name string, ctx APIContext) string {
client := newAPIClient(name, ctx)
return client.GetUrl()
}

func GetAPIData(name string, ctx map[string]interface{}) interface{} {
func GetAPIData(name string, ctx APIContext) interface{} {
client := newAPIClient(name, ctx)
return client.GetAPIData()
}
2 changes: 2 additions & 0 deletions event.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ type Duration struct {
Hours int `json:"hours"`
Days int `json:"days"`
}

type Events []Event
10 changes: 7 additions & 3 deletions events.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import (
)

type eventsAPIClient struct {
Ctx apiContext
Ctx APIContext
}

func newEventsAPIClient(ctx apiContext) apiClient {
func newEventsAPIClient(ctx APIContext) apiClient {
return &eventsAPIClient{
Ctx: ctx,
}
}

func init() {
registerAPIClient("events", newEventsAPIClient)
}

func (client *eventsAPIClient) GetUrl() string {
now := time.Now().Unix()

Expand All @@ -43,7 +47,7 @@ func (client *eventsAPIClient) GetAPIData() interface{} {
}
defer resp.Body.Close()

var events []Event
var events Events
httpResponseToStruct(resp, &events)
return events
}
10 changes: 10 additions & 0 deletions team.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ctftime

type Team struct {
TeamId int64 `json:"team_id"`
TeamName string `json:"team_name"`
Points float64 `json:"points"`
}

type Top10 []Team
type Top10s map[string]Top10
1 change: 1 addition & 0 deletions test_data/top10_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"2016": [{"team_name": "dcua", "points": 1626.8398252895672, "team_id": 762}, {"team_name": "Dragon Sector", "points": 1437.0797589689191, "team_id": 3329}, {"team_name": "LC\u21afBC", "points": 1421.9162070743116, "team_id": 15726}, {"team_name": "Plaid Parliament of Pwning", "points": 1420.0849788230748, "team_id": 284}, {"team_name": "p4", "points": 1139.5346754638151, "team_id": 5152}, {"team_name": "217", "points": 1089.1912175681678, "team_id": 5160}, {"team_name": "TokyoWesterns", "points": 883.9267147647392, "team_id": 12599}, {"team_name": "Tasteless", "points": 875.5401997885435, "team_id": 604}, {"team_name": "0daysober", "points": 852.102032615904, "team_id": 760}, {"team_name": "Eat, Sleep, Pwn, Repeat", "points": 782.212810041931, "team_id": 15712}], "2017": [{"team_name": "Plaid Parliament of Pwning", "points": 701.2585566581962, "team_id": 284}, {"team_name": "217", "points": 570.1486229891746, "team_id": 5160}, {"team_name": "LC\u21afBC", "points": 469.20455737082784, "team_id": 15726}, {"team_name": "Bushwhackers", "points": 435.99265313767927, "team_id": 586}, {"team_name": "Dragon Sector", "points": 428.0948575325616, "team_id": 3329}, {"team_name": "Shellphish", "points": 426.1389494220905, "team_id": 285}, {"team_name": "binja", "points": 345.1303732845989, "team_id": 9083}, {"team_name": "dcua", "points": 344.43269610104136, "team_id": 762}, {"team_name": "p4", "points": 337.01080389588515, "team_id": 5152}, {"team_name": "Tasteless", "points": 330.3306567560065, "team_id": 604}], "2011": [], "2012": [{"team_name": "More Smoked Leet Chicken", "points": 1620.9290291782108, "team_id": 1005}, {"team_name": "Plaid Parliament of Pwning", "points": 1488.2189290148378, "team_id": 284}, {"team_name": "Eindbazen", "points": 1173.1869831197898, "team_id": 322}, {"team_name": "sutegoma2", "points": 739.9457040460449, "team_id": 280}, {"team_name": "LSE", "points": 680.8464679805236, "team_id": 757}, {"team_name": "GoN", "points": 582.0422811390673, "team_id": 1288}, {"team_name": "Hates Irony", "points": 481.28155543835965, "team_id": 279}, {"team_name": "CLGT", "points": 473.9239266941017, "team_id": 298}, {"team_name": "HackerDom", "points": 459.2769161002838, "team_id": 552}, {"team_name": "disekt", "points": 457.5268910229511, "team_id": 308}], "2013": [], "2014": [], "2015": [{"team_name": "Plaid Parliament of Pwning", "points": 1789.8838263769155, "team_id": 284}, {"team_name": "Dragon Sector", "points": 1184.774269385523, "team_id": 3329}, {"team_name": "0ops", "points": 1088.710549206889, "team_id": 4419}, {"team_name": "Shellphish", "points": 1019.3068651403331, "team_id": 285}, {"team_name": "!SpamAndHex", "points": 1015.4893823023731, "team_id": 5347}, {"team_name": "dcua", "points": 917.8869637081789, "team_id": 762}, {"team_name": "Samurai", "points": 786.9403043232309, "team_id": 1937}, {"team_name": "blue-lotus", "points": 783.0605525584488, "team_id": 1941}, {"team_name": "217", "points": 769.1900083740462, "team_id": 5160}, {"team_name": "Tasteless", "points": 766.7836623792566, "team_id": 604}]}
48 changes: 48 additions & 0 deletions top10.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package ctftime

import (
"log"
"net/http"
)

type top10APIClient struct {
Ctx APIContext
}

func newTop10APIClient(ctx APIContext) apiClient {
return &top10APIClient{
Ctx: ctx,
}
}

func init() {
registerAPIClient("top10", newTop10APIClient)
}

func (client *top10APIClient) GetUrl() string {
url := API_ENDPOINT + "/top/"
if year, ok := client.Ctx["year"]; ok {
url = url + year.(string) + "/"
}

return url
}

func (client *top10APIClient) GetAPIData() interface{} {
url := client.GetUrl()

resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()

var top10s Top10s
httpResponseToMap(resp, &top10s)
if year, ok := client.Ctx["year"]; ok {
var top10 Top10 = top10s[year.(string)]
return top10
} else {
return top10s
}
}
43 changes: 43 additions & 0 deletions top10_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ctftime

import (
"bytes"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"reflect"
"testing"
)

func TestGetTop10Data(t *testing.T) {
client := newAPIClient("top10", nil)

result := client.GetAPIData()

body, err := json.Marshal(result)
if err != nil {
log.Fatal(err)
}

dummy_resp := &http.Response{
StatusCode: 200,
ProtoMajor: 1,
ProtoMinor: 0,
Body: ioutil.NopCloser(bytes.NewReader(body)),
}

var dummy_top10 Top10s
httpResponseToMap(dummy_resp, &dummy_top10)
if len(dummy_top10["2017"]) != 10 {
t.Error("Invalid top10 of 2017 length!")
}

for _, team := range dummy_top10["2017"] {
valid := reflect.TypeOf(Team{})
actual := reflect.TypeOf(team)
if actual != valid {
t.Errorf("Invalid team type of %v! (should be %v).", actual, valid)
}
}
}
28 changes: 28 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ package ctftime

import (
"encoding/json"
"io"
"io/ioutil"
"log"
"net/http"
"path"
"strings"
)

/* Test */

// テストに使うjsonファイルのデータを読み込んでくる
func getTestData(fname string) []byte {
fpath := path.Join(test_dir, fname)

Expand All @@ -23,6 +26,7 @@ func getTestData(fname string) []byte {

/* HTTP */

// HTTPレスポンスのボディから構造体へ変換するための関数
func httpResponseToStruct(r *http.Response, v interface{}) {
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
Expand All @@ -44,3 +48,27 @@ func httpResponseToStruct(r *http.Response, v interface{}) {
}
}
}

// 年度をキーにしているなど、今後キーが変更される場合、mapで取れるようにするための関数
func httpResponseToMap(r *http.Response, v interface{}) {
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal("[ReadAll ")
}

decoder := json.NewDecoder(strings.NewReader(string(body)))
err = decoder.Decode(&v)
if err != nil && err != io.EOF {
log.Fatal(err)
}

if valid, ok := v.(interface {
OK() error
}); ok {
err = valid.OK()
if err != nil {
log.Fatal("[Validation] ")
}
}
}
18 changes: 17 additions & 1 deletion util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestGetTestData(t *testing.T) {
}
}

func TestDecodeJsonResponse(t *testing.T) {
func TestHttpResponseToStruct(t *testing.T) {
buf := getTestData("event_1.json")

// Create HTTP Response
Expand All @@ -44,3 +44,19 @@ func TestDecodeJsonResponse(t *testing.T) {
var events []interface{}
httpResponseToStruct(response, &events)
}

func TestHttpResponseToMap(t *testing.T) {
buf := getTestData("top10_1.json")

response := &http.Response{
Status: "200 OK",
StatusCode: 200,
Proto: "HTTP/1.0",
ProtoMajor: 1,
ProtoMinor: 0,
Body: ioutil.NopCloser(bytes.NewReader(buf)),
}

var top10 Top10s
httpResponseToMap(response, &top10)
}