mirror of
https://github.com/getredash/redash.git
synced 2026-05-13 16:01:20 -04:00
Merge pull request #2533 from denisov-vlad/metrika
New datasource: Yandex Metrika&AppMetrika
This commit is contained in:
BIN
client/app/assets/images/db-logos/yandex_appmetrika.png
Normal file
BIN
client/app/assets/images/db-logos/yandex_appmetrika.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
client/app/assets/images/db-logos/yandex_metrika.png
Normal file
BIN
client/app/assets/images/db-logos/yandex_metrika.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 824 B |
166
redash/query_runner/yandex_metrika.py
Normal file
166
redash/query_runner/yandex_metrika.py
Normal file
@@ -0,0 +1,166 @@
|
||||
import json
|
||||
import yaml
|
||||
import logging
|
||||
from redash.query_runner import *
|
||||
from redash.utils import JSONEncoder
|
||||
import requests
|
||||
from urlparse import parse_qs, urlparse
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
COLUMN_TYPES = {
|
||||
'date': (
|
||||
'firstVisitDate', 'firstVisitStartOfYear', 'firstVisitStartOfQuarter',
|
||||
'firstVisitStartOfMonth', 'firstVisitStartOfWeek',
|
||||
),
|
||||
'datetime': (
|
||||
'firstVisitStartOfHour', 'firstVisitStartOfDekaminute', 'firstVisitStartOfMinute',
|
||||
'firstVisitDateTime', 'firstVisitHour', 'firstVisitHourMinute'
|
||||
|
||||
),
|
||||
'int': (
|
||||
'pageViewsInterval', 'pageViews', 'firstVisitYear', 'firstVisitMonth',
|
||||
'firstVisitDayOfMonth', 'firstVisitDayOfWeek', 'firstVisitMinute',
|
||||
'firstVisitDekaminute',
|
||||
)
|
||||
}
|
||||
|
||||
for type_, elements in COLUMN_TYPES.items():
|
||||
for el in elements:
|
||||
if 'first' in el:
|
||||
el = el.replace('first', 'last')
|
||||
COLUMN_TYPES[type_] += (el, )
|
||||
|
||||
|
||||
def parse_ym_response(response):
|
||||
columns = []
|
||||
dimensions_len = len(response['query']['dimensions'])
|
||||
|
||||
for h in response['query']['dimensions'] + response['query']['metrics']:
|
||||
friendly_name = h.split(':')[-1]
|
||||
if friendly_name in COLUMN_TYPES['date']:
|
||||
data_type = TYPE_DATE
|
||||
elif friendly_name in COLUMN_TYPES['datetime']:
|
||||
data_type = TYPE_DATETIME
|
||||
else:
|
||||
data_type = TYPE_STRING
|
||||
columns.append({'name': h, 'friendly_name': friendly_name, 'type': data_type})
|
||||
|
||||
rows = []
|
||||
for num, row in enumerate(response['data']):
|
||||
res = {}
|
||||
for i, d in enumerate(row['dimensions']):
|
||||
res[columns[i]['name']] = d['name']
|
||||
for i, d in enumerate(row['metrics']):
|
||||
res[columns[dimensions_len + i]['name']] = d
|
||||
if num == 0 and isinstance(d, float):
|
||||
columns[dimensions_len + i]['type'] = TYPE_FLOAT
|
||||
rows.append(res)
|
||||
|
||||
return {'columns': columns, 'rows': rows}
|
||||
|
||||
|
||||
class YandexMetrika(BaseSQLQueryRunner):
|
||||
@classmethod
|
||||
def annotate_query(cls):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def type(cls):
|
||||
return "yandex_metrika"
|
||||
|
||||
@classmethod
|
||||
def name(cls):
|
||||
return "Yandex Metrika"
|
||||
|
||||
@classmethod
|
||||
def configuration_schema(cls):
|
||||
return {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": {
|
||||
"type": "string",
|
||||
"title": "OAuth Token"
|
||||
}
|
||||
},
|
||||
"required": ["token"],
|
||||
}
|
||||
|
||||
def __init__(self, configuration):
|
||||
super(YandexMetrika, self).__init__(configuration)
|
||||
self.syntax = 'yaml'
|
||||
self.host = 'https://api-metrika.yandex.ru'
|
||||
self.list_path = 'counters'
|
||||
|
||||
def _get_tables(self, schema):
|
||||
|
||||
counters = self._send_query('management/v1/{0}'.format(self.list_path))
|
||||
|
||||
for row in counters[self.list_path]:
|
||||
owner = row.get('owner_login')
|
||||
counter = '{0} | {1}'.format(
|
||||
row.get('name', 'Unknown').encode('utf-8'), row.get('id', 'Unknown')
|
||||
)
|
||||
if owner not in schema:
|
||||
schema[owner] = {'name': owner, 'columns': []}
|
||||
|
||||
schema[owner]['columns'].append(counter)
|
||||
|
||||
return schema.values()
|
||||
|
||||
def test_connection(self):
|
||||
self._send_query('management/v1/{0}'.format(self.list_path))
|
||||
|
||||
def _send_query(self, path='stat/v1/data', **kwargs):
|
||||
token = kwargs.pop('oauth_token', self.configuration['token'])
|
||||
r = requests.get('{0}/{1}'.format(self.host, path), params=dict(oauth_token=token, **kwargs))
|
||||
if r.status_code != 200:
|
||||
raise Exception(r.text)
|
||||
return r.json()
|
||||
|
||||
def run_query(self, query, user):
|
||||
logger.debug("Metrika is about to execute query: %s", query)
|
||||
data = None
|
||||
query = query.strip()
|
||||
if query == "":
|
||||
error = "Query is empty"
|
||||
return data, error
|
||||
try:
|
||||
params = yaml.load(query)
|
||||
except ValueError as e:
|
||||
logging.exception(e)
|
||||
error = unicode(e)
|
||||
return data, error
|
||||
|
||||
if isinstance(params, dict):
|
||||
if 'url' in params:
|
||||
params = parse_qs(urlparse(params['url']).query, keep_blank_values=True)
|
||||
else:
|
||||
error = 'The query format must be JSON or YAML'
|
||||
return data, error
|
||||
|
||||
try:
|
||||
data = json.dumps(parse_ym_response(self._send_query(**params)), cls=JSONEncoder)
|
||||
error = None
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
error = unicode(e)
|
||||
return data, error
|
||||
|
||||
|
||||
class YandexAppMetrika(YandexMetrika):
|
||||
@classmethod
|
||||
def type(cls):
|
||||
return "yandex_appmetrika"
|
||||
|
||||
@classmethod
|
||||
def name(cls):
|
||||
return "Yandex AppMetrika"
|
||||
|
||||
def __init__(self, configuration):
|
||||
super(YandexAppMetrika, self).__init__(configuration)
|
||||
self.host = 'https://api.appmetrica.yandex.ru'
|
||||
self.list_path = 'applications'
|
||||
|
||||
|
||||
register(YandexMetrika)
|
||||
register(YandexAppMetrika)
|
||||
@@ -157,6 +157,7 @@ default_query_runners = [
|
||||
'redash.query_runner.impala_ds',
|
||||
'redash.query_runner.vertica',
|
||||
'redash.query_runner.clickhouse',
|
||||
'redash.query_runner.yandex_metrika',
|
||||
'redash.query_runner.treasuredata',
|
||||
'redash.query_runner.sqlite',
|
||||
'redash.query_runner.dynamodb_sql',
|
||||
|
||||
Reference in New Issue
Block a user