From 86c6ad8781f73979dc8532a59a46e5ef38c13a10 Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Sat, 27 Apr 2019 00:13:26 +0200
Subject: [PATCH] Implement suggestions API

---
 client.go              | 52 ++++++++++++++++++++++-----
 external_suggestion.go | 81 ++++++++++++++++++++++++++++++++++++++++++
 model_suggestion.go    | 16 +++++++++
 3 files changed, 140 insertions(+), 9 deletions(-)
 create mode 100644 external_suggestion.go
 create mode 100644 model_suggestion.go

diff --git a/client.go b/client.go
index 80ea638..62c865d 100644
--- a/client.go
+++ b/client.go
@@ -4,12 +4,15 @@ import (
 	"fmt"
 	"net/http"
 	"time"
+
+	"net/url"
 )
 
 type ApiClient struct {
 	IrisBaseUrl          string
 	CoachSequenceBaseUrl string
-	HttpClient            *http.Client
+	HafasBaseUrl         string
+	HttpClient           *http.Client
 }
 
 func (c *ApiClient) Station(evaId int64) ([]Station, error) {
@@ -62,12 +65,12 @@ func (c *ApiClient) Timetable(evaId int64, date time.Time) (Timetable, error) {
 func (c *ApiClient) RealtimeAll(evaId int64, date time.Time) (Timetable, error) {
 	var err error
 
-	url := fmt.Sprintf("%s/timetable/fchg/%d", c.IrisBaseUrl, evaId)
+	uri := fmt.Sprintf("%s/timetable/fchg/%d", c.IrisBaseUrl, evaId)
 
 	var timetable Timetable
 
 	var response *http.Response
-	if response, err = c.HttpClient.Get(url); err != nil {
+	if response, err = c.HttpClient.Get(uri); err != nil {
 		return timetable, err
 	}
 
@@ -85,12 +88,12 @@ func (c *ApiClient) RealtimeAll(evaId int64, date time.Time) (Timetable, error)
 func (c *ApiClient) RealtimeRecent(evaId int64, date time.Time) (Timetable, error) {
 	var err error
 
-	url := fmt.Sprintf("%s/timetable/rchg/%d", c.IrisBaseUrl, evaId)
+	uri := fmt.Sprintf("%s/timetable/rchg/%d", c.IrisBaseUrl, evaId)
 
 	var timetable Timetable
 
 	var response *http.Response
-	if response, err = c.HttpClient.Get(url); err != nil {
+	if response, err = c.HttpClient.Get(uri); err != nil {
 		return timetable, err
 	}
 
@@ -108,12 +111,12 @@ func (c *ApiClient) RealtimeRecent(evaId int64, date time.Time) (Timetable, erro
 func (c *ApiClient) WingDefinition(parent string, wing string) (WingDefinition, error) {
 	var err error
 
-	url := fmt.Sprintf("%s/timetable/wingdef/%s/%s", c.IrisBaseUrl, parent, wing)
+	uri := fmt.Sprintf("%s/timetable/wingdef/%s/%s", c.IrisBaseUrl, parent, wing)
 
 	var wingDefinition WingDefinition
 
 	var response *http.Response
-	if response, err = c.HttpClient.Get(url); err != nil {
+	if response, err = c.HttpClient.Get(uri); err != nil {
 		return wingDefinition, err
 	}
 
@@ -131,12 +134,12 @@ func (c *ApiClient) WingDefinition(parent string, wing string) (WingDefinition,
 func (c *ApiClient) CoachSequence(line string, date time.Time) (CoachSequence, error) {
 	var err error
 
-	url := fmt.Sprintf("%s/%s/%s", c.CoachSequenceBaseUrl, line, date.Format(TimeLayoutShort))
+	uri := fmt.Sprintf("%s/%s/%s", c.CoachSequenceBaseUrl, line, date.Format(TimeLayoutShort))
 
 	var coachSequence CoachSequence
 
 	var response *http.Response
-	if response, err = c.HttpClient.Get(url); err != nil {
+	if response, err = c.HttpClient.Get(uri); err != nil {
 		return coachSequence, err
 	}
 
@@ -150,3 +153,34 @@ func (c *ApiClient) CoachSequence(line string, date time.Time) (CoachSequence, e
 
 	return coachSequence, err
 }
+
+func (c *ApiClient) Suggestions(line string, date time.Time) ([]Suggestion, error) {
+	var err error
+
+	uri := fmt.Sprintf("%s/trainsearch.exe/dn", c.HafasBaseUrl)
+
+	var suggestions []Suggestion
+
+	DateFormat := "02.01.2006"
+	body := url.Values{
+		"maxResults": []string{"50"},
+		"trainname":  []string{line},
+		"date":       []string{date.Format(DateFormat)},
+		"L":          []string{"vs_json.vs_hap"},
+	}
+
+	var response *http.Response
+	if response, err = c.HttpClient.PostForm(uri, body); err != nil {
+		return suggestions, err
+	}
+
+	if suggestions, err = SuggestionsFromReader(response.Body); err != nil {
+		return suggestions, err
+	}
+
+	if err = response.Body.Close(); err != nil {
+		return suggestions, err
+	}
+
+	return suggestions, err
+}
diff --git a/external_suggestion.go b/external_suggestion.go
new file mode 100644
index 0000000..954a1d2
--- /dev/null
+++ b/external_suggestion.go
@@ -0,0 +1,81 @@
+package bahn
+
+import (
+	"encoding/json"
+	"io"
+	"time"
+)
+
+func SuggestionsFromReader(source io.Reader) ([]Suggestion, error) {
+	var raw rawSuggestions
+	if err := json.NewDecoder(source).Decode(&raw); err != nil {
+		return make([]Suggestion, 0), err
+	}
+	return parseSuggestions(raw), nil
+}
+
+func SuggestionsFromBytes(source []byte) ([]Suggestion, error) {
+	var raw rawSuggestions
+	if err := json.Unmarshal(source, &raw); err != nil {
+		return make([]Suggestion, 0), err
+	}
+	return parseSuggestions(raw), nil
+}
+
+type rawSuggestions struct {
+	Suggestions []rawSuggestion
+}
+
+func parseSuggestions(data rawSuggestions) []Suggestion {
+	result := make([]Suggestion, len(data.Suggestions))
+	for i, element := range data.Suggestions {
+		result[i] = parseSuggestion(element)
+	}
+	return result
+}
+
+type rawSuggestion struct {
+	Value            string `json:"value"`
+	Cycle            string `json:"cycle"`
+	Pool             string `json:"pool"`
+	Id               string `json:"id"`
+	TrainLink        string `json:"trainLink"`
+	JourneyParams    string `json:"journParam"`
+	PublishedTime    string `json:"pubTime"`
+	PublishedDate    string `json:"pubDate"`
+	DepartureStation string `json:"dep"`
+	DepartureDate    string `json:"depDate"`
+	DepartureTime    string `json:"depTime"`
+	ArrivalStation   string `json:"arr"`
+	ArrivalTime      string `json:"arrTime"`
+	ArrivalDate      string `json:"arrDate"`
+}
+
+func parseTime(dateStr string, timeStr string) *time.Time {
+	DateFormat := "02.01.2006"
+	DateTimeFormat := "02.01.2006 15:04"
+	if dateStr == "" {
+		dateStr = time.Now().Format(DateFormat)
+	}
+
+	dateTime, err := time.Parse(DateTimeFormat, dateStr+" "+timeStr)
+	if err != nil {
+		return nil
+	}
+	return &dateTime
+}
+
+func parseSuggestion(data rawSuggestion) Suggestion {
+	return Suggestion{
+		Value:            data.Value,
+		Cycle:            data.Cycle,
+		Pool:             data.Pool,
+		Id:               data.Id,
+		TrainLink:        data.TrainLink,
+		PublishedTime:    parseTime(data.PublishedDate, data.PublishedTime),
+		DepartureStation: data.DepartureStation,
+		DepartureTime:    parseTime(data.DepartureDate, data.DepartureTime),
+		ArrivalStation:   data.ArrivalStation,
+		ArrivalTime:      parseTime(data.ArrivalDate, data.ArrivalTime),
+	}
+}
diff --git a/model_suggestion.go b/model_suggestion.go
new file mode 100644
index 0000000..26a5dfb
--- /dev/null
+++ b/model_suggestion.go
@@ -0,0 +1,16 @@
+package bahn
+
+import "time"
+
+type Suggestion struct {
+	Value            string
+	Cycle            string
+	Pool             string
+	Id               string
+	TrainLink        string
+	PublishedTime    *time.Time
+	DepartureStation string
+	DepartureTime    *time.Time
+	ArrivalStation   string
+	ArrivalTime      *time.Time
+}
-- 
GitLab