From 438cdcd1a54c8b9ea1c8aec7c138f6adfa1aea8a Mon Sep 17 00:00:00 2001 From: Steve Layton Date: Fri, 5 Feb 2021 14:34:12 -0800 Subject: [PATCH 1/2] taking a stab at adding the ML endpoint --- ml.go | 89 ++++++++++++++++++++++++++++++++++++ ml_test.go | 44 ++++++++++++++++++ mock/json/get_ml_model.json | 34 ++++++++++++++ mock/json/get_ml_models.json | 69 ++++++++++++++++++++++++++++ mock/lytics_mock.go | 1 + mock/ml_mock.go | 51 +++++++++++++++++++++ 6 files changed, 288 insertions(+) create mode 100644 ml.go create mode 100644 ml_test.go create mode 100644 mock/json/get_ml_model.json create mode 100644 mock/json/get_ml_models.json create mode 100644 mock/ml_mock.go diff --git a/ml.go b/ml.go new file mode 100644 index 0000000..7468c1b --- /dev/null +++ b/ml.go @@ -0,0 +1,89 @@ +package lytics + +import ( + "time" +) + +const ( + mlEndpoint = "ml/:id" + mlListEndpoint = "ml" + // MLDepEndpoint = "segmentml/:id/_dependencies" +) + +// type MLConfigs struct { +// mlConfigs []ML +// } + +// ML Struct +type ML struct { + Aid int `json:"aid"` + Config struct { + AutoTune bool `json:"auto_tune"` + BuildOnly bool `json:"build_only"` + Collect int `json:"collect"` + CustomSegmentIds interface{} `json:"custom_segment_ids"` + Internal bool `json:"internal"` + ModelType string `json:"model_type"` + ReRun bool `json:"re_run"` + TuneModel bool `json:"tune_model"` + UseContent bool `json:"use_content"` + UseEntStore bool `json:"use_ent_store"` + UseScores bool `json:"use_scores"` + } `json:"config"` + Created time.Time `json:"created"` + ID string `json:"id"` + IsActive bool `json:"is_active"` + IsHealthy bool `json:"is_healthy"` + Label string `json:"label"` + Name string `json:"name"` + Source string `json:"source"` + State string `json:"state"` + Target string `json:"target"` + Type string `json:"type"` + Updated time.Time `json:"updated"` + WorkIds []string `json:"work_ids"` +} + +// GetMLModel returns the details for a single ML Model based on id +// https://www.getlytics.com/developers/rest-api#segment-m-l +func (l *Client) GetMLModel(id string) (ML, error) { + res := ApiResp{} + data := ML{} + + // make the request + err := l.Get(parseLyticsURL(mlEndpoint, map[string]string{"id": id}), nil, nil, &res, &data) + if err != nil { + return ML{}, err + } + return data, nil +} + +// GetMLModels returns all ML models for the account +// https://www.getlytics.com/developers/rest-api#segment-m-l +func (l *Client) GetMLModels() ([]ML, error) { + res := ApiResp{} + data := []ML{} + + // make the request + err := l.Get(mlListEndpoint, nil, nil, &res, &data) + if err != nil { + return data, err + } + + return data, nil +} + +// Headers returns headers for tablewriter +func (m ML) Headers() []interface{} { + return []interface{}{ + "Name", "ID", "Source", "Target", "Type", "Active", "Healthy", + } +} + +// Row returns a row for tablewriter +func (m ML) Row() []interface{} { + // c := m.Config + return []interface{}{ + m.Name, m.ID, m.Source, m.Target, m.Type, m.IsActive, m.IsHealthy, + } +} diff --git a/ml_test.go b/ml_test.go new file mode 100644 index 0000000..60230b0 --- /dev/null +++ b/ml_test.go @@ -0,0 +1,44 @@ +package lytics + +import ( + "testing" + + "github.com/bmizerany/assert" + "github.com/jarcoal/httpmock" + "github.com/lytics/go-lytics/mock" +) + +func TestGetMLModel(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + mock.RegisterMLMocks() + + client := NewLytics(mock.MockApiKey, nil) + model, err := client.GetMLModel(mock.MockMLID) + + assert.Equal(t, err, nil) + assert.Equal(t, model.ID, mock.MockMLID) + assert.Equal(t, model.Config.AutoTune, false) +} + +func TestGetMLModels(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + mock.RegisterMLMocks() + + client := NewLytics(mock.MockApiKey, nil) + models, err := client.GetMLModels() + + assert.Equal(t, err, nil) + assert.Equal(t, len(models), 2) + + first := models[0] + + assert.Equal(t, first.IsActive, false) + assert.Equal(t, first.Name, "all::ly_binge_user") + + second := models[1] + + assert.Equal(t, second.Name, "all::ly_infrequent_user") + assert.Equal(t, second.IsHealthy, true) +} diff --git a/mock/json/get_ml_model.json b/mock/json/get_ml_model.json new file mode 100644 index 0000000..494cea0 --- /dev/null +++ b/mock/json/get_ml_model.json @@ -0,0 +1,34 @@ +{ + "data": { + "aid": 1, + "config": { + "auto_tune": false, + "build_only": false, + "collect": 0, + "custom_segment_ids": null, + "internal": false, + "model_type": "", + "re_run": false, + "tune_model": false, + "use_content": false, + "use_ent_store": false, + "use_scores": false + }, + "created": "2020-10-24T00:11:22.842Z", + "id": "d3df420e45ed9ce36d95d5ad89b0ff37", + "is_active": false, + "is_healthy": false, + "label": "", + "name": "test01", + "source": "0d560ecb1d6024ceb0d738d114a6bc1a", + "state": "building", + "target": "c15f12e27b791652845c05458c95e5a3", + "type": "", + "updated": "2020-10-24T04:27:24.827Z", + "work_ids": [ + "829c1088092e59189c4c4f52488b3fc4" + ] + }, + "message": "success", + "status": 200 +} \ No newline at end of file diff --git a/mock/json/get_ml_models.json b/mock/json/get_ml_models.json new file mode 100644 index 0000000..4240560 --- /dev/null +++ b/mock/json/get_ml_models.json @@ -0,0 +1,69 @@ +{ + "data": [{ + "aid": 1, + "config": { + "auto_tune": false, + "build_only": true, + "collect": 0, + "custom_segment_ids": null, + "internal": false, + "model_type": "", + "re_run": false, + "tune_model": false, + "use_content": false, + "use_ent_store": false, + "use_scores": false + }, + "created": "2020-10-23T22:06:45.972Z", + "id": "3a539197927ed11b0124d7d34a642174", + "is_active": false, + "is_healthy": true, + "label": "", + "name": "all::ly_binge_user", + "source": "757157f2b33143c04a247d93e71e390b", + "state": "complete", + "target": "22188a35b22888715066812682af8d3c", + "type": "", + "updated": "2020-10-23T22:55:36.755Z", + "work_ids": [ + "6db152e9b673cb50192b428fd5fe3377", + "7830f8ace7ae7b0d7d6e995a7825b189", + "e196086c3245fcc826d5ef03b6e2d20b" + ] + }, + { + "aid": 1, + "config": { + "auto_tune": false, + "build_only": true, + "collect": 0, + "custom_segment_ids": null, + "internal": false, + "model_type": "", + "re_run": false, + "tune_model": false, + "use_content": false, + "use_ent_store": false, + "use_scores": false + }, + "created": "2020-10-23T22:07:00.753Z", + "id": "05e49527930663222458428f3ebc9775", + "is_active": false, + "is_healthy": true, + "label": "", + "name": "all::ly_infrequent_user", + "source": "757157f2b33143c04a247d93e71e390b", + "state": "complete", + "target": "0d560ecb1d6024ceb0d738d114a6bc1a", + "type": "", + "updated": "2020-10-23T23:34:04.836Z", + "work_ids": [ + "17951e000baae7e6e39a1971a1eb5f31", + "5b0e6b75dde8df02e1501aca0675a9be", + "7715f7964f766f556d11324b864f94ca" + ] + } + ], + "message": "success", + "status": 200 +} \ No newline at end of file diff --git a/mock/lytics_mock.go b/mock/lytics_mock.go index f006b18..47377cf 100644 --- a/mock/lytics_mock.go +++ b/mock/lytics_mock.go @@ -18,6 +18,7 @@ const ( MockCampaignID = "4a5984e1138646bb9d692c" MockVariationID = "572d930921c447348ed424" MockSegmentMLID = "all::active_prospects" + MockMLID = "d3df420e45ed9ce36d95d5ad89b0ff37" MockTopicID = "mock" ) diff --git a/mock/ml_mock.go b/mock/ml_mock.go new file mode 100644 index 0000000..cc34f5a --- /dev/null +++ b/mock/ml_mock.go @@ -0,0 +1,51 @@ +package mock + +import ( + "net/http" + + "github.com/jarcoal/httpmock" +) + +func RegisterMLMocks() { + // ******************************************************* + // GET SINGLE ML MODEL + // ******************************************************* + httpmock.RegisterResponder("GET", "https://api.lytics.io/api/ml/"+MockMLID, + func(req *http.Request) (*http.Response, error) { + var fail bool + + queries := req.URL.Query() + + if queries.Get("key") != MockApiKey { + fail = true + } + + if fail { + return httpmock.NewStringResponse(401, readJsonFile("get_error")), nil + } + + return httpmock.NewStringResponse(200, readJsonFile("get_ml_model")), nil + }, + ) + + // ******************************************************* + // GET ALL ML MODELS + // ******************************************************* + httpmock.RegisterResponder("GET", "https://api.lytics.io/api/ml", + func(req *http.Request) (*http.Response, error) { + var fail bool + + queries := req.URL.Query() + + if queries.Get("key") != MockApiKey { + fail = true + } + + if fail { + return httpmock.NewStringResponse(401, readJsonFile("get_error")), nil + } + + return httpmock.NewStringResponse(200, readJsonFile("get_ml_models")), nil + }, + ) +} From 14b8e3839a031bd8d47f2166a357f3bc74fb1f5b Mon Sep 17 00:00:00 2001 From: Steve Layton Date: Fri, 5 Feb 2021 14:39:49 -0800 Subject: [PATCH 2/2] cleaned up comments corrected doc URL --- ml.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ml.go b/ml.go index 7468c1b..e545fcc 100644 --- a/ml.go +++ b/ml.go @@ -7,13 +7,8 @@ import ( const ( mlEndpoint = "ml/:id" mlListEndpoint = "ml" - // MLDepEndpoint = "segmentml/:id/_dependencies" ) -// type MLConfigs struct { -// mlConfigs []ML -// } - // ML Struct type ML struct { Aid int `json:"aid"` @@ -45,7 +40,7 @@ type ML struct { } // GetMLModel returns the details for a single ML Model based on id -// https://www.getlytics.com/developers/rest-api#segment-m-l +// https://learn.lytics.com/documentation/developer/api-docs/ml func (l *Client) GetMLModel(id string) (ML, error) { res := ApiResp{} data := ML{} @@ -59,7 +54,7 @@ func (l *Client) GetMLModel(id string) (ML, error) { } // GetMLModels returns all ML models for the account -// https://www.getlytics.com/developers/rest-api#segment-m-l +// https://learn.lytics.com/documentation/developer/api-docs/ml func (l *Client) GetMLModels() ([]ML, error) { res := ApiResp{} data := []ML{}