Commit 5de5ebaee0eb9523962d4a571c09ff8b157280e6

Authored by Alex Savenko
1 parent c6c9c77e

фиксы по гуглу: множественные дименшины, сортировка, лимит, трансформер для графиков

app/library/App/Controllers/GaController.php
@@ -10,12 +10,15 @@ namespace App\Controllers; @@ -10,12 +10,15 @@ namespace App\Controllers;
10 10
11 11
12 use App\Model\Project; 12 use App\Model\Project;
  13 +use Codeception\Exception\ContentNotFound;
  14 +use DateTime;
13 use Google_Client; 15 use Google_Client;
14 use Google_Service_AnalyticsReporting; 16 use Google_Service_AnalyticsReporting;
15 use Google_Service_AnalyticsReporting_DateRange; 17 use Google_Service_AnalyticsReporting_DateRange;
16 use Google_Service_AnalyticsReporting_Dimension; 18 use Google_Service_AnalyticsReporting_Dimension;
17 use Google_Service_AnalyticsReporting_GetReportsRequest; 19 use Google_Service_AnalyticsReporting_GetReportsRequest;
18 use Google_Service_AnalyticsReporting_Metric; 20 use Google_Service_AnalyticsReporting_Metric;
  21 +use Google_Service_AnalyticsReporting_OrderBy;
19 use Google_Service_AnalyticsReporting_ReportRequest; 22 use Google_Service_AnalyticsReporting_ReportRequest;
20 use PhalconRest\Mvc\Controllers\CrudResourceController; 23 use PhalconRest\Mvc\Controllers\CrudResourceController;
21 24
@@ -25,6 +28,11 @@ class GaController extends CrudResourceController { @@ -25,6 +28,11 @@ class GaController extends CrudResourceController {
25 const VIEW_ID = '119240817'; 28 const VIEW_ID = '119240817';
26 const SCOPE = 'https://www.googleapis.com/auth/analytics.readonly'; 29 const SCOPE = 'https://www.googleapis.com/auth/analytics.readonly';
27 30
  31 + /**
  32 + * Main action for /ga request. Send it google report api.
  33 + *
  34 + * @return array
  35 + */
28 public function getAction() { 36 public function getAction() {
29 37
30 /** user params **/ 38 /** user params **/
@@ -38,22 +46,53 @@ class GaController extends CrudResourceController { @@ -38,22 +46,53 @@ class GaController extends CrudResourceController {
38 $get_start_date = $this->request->get('start') ?? '30daysAgo'; 46 $get_start_date = $this->request->get('start') ?? '30daysAgo';
39 $get_end_date = $this->request->get('end') ?? 'today'; 47 $get_end_date = $this->request->get('end') ?? 'today';
40 $filter_expression = $this->request->get('filter'); 48 $filter_expression = $this->request->get('filter');
41 -  
42 -  
43 - if (empty($view_id)) {  
44 - $result = [];  
45 - $projects = Project::find(['user_id' => $user_id]);  
46 - foreach ($projects as $project) {  
47 - $view_id = (string)$project->ga_view_id;  
48 - if (!empty($view_id)) {  
49 - $result[] = $this->sendGaRequest($project->name, $view_id, $get_metrics, $get_dimensions, $get_start_date, $get_end_date, $chart, $filter_expression); 49 + $sort = $this->request->get('sort');
  50 + $sort_type = $this->request->get('sort_type');
  51 + $max_result = $this->request->get('max_result');
  52 +
  53 +
  54 + /** if empty $_GET["view_id"] send request to all projects in user's model **/
  55 + if (empty($view_id)) {
  56 + $result = [];
  57 + $projects = Project::find(['user_id' => $user_id]);
  58 + foreach ($projects as $project) {
  59 + $view_id = (string)$project->ga_view_id;
  60 + if (!empty($view_id)) {
  61 + $result[] = $this->sendGaRequest(
  62 + $project->name,
  63 + $view_id,
  64 + $get_metrics,
  65 + $get_dimensions,
  66 + $get_start_date,
  67 + $get_end_date,
  68 + $chart,
  69 + $filter_expression,
  70 + $sort,
  71 + $sort_type,
  72 + $max_result
  73 + );
  74 + }
50 } 75 }
51 } 76 }
52 - }  
53 - else {  
54 - $project = Project::findFirst(['ga_view_id' => $view_id]);  
55 - $result = $this->sendGaRequest($project->name , $view_id, $get_metrics, $get_dimensions, $get_start_date, $get_end_date, $chart, $filter_expression);  
56 - } 77 + else {
  78 + $project = Project::findFirst([
  79 + "ga_view_id = '$view_id'",
  80 + ]);
  81 + $result = $this->sendGaRequest(
  82 + $project->name,
  83 + $view_id,
  84 + $get_metrics,
  85 + $get_dimensions,
  86 + $get_start_date,
  87 + $get_end_date,
  88 + $chart,
  89 + $filter_expression,
  90 + $sort,
  91 + $sort_type,
  92 + $max_result
  93 + );
  94 + }
  95 + /** --------------------------------------------------------------- **/
57 return $result; 96 return $result;
58 97
59 } 98 }
@@ -69,9 +108,12 @@ class GaController extends CrudResourceController { @@ -69,9 +108,12 @@ class GaController extends CrudResourceController {
69 * @param string $end 108 * @param string $end
70 * @param bool $chart 109 * @param bool $chart
71 * @param string $filter_expression 110 * @param string $filter_expression
  111 + * @param string $sort
  112 + * @param string $sort_type
  113 + * @param int $max_result
72 * @return array 114 * @return array
73 */ 115 */
74 - public function sendGaRequest($project_name, $view, $get_metrics, $get_dimensions, $start, $end, $chart = false, $filter_expression = null) { 116 + public function sendGaRequest($project_name, $view, $get_metrics, $get_dimensions, $start, $end, $chart = false, $filter_expression = null, $sort = null, $sort_type = 'desc', $max_result = 50000) {
75 117
76 putenv('GOOGLE_APPLICATION_CREDENTIALS=/var/www/phalcon/'.self::SECRET_JSON); 118 putenv('GOOGLE_APPLICATION_CREDENTIALS=/var/www/phalcon/'.self::SECRET_JSON);
77 $client = new Google_Client(); 119 $client = new Google_Client();
@@ -80,57 +122,89 @@ class GaController extends CrudResourceController { @@ -80,57 +122,89 @@ class GaController extends CrudResourceController {
80 $analytics = new Google_Service_AnalyticsReporting($client); 122 $analytics = new Google_Service_AnalyticsReporting($client);
81 123
82 /** Create the DateRange object. **/ 124 /** Create the DateRange object. **/
83 - $dateRange = new Google_Service_AnalyticsReporting_DateRange();  
84 - $dateRange->setStartDate($start);  
85 - $dateRange->setEndDate($end); 125 + $dateRange = new Google_Service_AnalyticsReporting_DateRange();
  126 + $dateRange->setStartDate($start);
  127 + $dateRange->setEndDate($end);
  128 + /** ---------------------------- **/
86 129
87 /** Create the Metrics object. **/ 130 /** Create the Metrics object. **/
88 - $metrics = [];  
89 - $get_metrics = explode(',', $get_metrics);  
90 - foreach ($get_metrics as $metric) {  
91 - $metrics_obj = new Google_Service_AnalyticsReporting_Metric();  
92 - $metrics_obj->setExpression('ga:'.$metric);  
93 - $metrics_obj->setAlias('ga:'.$metric);  
94 - $metrics[] = $metrics_obj;  
95 - } 131 + $metrics = [];
  132 + $get_metrics = explode(',', $get_metrics);
  133 + foreach ($get_metrics as $metric) {
  134 + $metrics_obj = new Google_Service_AnalyticsReporting_Metric();
  135 + $metrics_obj->setExpression('ga:'.$metric);
  136 + $metrics_obj->setAlias('ga:'.$metric);
  137 + $metrics[] = $metrics_obj;
  138 + }
  139 + /** -------------------------- **/
96 140
97 /** Create the Dimensions object. **/ 141 /** Create the Dimensions object. **/
98 - if (!empty($get_dimensions)) {  
99 - $dimensions = [];  
100 - $get_dimensions = explode(',', $get_dimensions);  
101 - foreach ($get_dimensions as $dimension) {  
102 - $dimension_obj = new Google_Service_AnalyticsReporting_Dimension();  
103 - $dimension_obj->setName("ga:".$dimension);  
104 - $dimensions[] = $dimension_obj; 142 + if (!empty($get_dimensions)) {
  143 + $dimensions = [];
  144 + $get_dimensions = explode(',', $get_dimensions);
  145 + foreach ($get_dimensions as $dimension) {
  146 + $dimension_obj = new Google_Service_AnalyticsReporting_Dimension();
  147 + $dimension_obj->setName("ga:".$dimension);
  148 + $dimensions[] = $dimension_obj;
  149 + }
105 } 150 }
106 - } 151 + /** ----------------------------- **/
107 152
108 /** Create the ReportRequest object. **/ 153 /** Create the ReportRequest object. **/
109 - $request = new Google_Service_AnalyticsReporting_ReportRequest();  
110 - $request->setViewId($view);  
111 - $request->setDateRanges($dateRange);  
112 - $request->setIncludeEmptyRows(true);  
113 - if (!empty($dimensions)) {  
114 - $request->setDimensions(array($dimensions));  
115 - }  
116 - $request->setMetrics(array($metrics));  
117 - if (!empty($filter_expression)) {  
118 - $request->setFiltersExpression("ga:".$filter_expression);  
119 - } 154 + $request = new Google_Service_AnalyticsReporting_ReportRequest();
  155 + $request->setViewId($view);
  156 + $request->setPageSize($max_result);
  157 + $request->setDateRanges($dateRange);
  158 + $request->setIncludeEmptyRows(true);
  159 + /** Create the Ordering **/
  160 + if (isset($sort)) {
  161 + $ordering = new Google_Service_AnalyticsReporting_OrderBy();
  162 + $ordering->setFieldName("ga:".$sort);
  163 + $ordering->setOrderType("VALUE");
  164 + $ordering->setSortOrder("DESCENDING");
  165 + if ($sort_type == 'asc') {
  166 + $ordering->setSortOrder("ASCENDING");
  167 + }
  168 + $request->setOrderBys($ordering);
  169 + }
  170 + /** --------------- **/
  171 + if (!empty($dimensions)) {
  172 + $request->setDimensions(array($dimensions));
  173 + }
  174 + $request->setMetrics(array($metrics));
  175 + if (!empty($filter_expression)) {
  176 + $request->setFiltersExpression("ga:".$filter_expression);
  177 + }
  178 + /** compute days in request **/
  179 + $request_date['start'] = new DateTime(date('d.m.Y', strtotime($request->getDateRanges()['startDate'])));
  180 + $request_date['end'] = new DateTime(date('d.m.Y', strtotime($request->getDateRanges()['endDate'])));
  181 + $request_days = (date_diff($request_date['start'], $request_date['end'])->days)+1;
  182 + /** ----------------------- **/
  183 + $request_dim = $request->getDimensions();
  184 + if (count($request_dim[0]) == 2) {
  185 + $request_dim = $request_dim[0][1]->name;
  186 + }
  187 + else {
  188 + $request_dim = $request_dim[0][0]->name;
  189 + }
  190 + $iterations = self::countIterations($request_dim, $request_days);
  191 + /** ---------------------------- **/
120 192
121 $body = new Google_Service_AnalyticsReporting_GetReportsRequest(); 193 $body = new Google_Service_AnalyticsReporting_GetReportsRequest();
122 $body->setReportRequests(array($request)); 194 $body->setReportRequests(array($request));
123 195
124 $response = $analytics->reports->batchGet($body); 196 $response = $analytics->reports->batchGet($body);
125 197
126 - /** can be refactored (code below 2 rows) **/ 198 + //can be refactored (code below, 2 rows)
127 $response = $response->toSimpleObject(); 199 $response = $response->toSimpleObject();
128 $response = $response->reports[0]['data']['rows']; 200 $response = $response->reports[0]['data']['rows'];
129 201
130 if ($chart) { 202 if ($chart) {
131 - $result = self::responseChartTransform($response, $project_name); 203 + //$result = self::responseChartTransform($response, $project_name);
  204 + $result = self::responseDataTransform($response, $iterations, $request_dim, $project_name);
  205 + $result = self::chartTransform($result);
132 } else { 206 } else {
133 - $result = self::responseDataTransform($response, $project_name); 207 + $result = self::responseDataTransform($response, $iterations, $request_dim, $project_name);
134 } 208 }
135 209
136 return $result; 210 return $result;
@@ -141,29 +215,84 @@ class GaController extends CrudResourceController { @@ -141,29 +215,84 @@ class GaController extends CrudResourceController {
141 * Data-transformer for tables. Used by default. 215 * Data-transformer for tables. Used by default.
142 * 216 *
143 * @param array $response 217 * @param array $response
  218 + * @param int $iterations
  219 + * @param string $request_dimension
144 * @param string $project_name 220 * @param string $project_name
145 * @return array 221 * @return array
146 */ 222 */
147 - public static function responseDataTransform(array $response, $project_name) { 223 + public static function responseDataTransform(array $response, $iterations, $request_dimension, $project_name) {
148 224
149 - $result = []; 225 + $result = [];
  226 + $int_query = true;
  227 + $max_values = 0;
150 228
151 foreach ($response as $item) { 229 foreach ($response as $item) {
152 230
153 $metric_val = $item['metrics'][0]['values']; 231 $metric_val = $item['metrics'][0]['values'];
154 $dimension_val = $item['dimensions'][0]; 232 $dimension_val = $item['dimensions'][0];
  233 + $dimension_count = count($item['dimensions']);
  234 +
  235 + /** remove "0001" from dimension keys **/
  236 + for ($i = 0; $i < $dimension_count; $i++) {
  237 + $current_value = $item['dimensions'][$i];
  238 + if (ctype_digit(strval($current_value))) {
  239 + $item['dimensions'][$i] = (int)$current_value;
  240 + if ($i == 0) {
  241 + $dimension_val = (int)$current_value;
  242 + }
  243 + }
  244 + elseif ($i == 1) {
  245 + $int_query = false;
  246 + }
  247 + }
  248 + /** --------------------------------- **/
155 249
156 - $result['name'] = $project_name;  
157 250
158 - if (count($metric_val) > 1) {  
159 - for ($i = 0; $i < count($metric_val); $i++) {  
160 - $result[$dimension_val][] = $metric_val[$i]; 251 + if ($dimension_count == 2) {
  252 + $dimension_val_2 = $item['dimensions'][1];
  253 + if (count($metric_val) > 1) {
  254 + for ($i = 0; $i < count($metric_val); $i++) {
  255 + $result[$dimension_val][$dimension_val_2][] = (int)$metric_val[$i];
  256 + }
  257 + } else {
  258 + $result[$dimension_val][$dimension_val_2] = (int)$metric_val[0];
  259 + }
  260 + }
  261 + else {
  262 + if (count($metric_val) > 1) {
  263 + for ($i = 0; $i < count($metric_val); $i++) {
  264 + $result[$dimension_val][] = (int)$metric_val[$i];
  265 + }
  266 + } else {
  267 + $result[$dimension_val] = (int)$metric_val[0];
161 } 268 }
162 - } else {  
163 - $result[$dimension_val] = $metric_val[0];  
164 } 269 }
  270 +
  271 + $dim_val_count = count($result[$dimension_val]);
  272 + if ($dim_val_count > $max_values) $max_values = $dim_val_count;
  273 + unset($dim_val_count);
  274 +
165 } 275 }
166 276
  277 + $int_query = self::checkDimension($request_dimension);
  278 +
  279 + /** ------ filling missing data ------ **/
  280 + if ($int_query) {
  281 + foreach ($result as $key => $item) {
  282 + if (!is_array($item)) break;
  283 + $iterations = $iterations ?? $max_values;
  284 + for ($i = 0; $i < $iterations; $i++) {
  285 + $result[$key][$i] = $item[$i] ?? 0;
  286 + }
  287 + ksort($result[$key]);
  288 + }
  289 + }
  290 + /** --------------------------------- **/
  291 +
  292 + /** ----- add custom fields ------ **/
  293 + $result['name'] = $project_name ?? 'Неизвестный проект';
  294 + /** ------------------------------ **/
  295 +
167 return $result; 296 return $result;
168 297
169 } 298 }
@@ -171,6 +300,91 @@ class GaController extends CrudResourceController { @@ -171,6 +300,91 @@ class GaController extends CrudResourceController {
171 /** 300 /**
172 * Data-transformer for charts, when query string contains "?chart=true" 301 * Data-transformer for charts, when query string contains "?chart=true"
173 * 302 *
  303 + * @param array $data
  304 + * @return array
  305 + */
  306 + public static function chartTransform(array $data) {
  307 + $result = [];
  308 + foreach ($data as $key => $value) {
  309 + if ($key === 'name') {
  310 + $result[$key] = $value;
  311 + }
  312 + else {
  313 + if (!is_array($value)) {
  314 + $result['data'][] = $value;
  315 + }
  316 + else {
  317 + foreach ($value as $v_key => $v_value) {
  318 + $result['data'][$key][$v_key] = $v_value;
  319 + }
  320 + ksort($result['data'][$key]);
  321 + }
  322 + }
  323 + }
  324 +
  325 + return $result;
  326 + }
  327 +
  328 + /**
  329 + * Deprecated
  330 + *
  331 + * @param array $data
  332 + * @return array
  333 + */
  334 + public static function chartTransform1(array $data) {
  335 +
  336 + $result = [];
  337 + $max = 0;
  338 +
  339 + foreach ($data as $key => $value) {
  340 + if ($key == 'name') {
  341 + $result[$key] = $value;
  342 + continue;
  343 + }
  344 +
  345 + if (!is_array($value)) break;
  346 +
  347 + /** ------ check array keys for int values --- **/
  348 + $count = count($value);
  349 + if ($count > $max) $max = $count;
  350 + $int_type = true;
  351 + foreach ($value as $inc_key => $inc_value) {
  352 + if (!preg_match('/\d+/', $inc_key)) {
  353 + $int_type = false;
  354 + break;
  355 + }
  356 + }
  357 + /** ------------------------------------------ **/
  358 +
  359 + /** rewrites keys like "0001" to integer type **/
  360 + if ($int_type) {
  361 + $bad_keys = array_keys($value);
  362 + for ($i = 0; $i < $count; $i++) {
  363 + $good_key = (int)$bad_keys[$i];
  364 + $result[$key][$good_key] = $value[$bad_keys[$i]] ?? 0;
  365 + }
  366 + }
  367 + /** ------------------------------------------ **/
  368 +
  369 + }
  370 +
  371 + /** ---------- filling missing data ---------- **/
  372 + foreach ($result as $key => $value) {
  373 + if ($key == 'name') continue;
  374 + for ($i = 0; $i < $max; $i++) {
  375 + $result[$key][$i] = (int)$result[$key][$i] ?? 0;
  376 + }
  377 + ksort($result[$key]);
  378 + }
  379 + /** ------------------------------------------ **/
  380 +
  381 + return $result;
  382 +
  383 + }
  384 +
  385 + /**
  386 + * Deprecated
  387 + *
174 * @param array $response 388 * @param array $response
175 * @param string $project_name 389 * @param string $project_name
176 * @return array 390 * @return array
@@ -181,15 +395,30 @@ class GaController extends CrudResourceController { @@ -181,15 +395,30 @@ class GaController extends CrudResourceController {
181 395
182 foreach ($response as $item) { 396 foreach ($response as $item) {
183 397
184 - $metric_val = $item['metrics'][0]['values'];  
185 $result['name'] = $project_name; 398 $result['name'] = $project_name;
186 399
187 - if (count($metric_val) > 1) {  
188 - for ($i = 0; $i < count($metric_val); $i++) {  
189 - $result['data'][] = (int)$metric_val[$i]; 400 + $metric_val = $item['metrics'][0]['values'];
  401 + $dimension_val = $item['dimensions'][0];
  402 + $dimension_count = count($item['dimensions']);
  403 +
  404 + if ($dimension_count == 2) {
  405 + $dimension_val_2 = $item['dimensions'][1];
  406 + if (count($metric_val) > 1) {
  407 + for ($i = 0; $i < count($metric_val); $i++) {
  408 + $result['data'][$dimension_val][] = (int)$metric_val[$i];
  409 + }
  410 + } else {
  411 + $result['data'][$dimension_val][] = (int)$metric_val[0];
  412 + }
  413 + }
  414 + else {
  415 + if (count($metric_val) > 1) {
  416 + for ($i = 0; $i < count($metric_val); $i++) {
  417 + $result['data'][] = (int)$metric_val[$i];
  418 + }
  419 + } else {
  420 + $result['data'][] = (int)$metric_val[0];
190 } 421 }
191 - } else {  
192 - $result['data'][] = (int)$metric_val[0];  
193 } 422 }
194 } 423 }
195 424
@@ -197,6 +426,49 @@ class GaController extends CrudResourceController { @@ -197,6 +426,49 @@ class GaController extends CrudResourceController {
197 426
198 } 427 }
199 428
  429 + /**
  430 + * Compute count of fields
  431 + *
  432 + * @param string $request_dim
  433 + * @param int $request_days
  434 + * @return int
  435 + * @throws ContentNotFound if functions params is empty
  436 + */
  437 + public static function countIterations($request_dim, $request_days) {
  438 +
  439 + if (empty($request_dim)) throw new ContentNotFound('PHP: request_dim not found');
  440 + if (empty($request_days)) throw new ContentNotFound('PHP: request_days not found');
  441 + switch ($request_dim) {
  442 + case 'ga:nthDay':
  443 + $iterations = $request_days*1;
  444 + break;
  445 + case 'ga:nthHour':
  446 + $iterations = $request_days*24;
  447 + break;
  448 + case 'ga:nthMinute':
  449 + $iterations = $request_days*24*60;
  450 + break;
  451 + default:
  452 + $iterations = null;
  453 + }
  454 +
  455 + return $iterations;
  456 +
  457 + }
  458 +
  459 + /**
  460 + * Boolean indicator for chart transformer
  461 + *
  462 + * @param string $dimension
  463 + * @return bool
  464 + */
  465 + public static function checkDimension($dimension) {
  466 +
  467 + $nthArray = ['ga:nthMonth', 'ga:nthWeek', 'ga:nthDay', 'ga:nthMinute', 'ga:nthHour'];
  468 +
  469 + return in_array($dimension, $nthArray) ? true : false;
  470 +
  471 + }
200 472
201 /** 473 /**
202 * without usage, from google docs. 474 * without usage, from google docs.
app/library/App/Controllers/UserController.php
@@ -2,7 +2,6 @@ @@ -2,7 +2,6 @@
2 2
3 namespace App\Controllers; 3 namespace App\Controllers;
4 4
5 -use App\Model\Project;  
6 use App\Model\User; 5 use App\Model\User;
7 use PhalconApi\Constants\ErrorCodes; 6 use PhalconApi\Constants\ErrorCodes;
8 use PhalconApi\Exception; 7 use PhalconApi\Exception;
app/library/App/Resources/GaResource.php
@@ -21,7 +21,6 @@ class GaResource extends ApiResource { @@ -21,7 +21,6 @@ class GaResource extends ApiResource {
21 $this 21 $this
22 ->name('Google Analytics') 22 ->name('Google Analytics')
23 ->expectsJsonData() 23 ->expectsJsonData()
24 - //->transformer(ModelTransformer::class)  
25 ->itemKey('ga') 24 ->itemKey('ga')
26 ->collectionKey('ga') 25 ->collectionKey('ga')
27 ->deny(AclRoles::UNAUTHORIZED) 26 ->deny(AclRoles::UNAUTHORIZED)
@@ -49,13 +48,15 @@ class GaResource extends ApiResource { @@ -49,13 +48,15 @@ class GaResource extends ApiResource {
49 'end' => 'ГГГГ-ММ-ДД/NdaysAgo, где N – целое положительное число(дата конца загрузки данных)' 48 'end' => 'ГГГГ-ММ-ДД/NdaysAgo, где N – целое положительное число(дата конца загрузки данных)'
50 ], 49 ],
51 'optional params' => [ 50 'optional params' => [
52 - 'user_id' => 'integer(id пользователя в системе)',  
53 - 'view_id' => 'integer(id представления проэкта с гугл аналитики)',  
54 - 'chart' => 'boolean(Задает структуру возвращаемых данных(true для графиков))',  
55 - 'filter' => 'expression(https://developers.google.com/analytics/devguides/reporting/core/v3/reference#filters)', 51 + 'user_id' => 'integer(id пользователя в системе)',
  52 + 'view_id' => 'integer(id представления проэкта с гугл аналитики)',
  53 + 'chart' => 'boolean(Задает структуру возвращаемых данных(true для графиков))',
  54 + 'filter' => 'expression(https://developers.google.com/analytics/devguides/reporting/core/v3/reference#filters)',
  55 + 'sort' => 'string(параметр сортировки, metric либо dimension)',
  56 + 'sort_type' => 'enum(ans, desc)',
  57 + 'max_result' => 'integer(максимальное число строк в результате, по умолчанию 50,000)',
56 ] 58 ]
57 - ],  
58 - 'summary' => '/ga?view_id=119240817&metric=users,sessions&dimension=source,browser&filter=browser=~^Firef' 59 + ]
59 ]) 60 ])
60 ) 61 )
61 ; 62 ;