add analytics methods

This commit is contained in:
diPhantxm
2023-03-13 23:29:39 +03:00
parent 46a60cef6f
commit d1565b9d00
5 changed files with 280 additions and 8 deletions

View File

@@ -102,8 +102,8 @@ func isZero(v interface{}) (bool, error) {
return v == reflect.Zero(t).Interface(), nil return v == reflect.Zero(t).Interface(), nil
} }
func TimeFromString(t *testing.T, datetime string) time.Time { func TimeFromString(t *testing.T, format, datetime string) time.Time {
dt, err := time.Parse("2006-01-02T15:04:05Z", datetime) dt, err := time.Parse(format, datetime)
if err != nil { if err != nil {
t.Errorf("error when parsing time: %s", err) t.Errorf("error when parsing time: %s", err)
} }

167
ozon/analytics.go Normal file
View File

@@ -0,0 +1,167 @@
package ozon
import (
"net/http"
"time"
core "github.com/diphantxm/ozon-api-client"
)
type GetAnalyticsDataParams struct {
// Date from which the data will be in the report
DateFrom time.Time `json:"date_from"`
// Date up to which the data will be in the report
DateTo time.Time `json:"date_to"`
// Items Enum: "unknownDimension" "sku" "spu" "day" "week" "month" "year" "category1" "category2" "category3" "category4" "brand" "modelID"
Dimension []string `json:"dimension"`
// Filters
Filters []struct {
// Sorting parameter. You can pass any attribute from the `dimension` and `metric` parameters except the `brand` attribute
Key string `json:"key"`
// Comparison operation
//
// Enum: "EQ" "GT" "GTE" "LT" "LTE"
Operation string `json:"operation"`
// Value for comparison
Value string `json:"value"`
} `json:"filters"`
// Number of items in the respones:
// - maximum is 1000,
// - minimum is 1.
Limit int64 `json:"limit"`
// Specify up to 14 metrics. If there are more, you will get an error with the InvalidArgument code
//
// Items Enum: "unknown_metric" "hits_view_search" "hits_view_pdp" "hits_view" "hits_tocart_search" "hits_tocart_pdp" "hits_tocart" "session_view_search"
// "session_view_pdp" "session_view" "conv_tocart_search" "conv_tocart_pdp" "conv_tocart" "revenue" "returns" "cancellations" "ordered_units" "delivered_units"
// "adv_view_pdp" "adv_view_search_category" "adv_view_all" "adv_sum_all" "position_category" "postings" "postings_premium"
Metrics []string `json:"metrics"`
// Number of elements that will be skipped in the response. For example, if `offset=10`, the response will start with the 11th element found
Offset int64 `json:"offset"`
// Report sorting settings
Sort []GetAnalyticsDataSort `json:"sort"`
}
// Report sorting settings
type GetAnalyticsDataSort struct {
// Metric by which the method result will be sorted
Key string `json:"key"`
// Sorting type
// - ASC — in ascending order,
// - DESC — in descending order.
Order string `json:"order"`
}
type GetAnalyticsDataResponse struct {
core.CommonResponse
// Method result
Result struct {
// Data array
Data []struct {
// Data grouping in the report
Dimensions []struct {
// Identifier
Id string `json:"id"`
// Name
Name string `json:"name"`
} `json:"dimensions"`
// Metric values list
Metrics []float64 `json:"metrics"`
} `json:"data"`
// Total and average metrics values
Totals []float64 `json:"totals"`
} `json:"result"`
// Report creation time
Timestamp string `json:"timestamp"`
}
// Specify the period and metrics that are required. The response will contain analytical data grouped by the `dimensions` parameter.
func (c Client) GetAnalyticsData(params *GetAnalyticsDataParams) (*GetAnalyticsDataResponse, error) {
url := "/v1/analytics/data"
resp := &GetAnalyticsDataResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetStocksOnWarehousesParams struct {
// Number of values per page.
//
// Default is 100
Limit int64 `json:"limit"`
// Number of elements that will be skipped in the response. For example, if `offset=10`, the response will start with the 11th element found
Offset int64 `json:"offset"`
// Warehouse type filter:
// - EXPRESS_DARK_STORE — Ozon warehouses with Fresh delivery.
// - NOT_EXPRESS_DARK_STORE — Ozon warehouses without Fresh delivery.
// - ALL — all Ozon warehouses.
WarehouseType string `json:"warehouse_type"`
}
type GetStocksOnWarehousesResponse struct {
core.CommonResponse
// Method result
Result struct {
// Information about products and stocks
Rows []struct {
// Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
// Product identifier in the seller's system
ItemCode string `json:"item_code"`
// Product name in the Ozon system
ItemName string `json:"item_name"`
// Product amount available for sale on Ozon
FreeToSellAmount int64 `json:"free_to_sell_amount"`
// Product amount specified for confirmed future supplies
PromisedAmount int64 `json:"promised_amount"`
// Product amount reserved for purchase, returns, and transportation between warehouses
ReservedAmount int64 `json:"reserved_amount"`
// Name of the warehouse where the products are stored
WarehouseName string `json:"warehouse_name"`
} `json:"rows"`
} `json:"result"`
}
// Report on stocks and products movement at Ozon warehouses
func (c Client) GetStocksOnWarehouses(params *GetStocksOnWarehousesParams) (*GetStocksOnWarehousesResponse, error) {
url := "/v2/analytics/stock_on_warehouses"
resp := &GetStocksOnWarehousesResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

105
ozon/analytics_test.go Normal file
View File

@@ -0,0 +1,105 @@
package ozon
import (
"net/http"
"testing"
core "github.com/diphantxm/ozon-api-client"
)
func TestGetAnalyticsData(t *testing.T) {
tests := []struct {
statusCode int
headers map[string]string
params *GetAnalyticsDataParams
response string
}{
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetAnalyticsDataParams{
DateFrom: core.TimeFromString(t, "2006-01-02", "2020-09-01"),
DateTo: core.TimeFromString(t, "2006-01-02", "2021-10-15"),
Dimension: []string{"sku", "day"},
Metrics: []string{"hits_view_search"},
Sort: []GetAnalyticsDataSort{
{
Key: "hits_view_search",
Order: "DESC",
},
},
Limit: 1000,
Offset: 0,
},
`{
"result": {
"data": [],
"totals": [
0
]
},
"timestamp": "2021-11-25 15:19:21"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.GetAnalyticsData(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetStocksOnWarehouses(t *testing.T) {
tests := []struct {
statusCode int
headers map[string]string
params *GetStocksOnWarehousesParams
response string
}{
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetStocksOnWarehousesParams{
Limit: 1000,
Offset: 0,
WarehouseType: "ALL",
},
`{
"result": {
"rows": [
{
"free_to_sell_amount": 15,
"item_code": "my-code",
"item_name": "my-name",
"promised_amount": 12,
"reserved_amount": 11,
"sku": 12345,
"warehouse_name": "my-warehouse"
}
]
}
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.GetStocksOnWarehouses(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

View File

@@ -20,9 +20,9 @@ func TestGetFBOShipmentsList(t *testing.T) {
&GetFBOShipmentsListParams{ &GetFBOShipmentsListParams{
Direction: "ASC", Direction: "ASC",
Filter: GetFBOShipmentsListFilter{ Filter: GetFBOShipmentsListFilter{
Since: core.TimeFromString(t, "2021-09-01T00:00:00.000Z"), Since: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-09-01T00:00:00.000Z"),
Status: "awaiting_packaging", Status: "awaiting_packaging",
To: core.TimeFromString(t, "2021-11-17T10:44:12.828Z"), To: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-17T10:44:12.828Z"),
}, },
Limit: 5, Limit: 5,
Offset: 0, Offset: 0,

View File

@@ -20,8 +20,8 @@ func TestListUnprocessedShipments(t *testing.T) {
&ListUnprocessedShipmentsParams{ &ListUnprocessedShipmentsParams{
Direction: "ASC", Direction: "ASC",
Filter: ListUnprocessedShipmentsFilter{ Filter: ListUnprocessedShipmentsFilter{
CutoffFrom: core.TimeFromString(t, "2021-08-24T14:15:22Z"), CutoffFrom: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-08-24T14:15:22Z"),
CutoffTo: core.TimeFromString(t, "2021-08-31T14:15:22Z"), CutoffTo: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-08-31T14:15:22Z"),
Status: "awaiting_packaging", Status: "awaiting_packaging",
}, },
Limit: 100, Limit: 100,
@@ -178,8 +178,8 @@ func TestGetFBSShipmentsList(t *testing.T) {
&GetFBSShipmentsListParams{ &GetFBSShipmentsListParams{
Direction: "ASC", Direction: "ASC",
Filter: GetFBSShipmentsListFilter{ Filter: GetFBSShipmentsListFilter{
Since: core.TimeFromString(t, "2021-11-01T00:00:00.000Z"), Since: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-01T00:00:00.000Z"),
To: core.TimeFromString(t, "2021-12-01T23:59:59.000Z"), To: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-12-01T23:59:59.000Z"),
Status: "awaiting_packaging", Status: "awaiting_packaging",
}, },
Limit: 100, Limit: 100,