From ede143293622fea1a87e9778d350dc37a800ea17 Mon Sep 17 00:00:00 2001 From: timifasubaa <30888507+timifasubaa@users.noreply.github.com> Date: Mon, 18 Sep 2017 20:40:27 -0700 Subject: [PATCH 01/20] Improve code quality (#3480) * add lanscape? * add code climate badges * pylint first pass * Try out yapf * merge * merge * lint * more yapf * removed unnecessary corrections --- superset/__init__.py | 24 +++++++------ superset/db_engine_specs.py | 8 ++--- superset/legacy.py | 4 +-- superset/models/sql_lab.py | 33 ++++++----------- superset/security.py | 23 ++++++------ superset/sql_lab.py | 61 ++++++++++++++++--------------- superset/stats_logger.py | 16 ++++----- superset/utils.py | 72 ++++++++++++++++++------------------- 8 files changed, 112 insertions(+), 129 deletions(-) diff --git a/superset/__init__.py b/superset/__init__.py index af81248c614c2..6988e6b580fbb 100644 --- a/superset/__init__.py +++ b/superset/__init__.py @@ -20,14 +20,12 @@ from superset.connectors.connector_registry import ConnectorRegistry from superset import utils, config # noqa - APP_DIR = os.path.dirname(__file__) CONFIG_MODULE = os.environ.get('SUPERSET_CONFIG', 'superset.config') with open(APP_DIR + '/static/assets/backendSync.json', 'r') as f: frontend_config = json.load(f) - app = Flask(__name__) app.config.from_object(CONFIG_MODULE) conf = app.config @@ -53,14 +51,16 @@ def get_manifest_file(filename): parse_manifest_json() return '/static/assets/dist/' + manifest.get(filename, '') + parse_manifest_json() + @app.context_processor def get_js_manifest(): return dict(js_manifest=get_manifest_file) -################################################################# +################################################################# for bp in conf.get('BLUEPRINTS'): try: @@ -100,10 +100,11 @@ def get_js_manifest(): if app.config.get('ENABLE_TIME_ROTATE'): logging.getLogger().setLevel(app.config.get('TIME_ROTATE_LOG_LEVEL')) - handler = TimedRotatingFileHandler(app.config.get('FILENAME'), - when=app.config.get('ROLLOVER'), - interval=app.config.get('INTERVAL'), - backupCount=app.config.get('BACKUP_COUNT')) + handler = TimedRotatingFileHandler( + app.config.get('FILENAME'), + when=app.config.get('ROLLOVER'), + interval=app.config.get('INTERVAL'), + backupCount=app.config.get('BACKUP_COUNT')) logging.getLogger().addHandler(handler) if app.config.get('ENABLE_CORS'): @@ -114,17 +115,18 @@ def get_js_manifest(): app.wsgi_app = ProxyFix(app.wsgi_app) if app.config.get('ENABLE_CHUNK_ENCODING'): - class ChunkedEncodingFix(object): + class ChunkedEncodingFix(object): def __init__(self, app): self.app = app def __call__(self, environ, start_response): # Setting wsgi.input_terminated tells werkzeug.wsgi to ignore # content-length and read the stream till the end. - if 'chunked' == environ.get('HTTP_TRANSFER_ENCODING', '').lower(): + if environ.get('HTTP_TRANSFER_ENCODING', '').lower() == u'chunked': environ['wsgi.input_terminated'] = True return self.app(environ, start_response) + app.wsgi_app = ChunkedEncodingFix(app.wsgi_app) if app.config.get('UPLOAD_FOLDER'): @@ -142,8 +144,10 @@ class MyIndexView(IndexView): def index(self): return redirect('/superset/welcome') + appbuilder = AppBuilder( - app, db.session, + app, + db.session, base_template='superset/base.html', indexview=MyIndexView, security_manager_class=app.config.get("CUSTOM_SECURITY_MANAGER")) diff --git a/superset/db_engine_specs.py b/superset/db_engine_specs.py index bf4940b22f381..d44ba55b0ba01 100644 --- a/superset/db_engine_specs.py +++ b/superset/db_engine_specs.py @@ -439,7 +439,7 @@ def fetch_result_sets(cls, db, datasource_type, force=False): result_set_df = db.get_df( """SELECT table_schema, table_name FROM INFORMATION_SCHEMA.{}S ORDER BY concat(table_schema, '.', table_name)""".format( - datasource_type.upper()), None) + datasource_type.upper()), None) result_sets = defaultdict(list) for unused, row in result_set_df.iterrows(): result_sets[row['table_schema']].append(row['table_name']) @@ -1003,8 +1003,7 @@ def convert_dttm(cls, target_type, dttm): tt = target_type.upper() if tt == 'DATE': return "'{}'".format(dttm.strftime('%Y-%m-%d')) - else: - return "'{}'".format(dttm.strftime('%Y-%m-%d %H:%M:%S')) + return "'{}'".format(dttm.strftime('%Y-%m-%d %H:%M:%S')) class ImpalaEngineSpec(BaseEngineSpec): @@ -1028,8 +1027,7 @@ def convert_dttm(cls, target_type, dttm): tt = target_type.upper() if tt == 'DATE': return "'{}'".format(dttm.strftime('%Y-%m-%d')) - else: - return "'{}'".format(dttm.strftime('%Y-%m-%d %H:%M:%S')) + return "'{}'".format(dttm.strftime('%Y-%m-%d %H:%M:%S')) engines = { diff --git a/superset/legacy.py b/superset/legacy.py index 8de2548c8e160..c398d873cac4c 100644 --- a/superset/legacy.py +++ b/superset/legacy.py @@ -4,8 +4,8 @@ from __future__ import print_function from __future__ import unicode_literals -from superset import frontend_config import re +from superset import frontend_config FORM_DATA_KEY_WHITELIST = list(frontend_config.get('controls').keys()) + ['slice_id'] @@ -79,5 +79,3 @@ def cast_form_data(form_data): if k not in FORM_DATA_KEY_WHITELIST: del d[k] return d - - diff --git a/superset/models/sql_lab.py b/superset/models/sql_lab.py index e2e125ad2438a..24aff218e4183 100644 --- a/superset/models/sql_lab.py +++ b/superset/models/sql_lab.py @@ -16,8 +16,7 @@ import sqlalchemy as sqla from sqlalchemy import ( Column, Integer, String, ForeignKey, Text, Boolean, - DateTime, Numeric, -) + DateTime, Numeric, ) from sqlalchemy.orm import backref, relationship from superset import sm @@ -28,7 +27,6 @@ class Query(Model): - """ORM model for SQL query""" __tablename__ = 'query' @@ -39,8 +37,7 @@ class Query(Model): # Store the tmp table into the DB only if the user asks for it. tmp_table_name = Column(String(256)) - user_id = Column( - Integer, ForeignKey('ab_user.id'), nullable=True) + user_id = Column(Integer, ForeignKey('ab_user.id'), nullable=True) status = Column(String(16), default=QueryStatus.PENDING) tab_name = Column(String(256)) sql_editor_id = Column(String(256)) @@ -80,14 +77,11 @@ class Query(Model): database = relationship( 'Database', foreign_keys=[database_id], - backref=backref('queries', cascade='all, delete-orphan') - ) - user = relationship( - sm.user_model, - foreign_keys=[user_id]) + backref=backref('queries', cascade='all, delete-orphan')) + user = relationship(sm.user_model, foreign_keys=[user_id]) __table_args__ = ( - sqla.Index('ti_user_id_changed_on', user_id, changed_on), + sqla.Index('ti_user_id_changed_on', user_id, changed_on), ) @property @@ -128,25 +122,19 @@ def name(self): """Name property""" ts = datetime.now().isoformat() ts = ts.replace('-', '').replace(':', '').split('.')[0] - tab = ( - self.tab_name.replace(' ', '_').lower() - if self.tab_name - else 'notab' - ) + tab = (self.tab_name.replace(' ', '_').lower() + if self.tab_name else 'notab') tab = re.sub(r'\W+', '', tab) return "sqllab_{tab}_{ts}".format(**locals()) class SavedQuery(Model, AuditMixinNullable): - """ORM model for SQL query""" __tablename__ = 'saved_query' id = Column(Integer, primary_key=True) - user_id = Column( - Integer, ForeignKey('ab_user.id'), nullable=True) - db_id = Column( - Integer, ForeignKey('dbs.id'), nullable=True) + user_id = Column(Integer, ForeignKey('ab_user.id'), nullable=True) + db_id = Column(Integer, ForeignKey('dbs.id'), nullable=True) schema = Column(String(128)) label = Column(String(256)) description = Column(Text) @@ -158,8 +146,7 @@ class SavedQuery(Model, AuditMixinNullable): database = relationship( 'Database', foreign_keys=[db_id], - backref=backref('saved_queries', cascade='all, delete-orphan') - ) + backref=backref('saved_queries', cascade='all, delete-orphan')) @property def pop_tab_link(self): diff --git a/superset/security.py b/superset/security.py index 012891143e45c..11b6b647c1f96 100644 --- a/superset/security.py +++ b/superset/security.py @@ -11,7 +11,6 @@ from superset.models import core as models from superset.connectors.connector_registry import ConnectorRegistry - READ_ONLY_MODEL_VIEWS = { 'DatabaseAsync', 'DatabaseView', @@ -104,16 +103,16 @@ def get_or_create_main_db(): def is_admin_only(pvm): # not readonly operations on read only model views allowed only for admins - if (pvm.view_menu.name in READ_ONLY_MODEL_VIEWS and - pvm.permission.name not in READ_ONLY_PERMISSION): + if (pvm.view_menu.name in READ_ONLY_MODEL_VIEWS + and pvm.permission.name not in READ_ONLY_PERMISSION): return True - return (pvm.view_menu.name in ADMIN_ONLY_VIEW_MENUS or - pvm.permission.name in ADMIN_ONLY_PERMISSIONS) + return (pvm.view_menu.name in ADMIN_ONLY_VIEW_MENUS + or pvm.permission.name in ADMIN_ONLY_PERMISSIONS) def is_alpha_only(pvm): - if (pvm.view_menu.name in GAMMA_READ_ONLY_MODEL_VIEWS and - pvm.permission.name not in READ_ONLY_PERMISSION): + if (pvm.view_menu.name in GAMMA_READ_ONLY_MODEL_VIEWS + and pvm.permission.name not in READ_ONLY_PERMISSION): return True return pvm.permission.name in ALPHA_ONLY_PERMISSIONS @@ -133,12 +132,14 @@ def is_gamma_pvm(pvm): def is_sql_lab_pvm(pvm): return pvm.view_menu.name in {'SQL Lab'} or pvm.permission.name in { - 'can_sql_json', 'can_csv', 'can_search_queries'} + 'can_sql_json', 'can_csv', 'can_search_queries' + } def is_granter_pvm(pvm): - return pvm.permission.name in {'can_override_role_permissions', - 'can_approve'} + return pvm.permission.name in { + 'can_override_role_permissions', 'can_approve' + } def set_role(role_name, pvm_check): @@ -191,7 +192,7 @@ def merge_pv(view_menu, perm): metrics += list(db.session.query(datasource_class.metric_class).all()) for metric in metrics: - if (metric.is_restricted): + if metric.is_restricted: merge_pv('metric_access', metric.perm) diff --git a/superset/sql_lab.py b/superset/sql_lab.py index 0f2f1fefa5e23..b3488a745e941 100644 --- a/superset/sql_lab.py +++ b/superset/sql_lab.py @@ -4,16 +4,15 @@ from datetime import datetime import json import logging +import uuid import pandas as pd import sqlalchemy -import uuid -from celery.exceptions import SoftTimeLimitExceeded from sqlalchemy.pool import NullPool from sqlalchemy.orm import sessionmaker +from celery.exceptions import SoftTimeLimitExceeded -from superset import ( - app, db, utils, dataframe, results_backend) +from superset import (app, db, utils, dataframe, results_backend) from superset.models.sql_lab import Query from superset.sql_parse import SupersetQuery from superset.db_engine_specs import LimitMethod @@ -78,10 +77,9 @@ def get_session(nullpool): session_class = sessionmaker() session_class.configure(bind=engine) return session_class() - else: - session = db.session() - session.commit() # HACK - return session + session = db.session() + session.commit() # HACK + return session @celery_app.task(bind=True, soft_time_limit=SQLLAB_TIMEOUT) @@ -103,7 +101,8 @@ def get_sql_results( raise -def execute_sql(ctask, query_id, return_results=True, store_results=False, user_name=None): +def execute_sql( + ctask, query_id, return_results=True, store_results=False, user_name=None): """Executes the sql query returns the results.""" session = get_session(not ctask.request.called_directly) @@ -144,13 +143,11 @@ def handle_error(msg): if not query.tmp_table_name: start_dttm = datetime.fromtimestamp(query.start_time) query.tmp_table_name = 'tmp_{}_table_{}'.format( - query.user_id, - start_dttm.strftime('%Y_%m_%d_%H_%M_%S')) + query.user_id, start_dttm.strftime('%Y_%m_%d_%H_%M_%S')) executed_sql = superset_query.as_create_table(query.tmp_table_name) query.select_as_cta_used = True - elif ( - query.limit and superset_query.is_select() and - db_engine_spec.limit_method == LimitMethod.WRAP_SQL): + elif (query.limit and superset_query.is_select() + and db_engine_spec.limit_method == LimitMethod.WRAP_SQL): executed_sql = database.wrap_sql_limit(executed_sql, query.limit) query.limit_used = True try: @@ -170,7 +167,7 @@ def handle_error(msg): logging.info("Set query to 'running'") engine = database.get_sqla_engine( - schema=query.schema, nullpool=not ctask.request.called_directly, user_name=user_name) + schema=query.schema, nullpool=not ctask.request.called_directly, user_name=user_name) try: engine = database.get_sqla_engine( schema=query.schema, nullpool=not ctask.request.called_directly, user_name=user_name) @@ -178,8 +175,8 @@ def handle_error(msg): cursor = conn.cursor() logging.info("Running query: \n{}".format(executed_sql)) logging.info(query.executed_sql) - cursor.execute( - query.executed_sql, **db_engine_spec.cursor_execute_kwargs) + cursor.execute(query.executed_sql, + **db_engine_spec.cursor_execute_kwargs) logging.info("Handling cursor") db_engine_spec.handle_cursor(cursor, query, session) logging.info("Fetching data: {}".format(query.to_dict())) @@ -202,29 +199,31 @@ def handle_error(msg): conn.close() if query.status == utils.QueryStatus.STOPPED: - return json.dumps({ - 'query_id': query.id, - 'status': query.status, - 'query': query.to_dict(), - }, default=utils.json_iso_dttm_ser) + return json.dumps( + { + 'query_id': query.id, + 'status': query.status, + 'query': query.to_dict(), + }, + default=utils.json_iso_dttm_ser) column_names = ( [col[0] for col in cursor_description] if cursor_description else []) column_names = dedup(column_names) - cdf = dataframe.SupersetDataFrame(pd.DataFrame( - list(data), columns=column_names)) + cdf = dataframe.SupersetDataFrame( + pd.DataFrame(list(data), columns=column_names)) query.rows = cdf.size query.progress = 100 query.status = QueryStatus.SUCCESS if query.select_as_cta: - query.select_sql = '{}'.format(database.select_star( - query.tmp_table_name, - limit=query.limit, - schema=database.force_ctas_schema, - show_cols=False, - latest_partition=False, - )) + query.select_sql = '{}'.format( + database.select_star( + query.tmp_table_name, + limit=query.limit, + schema=database.force_ctas_schema, + show_cols=False, + latest_partition=False, )) query.end_time = utils.now_as_float() session.merge(query) session.flush() diff --git a/superset/stats_logger.py b/superset/stats_logger.py index 76eb7675c5798..b6359f372970a 100644 --- a/superset/stats_logger.py +++ b/superset/stats_logger.py @@ -1,5 +1,5 @@ -from colorama import Fore, Style import logging +from colorama import Fore, Style class BaseStatsLogger(object): @@ -27,20 +27,18 @@ def gauge(self, key): class DummyStatsLogger(BaseStatsLogger): - def incr(self, key): logging.info( Fore.CYAN + "[stats_logger] (incr) " + key + Style.RESET_ALL) def decr(self, key): - logging.info( - Fore.CYAN + "[stats_logger] (decr) " + key + Style.RESET_ALL) + logging.info(Fore.CYAN + "[stats_logger] (decr) " + key + + Style.RESET_ALL) def gauge(self, key, value): logging.info(( Fore.CYAN + "[stats_logger] (gauge) " - "{key} | {value}" + Style.RESET_ALL).format(**locals()) - ) + "{key} | {value}" + Style.RESET_ALL).format(**locals())) try: @@ -48,10 +46,8 @@ def gauge(self, key, value): class StatsdStatsLogger(BaseStatsLogger): def __init__(self, host, port, prefix='superset'): - self.client = StatsClient( - host=host, - port=port, - prefix=prefix) + super() + self.client = StatsClient(host=host, port=port, prefix=prefix) def incr(self, key): self.client.incr(key) diff --git a/superset/utils.py b/superset/utils.py index 1c792f035f015..956575b460ac6 100644 --- a/superset/utils.py +++ b/superset/utils.py @@ -8,16 +8,16 @@ import functools import json import logging -import numpy import os +import signal import parsedatetime -import pytz import smtplib +import pytz import sqlalchemy as sa -import signal import uuid import sys import zlib +import numpy from builtins import object from datetime import date, datetime, time, timedelta @@ -28,15 +28,16 @@ from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from email.utils import formatdate + from flask import flash, Markup, render_template, url_for, redirect, request from flask_appbuilder.const import ( LOGMSG_ERR_SEC_ACCESS_DENIED, FLAMSG_ERR_SEC_ACCESS_DENIED, PERMISSION_PREFIX ) -from flask_cache import Cache from flask_appbuilder._compat import as_unicode from flask_babel import gettext as __ +from flask_cache import Cache import markdown as md from past.builtins import basestring from pydruid.utils.having import Having @@ -78,8 +79,7 @@ def can_access(sm, permission_name, view_name, user): """Protecting from has_access failing from missing perms/view""" if user.is_anonymous(): return sm.is_item_public(permission_name, view_name) - else: - return sm._has_view_access(user, permission_name, view_name) + return sm._has_view_access(user, permission_name, view_name) def flasher(msg, severity=None): @@ -94,7 +94,6 @@ def flasher(msg, severity=None): class memoized(object): # noqa - """Decorator that caches a function's return value each time it is called If called later with the same arguments, the cached value is returned, and @@ -161,11 +160,13 @@ def __init__(self, **args): # Just a hack to prevent any exceptions Having.__init__(self, type='equalTo', aggregation=None, value=None) - self.having = {'having': { - 'type': 'dimSelector', - 'dimension': args['dimension'], - 'value': args['value'], - }} + self.having = { + 'having': { + 'type': 'dimSelector', + 'dimension': args['dimension'], + 'value': args['value'], + } + } def list_minus(l, minus): @@ -231,13 +232,11 @@ def parse_human_timedelta(s): cal = parsedatetime.Calendar() dttm = dttm_from_timtuple(datetime.now().timetuple()) d = cal.parse(s, dttm)[0] - d = datetime( - d.tm_year, d.tm_mon, d.tm_mday, d.tm_hour, d.tm_min, d.tm_sec) + d = datetime(d.tm_year, d.tm_mon, d.tm_mday, d.tm_hour, d.tm_min, d.tm_sec) return d - dttm class JSONEncodedDict(TypeDecorator): - """Represents an immutable structure as a json-encoded string.""" impl = TEXT @@ -301,8 +300,7 @@ def json_iso_dttm_ser(obj): obj = obj.isoformat() else: raise TypeError( - "Unserializable object {} of type {}".format(obj, type(obj)) - ) + "Unserializable object {} of type {}".format(obj, type(obj))) return obj @@ -328,8 +326,7 @@ def json_int_dttm_ser(obj): obj = (obj - EPOCH.date()).total_seconds() * 1000 else: raise TypeError( - "Unserializable object {} of type {}".format(obj, type(obj)) - ) + "Unserializable object {} of type {}".format(obj, type(obj))) return obj @@ -353,7 +350,7 @@ def error_msg_from_exception(e): """ msg = '' if hasattr(e, 'message'): - if type(e.message) is dict: + if isinstance(e.message, dict): msg = e.message.get('message') elif e.message: msg = "{}".format(e.message) @@ -382,9 +379,8 @@ def generic_find_constraint_name(table, columns, referenced, db): t = sa.Table(table, db.metadata, autoload=True, autoload_with=db.engine) for fk in t.foreign_key_constraints: - if ( - fk.referred_table.name == referenced and - set(fk.column_keys) == columns): + if (fk.referred_table.name == referenced + and set(fk.column_keys) == columns): return fk.name @@ -421,6 +417,7 @@ class timeout(object): """ To be used in a ``with`` block and timeout its content. """ + def __init__(self, seconds=1, error_message='Timeout'): self.seconds = seconds self.error_message = error_message @@ -483,7 +480,6 @@ def ping_connection(connection, branch): class QueryStatus(object): - """Enum-type class for query statuses""" STOPPED = 'stopped' @@ -539,11 +535,11 @@ def send_email_smtp(to, subject, html_content, config, files=None, for fname in files or []: basename = os.path.basename(fname) with open(fname, "rb") as f: - msg.attach(MIMEApplication( - f.read(), - Content_Disposition='attachment; filename="%s"' % basename, - Name=basename - )) + msg.attach( + MIMEApplication( + f.read(), + Content_Disposition='attachment; filename="%s"' % basename, + Name=basename)) send_MIME_email(smtp_mail_from, recipients, msg, config, dryrun=dryrun) @@ -600,17 +596,20 @@ def has_access(f): def wraps(self, *args, **kwargs): permission_str = PERMISSION_PREFIX + f._permission_name - if self.appbuilder.sm.has_access( - permission_str, self.__class__.__name__): + if self.appbuilder.sm.has_access(permission_str, + self.__class__.__name__): return f(self, *args, **kwargs) else: - logging.warning(LOGMSG_ERR_SEC_ACCESS_DENIED.format( - permission_str, self.__class__.__name__)) + logging.warning( + LOGMSG_ERR_SEC_ACCESS_DENIED.format(permission_str, + self.__class__.__name__)) flash(as_unicode(FLAMSG_ERR_SEC_ACCESS_DENIED), "danger") # adds next arg to forward to the original path once user is logged in. - return redirect(url_for( - self.appbuilder.sm.auth_view.__class__.__name__ + ".login", - next=request.path)) + return redirect( + url_for( + self.appbuilder.sm.auth_view.__class__.__name__ + ".login", + next=request.path)) + f._permission_name = permission_str return functools.update_wrapper(wraps, f) @@ -656,6 +655,7 @@ def zlib_decompress_to_string(blob): return decompressed.decode("utf-8") return zlib.decompress(blob) + _celery_app = None From c5252d0f43621999030d3dcf3d2e8bfee5452510 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Mon, 18 Sep 2017 21:32:39 -0700 Subject: [PATCH 02/20] [heatmap] account for bounds = 0 (#3474) * [heatmap] account for bounds = 0 * Fix sorting * linting --- .../javascripts/explore/stores/controls.jsx | 8 ++++---- superset/assets/visualizations/heatmap.js | 18 +++++++++++++----- superset/viz.py | 4 ++-- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/superset/assets/javascripts/explore/stores/controls.jsx b/superset/assets/javascripts/explore/stores/controls.jsx index b87e9881a757b..c226f66cdd0a5 100644 --- a/superset/assets/javascripts/explore/stores/controls.jsx +++ b/superset/assets/javascripts/explore/stores/controls.jsx @@ -37,10 +37,10 @@ const timeColumnOption = { 'account'), }; const sortAxisChoices = [ - ['alpha_asc', 'Alphabetical ascending'], - ['alpha_desc', 'Alphabetical descending'], - ['value_asc', 'Value ascending'], - ['value_desc', 'Value descending'], + ['alpha_asc', 'Axis ascending'], + ['alpha_desc', 'Axis descending'], + ['value_asc', 'sum(value) ascending'], + ['value_desc', 'sum(value) descending'], ]; const groupByControl = { diff --git a/superset/assets/visualizations/heatmap.js b/superset/assets/visualizations/heatmap.js index 1f76a3acba26d..002af8a704a44 100644 --- a/superset/assets/visualizations/heatmap.js +++ b/superset/assets/visualizations/heatmap.js @@ -7,6 +7,10 @@ import { colorScalerFactory } from '../javascripts/modules/colors'; import '../stylesheets/d3tip.css'; import './heatmap.css'; +function cmp(a, b) { + return a > b ? 1 : -1; +} + // Inspired from http://bl.ocks.org/mbostock/3074470 // https://jsfiddle.net/cyril123/h0reyumq/ function heatmapVis(slice, payload) { @@ -52,17 +56,21 @@ function heatmapVis(slice, payload) { function ordScale(k, rangeBands, sortMethod) { let domain = {}; + const actualKeys = {}; // hack to preserve type of keys when number data.forEach((d) => { - domain[d[k]] = domain[d[k]] || 0 + d.v; + domain[d[k]] = (domain[d[k]] || 0) + d.v; + actualKeys[d[k]] = d[k]; }); + // Not usgin object.keys() as it converts to strings + const keys = Object.keys(actualKeys).map(s => actualKeys[s]); if (sortMethod === 'alpha_asc') { - domain = Object.keys(domain).sort(); + domain = keys.sort(cmp); } else if (sortMethod === 'alpha_desc') { - domain = Object.keys(domain).sort().reverse(); + domain = keys.sort(cmp).reverse(); } else if (sortMethod === 'value_desc') { - domain = Object.keys(domain).sort((d1, d2) => domain[d2] - domain[d1]); + domain = Object.keys(domain).sort((a, b) => domain[a] > domain[b] ? -1 : 1); } else if (sortMethod === 'value_asc') { - domain = Object.keys(domain).sort((d1, d2) => domain[d1] - domain[d2]); + domain = Object.keys(domain).sort((a, b) => domain[b] > domain[a] ? -1 : 1); } if (k === 'y' && rangeBands) { diff --git a/superset/viz.py b/superset/viz.py index 1f844edef13b6..b22af0855c5c1 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -1484,9 +1484,9 @@ def get_data(self, df): max_ = df.v.max() min_ = df.v.min() bounds = fd.get('y_axis_bounds') - if bounds and bounds[0]: + if bounds and bounds[0] is not None: min_ = bounds[0] - if bounds and bounds[1]: + if bounds and bounds[1] is not None: max_ = bounds[1] if norm == 'heatmap': overall = True From ccd5fd44cf33227dca4ac2280f5633719323b56e Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Tue, 19 Sep 2017 11:19:49 -0700 Subject: [PATCH 03/20] Removing dependency on pythrifthiveapi (#3494) Since the latest pyhive, we don't need pythrifthiveapi as they ship with the latest version. There's actually a conflict between the new pyhive and pythrifthiveapi and this fixes it. --- dev-reqs.txt | 1 - superset/db_engine_specs.py | 2 +- superset/db_engines/hive.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dev-reqs.txt b/dev-reqs.txt index c53fdefa1ea47..0705bd2da4a32 100644 --- a/dev-reqs.txt +++ b/dev-reqs.txt @@ -7,7 +7,6 @@ mysqlclient nose psycopg2 pylint -pythrifthiveapi pyyaml redis statsd diff --git a/superset/db_engine_specs.py b/superset/db_engine_specs.py index d44ba55b0ba01..71c6ab20d9b6f 100644 --- a/superset/db_engine_specs.py +++ b/superset/db_engine_specs.py @@ -668,7 +668,7 @@ class HiveEngineSpec(PrestoEngineSpec): def patch(cls): from pyhive import hive from superset.db_engines import hive as patched_hive - from pythrifthiveapi.TCLIService import ( + from TCLIService import ( constants as patched_constants, ttypes as patched_ttypes, TCLIService as patched_TCLIService) diff --git a/superset/db_engines/hive.py b/superset/db_engines/hive.py index a31b4d7f323d8..f14608410823a 100644 --- a/superset/db_engines/hive.py +++ b/superset/db_engines/hive.py @@ -1,5 +1,5 @@ from pyhive import hive -from pythrifthiveapi.TCLIService import ttypes +from TCLIService import ttypes from thrift import Thrift From ed9f56448f39aab9e9bbb331d008390dd63d9b21 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Tue, 19 Sep 2017 11:20:22 -0700 Subject: [PATCH 04/20] Adding missing future imports (#3493) --- superset/sql_lab.py | 3 +++ superset/sql_parse.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/superset/sql_lab.py b/superset/sql_lab.py index b3488a745e941..f9566d3c430fb 100644 --- a/superset/sql_lab.py +++ b/superset/sql_lab.py @@ -1,3 +1,6 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function from __future__ import unicode_literals from time import sleep diff --git a/superset/sql_parse.py b/superset/sql_parse.py index d0bf5bb9a06fc..bcf36bfb5db6b 100644 --- a/superset/sql_parse.py +++ b/superset/sql_parse.py @@ -1,3 +1,6 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function from __future__ import unicode_literals import logging From ae7f16337232d8d9ecaa5d8581295fac3513e054 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Tue, 19 Sep 2017 16:41:09 -0700 Subject: [PATCH 05/20] Removing super() call from refactor (#3500) --- superset/stats_logger.py | 1 - 1 file changed, 1 deletion(-) diff --git a/superset/stats_logger.py b/superset/stats_logger.py index b6359f372970a..a19910d5c0d83 100644 --- a/superset/stats_logger.py +++ b/superset/stats_logger.py @@ -46,7 +46,6 @@ def gauge(self, key, value): class StatsdStatsLogger(BaseStatsLogger): def __init__(self, host, port, prefix='superset'): - super() self.client = StatsClient(host=host, port=port, prefix=prefix) def incr(self, key): From aebd089ca5c2ca993987d86230fa6ad84f8b963b Mon Sep 17 00:00:00 2001 From: timifasubaa <30888507+timifasubaa@users.noreply.github.com> Date: Tue, 19 Sep 2017 21:30:01 -0700 Subject: [PATCH 06/20] update contributing.md (#3495) Uses pip instead to setup superset. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f026c908568a7..0539ac3c823ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -173,7 +173,7 @@ Check the [OS dependencies](https://superset.incubator.apache.org/installation.h source env/bin/activate # install for development - python setup.py develop + pip install -e . # Create an admin user fabmanager create-admin --app superset From d7fc364ff4854ed5c255b19195eec034a1b8b151 Mon Sep 17 00:00:00 2001 From: timifasubaa <30888507+timifasubaa@users.noreply.github.com> Date: Tue, 19 Sep 2017 21:30:18 -0700 Subject: [PATCH 07/20] Fix idna requirement (#3497) There is often conflicting versions of idna. This fixes is to 2.5. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 94104776eb473..cb8fc94c8715a 100644 --- a/setup.py +++ b/setup.py @@ -58,6 +58,7 @@ def get_git_sha(): 'future>=0.16.0, <0.17', 'humanize==0.5.1', 'gunicorn==19.7.1', + 'idna==2.5', 'markdown==2.6.8', 'pandas==0.20.2', 'parsedatetime==2.0.0', From 1cf634afa2d6c4ee1eede98c33f9fadbfd666698 Mon Sep 17 00:00:00 2001 From: timifasubaa <30888507+timifasubaa@users.noreply.github.com> Date: Tue, 19 Sep 2017 21:30:48 -0700 Subject: [PATCH 08/20] Remove repeated line (#3491) * add lanscape? * Revert "add lanscape?" This reverts commit b3d6e80af49120b030f7d3d5eff0592a1e0566f2. * remove unused line --- superset/sql_lab.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/superset/sql_lab.py b/superset/sql_lab.py index f9566d3c430fb..aeb71f6b79a0e 100644 --- a/superset/sql_lab.py +++ b/superset/sql_lab.py @@ -168,9 +168,6 @@ def handle_error(msg): session.merge(query) session.commit() logging.info("Set query to 'running'") - - engine = database.get_sqla_engine( - schema=query.schema, nullpool=not ctask.request.called_directly, user_name=user_name) try: engine = database.get_sqla_engine( schema=query.schema, nullpool=not ctask.request.called_directly, user_name=user_name) From 9af34ba51cdb23a2970e968d0cbdee82778c6799 Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Wed, 20 Sep 2017 12:37:33 -0700 Subject: [PATCH 09/20] js translation -- performance improvment (#3390) * Chinese page * Using react-intl-universal to improve multi language in react page * Using react-intl-universal to improve multi language in react page * react_intl_universal * change * change * change * change * change * change * change * merge * multiple page in js * merge * merge * merge * merge * Js Translations * JS Translation * JS Translations * Js translation * JS translations * JS translations * Js translaion * JS en Translation * JS Translation * upgrade document Fixing the damn build (#3179) * Fixing the build * Going deeper [bugfix] only filterable columns should show up in FilterBox list (#3105) * [bugfix] only filterable columns should show up in FilterBox list * Touchups Datasource cannot be empty (#3035) add title description to model view (#3045) * add title description to model view * add missing import Add 'show/hide totals' option to pivot table vis (#3101) [bugfix] numeric value for date fields in table viz (#3036) Bug was present only when using the NOT GROUPED BY option fixes https://github.com/ApacheInfra/superset/issues/3027 fix hive.fetch_logs (#2968) add Zalando to the list of organizations (#3171) docs: fixup installation examples code indentation (#3169) [bugfix] fix bar order (#3180) [bugfix] visualize flow error: 'Metric x is not valid' (#3181) The metric name in the frontend doesn't match the one generated on the backend. It turns out the explore view will default to the first metric so specifying one isn't needed. Fix the segment interval for pulling metadata (#3174) The end of the interval would be on the truncated today date, which means that you will exclude today. If your realtime ingestion job runs shorter than a day, the metadata cannot be pulled from the druid cluster. Bump cryptography to 1.9 (#3065) As 1.7.2 doesn't compile here with openssl 1.1.0f Escaping the user's SQL in the explore view (#3186) * Escaping the user's SQL in the explore view When executing SQL from SQL Lab, we use a lower level API to the database which doesn't require escaping the SQL. When going through the explore view, the stack chain leading to the same method may need escaping depending on how the DBAPI driver is written, and that is the case for Presto (and perhaps other drivers). * Using regex to avoid doubling doubles [sqllab] improve Hive support (#3187) * [sqllab] improve Hive support * Fix "Transport not open" bug * Getting progress bar to show * Bump pyhive to 0.4.0 * Getting [Track Job] button to show * Fix testzz Add BigQuery engine specifications (#3193) As contributed by @mxmzdlv on issue #945 [bugfix] fix merge conflict that broke Hive support (#3196) Adding 'apache' to docs (#3194) [druid] Allow custom druid postaggregators (#3146) * [druid] Allow custom druid postaggregators Also, fix the postaggregation for approxHistogram quantiles so it adds the dependent field and that can show up in the graphs/tables. In general, postAggregators add significant power, we should probably support including custom postAggregators. Plywood has standard postAggregators here, and a customAggregator escape hatch that allows you to define custom postAggregators. This commit adds a similar capability for Superset and a additional field/fields/fieldName breakdown of the typical naming for dependent aggregations, which should make it significantly easier to develop approxHistogram and custom postAggregation-required dashboards. * [druid] Minor style cleanup in tests file. * [druid] Apply code review suggestions * break out CustomPostAggregator into separate class. This just cleans up the creation of the postaggregator a little bit. * minor style issues. * move the function around so the git diff is more readable add combine config for metrics in pivot table (#3086) * add combine config for metrics in pivot table * change method to stack/unstack * update backendSync Autofocus search input in VizTypeControl modal onEnter (#2929) Speed up JS build time (#3203) Also bumping a few related libs JS Translation JS translations js translation fix issue 3204 (#3205) [bugfix] capture Hive job_id pre-url transformation (#3213) js translation fix issue 3204 (#3205) [bugfix] capture Hive job_id pre-url transformation (#3213) [docs] update url in CONTRIBUTING.md (#3212) [sqllab/cosmetics] add margin-top for labels in query history (#3222) [explore] nvd3 sort values in rich tooltip (#3197) [sqllab] fix UI shows 'The query returned no results' momentarily (#3214) this is visible when running async queries between the fetching and success state as the rows are getting cached in the component [explore] DatasourceControl to pick datasource in modal (#3210) * [explore] DatasourceControl to pick datasource in modal Makes it easier to change datasource, also makes it such that the list of all datasources doesn't need to be loaded upfront. * Adding more metadata * Js translation * js tran * js trans * js trans * js tran * js trans * js trans * js tran * js translation * js trans * js translation * try load language pack async * Backend translations things * create language pack inside common data * performance improvement for js i18n. - js bundle should not contain localized content - we populate translation content from server-side, in boostrap.common.language_pack - in client-side, use promise to wrap around translation content. text will be translated after translation content arrived/parsed. - fix linting * fix Timer unit test * 1. add global hook for all tests, to make translation pack avaialble before each test starts. 2. fix unit test for Timer component 3. remove noused method get_locale, and modules 4. fix page reload after user change page language * parse and build i18n dictionary as a module * fix sync-backend task, which should run without DOM --- CONTRIBUTING.md | 14 + babel/babel.cfg | 4 + babel/messages.pot | 2814 ++++++++++++-- superset/assets/javascripts/SqlLab/actions.js | 27 +- .../SqlLab/components/CopyQueryTabUrl.jsx | 5 +- .../SqlLab/components/HighlightedSql.jsx | 7 +- .../SqlLab/components/QueryHistory.jsx | 3 +- .../SqlLab/components/QuerySearch.jsx | 17 +- .../SqlLab/components/QueryTable.jsx | 17 +- .../SqlLab/components/ResultSet.jsx | 19 +- .../components/RunQueryActionButton.jsx | 11 +- .../SqlLab/components/SaveQuery.jsx | 17 +- .../SqlLab/components/SouthPane.jsx | 9 +- .../SqlLab/components/SqlEditor.jsx | 5 +- .../SqlLab/components/SqlEditorLeftBar.jsx | 23 +- .../SqlLab/components/TabbedSqlEditors.jsx | 13 +- .../SqlLab/components/TableElement.jsx | 17 +- .../SqlLab/components/VisualizeModal.jsx | 23 +- .../assets/javascripts/SqlLab/constants.js | 10 +- .../assets/javascripts/SqlLab/reducers.js | 25 +- .../addSlice/AddSliceContainer.jsx | 12 +- superset/assets/javascripts/common.js | 11 + .../javascripts/components/AsyncSelect.jsx | 3 +- .../javascripts/components/CachedLabel.jsx | 7 +- .../components/CopyToClipboard.jsx | 9 +- .../javascripts/components/EditableTitle.jsx | 5 +- .../javascripts/components/FaveStar.jsx | 3 +- .../javascripts/dashboard/Dashboard.jsx | 17 +- .../dashboard/components/CodeModal.jsx | 3 +- .../dashboard/components/Controls.jsx | 7 +- .../dashboard/components/CssEditor.jsx | 9 +- .../components/RefreshIntervalModal.jsx | 15 +- .../dashboard/components/SaveModal.jsx | 19 +- .../dashboard/components/SliceAdder.jsx | 13 +- .../dashboard/components/SliceCell.jsx | 15 +- .../explore/components/ChartContainer.jsx | 5 +- .../explore/components/ControlHeader.jsx | 5 +- .../components/ControlPanelsContainer.jsx | 2 +- .../explore/components/DisplayQueryButton.jsx | 5 +- .../explore/components/EmbedCodeButton.jsx | 7 +- .../components/ExploreActionButtons.jsx | 5 +- .../explore/components/SaveModal.jsx | 28 +- .../explore/components/URLShortLinkButton.jsx | 5 +- .../components/controls/BoundsControl.jsx | 9 +- .../components/controls/DatasourceControl.jsx | 11 +- .../explore/components/controls/Filter.jsx | 7 +- .../components/controls/FilterControl.jsx | 3 +- .../components/controls/SelectControl.jsx | 3 +- .../components/controls/TextAreaControl.jsx | 5 +- .../components/controls/VizTypeControl.jsx | 5 +- superset/assets/javascripts/explore/index.jsx | 4 +- .../explore/reducers/chartReducer.js | 11 +- .../javascripts/explore/stores/controls.jsx | 553 ++- .../javascripts/explore/stores/visTypes.js | 348 +- superset/assets/javascripts/i18n.jsx | 32 + superset/assets/javascripts/locales.jsx | 148 + .../assets/javascripts/modules/superset.js | 11 +- .../javascripts/profile/components/App.jsx | 9 +- .../profile/components/CreatedContent.jsx | 9 +- .../profile/components/Favorites.jsx | 9 +- .../profile/components/Security.jsx | 7 +- .../profile/components/UserInfo.jsx | 7 +- superset/assets/javascripts/profile/index.jsx | 2 +- superset/assets/package.json | 3 + .../components/AsyncSelect_spec.jsx | 16 +- .../profile/EditableTitle_spec.jsx | 2 +- .../javascripts/sqllab/SaveQuery_spec.jsx | 9 +- .../spec/javascripts/sqllab/Timer_spec.jsx | 13 +- .../spec/javascripts/sqllab/reducers_spec.js | 6 +- superset/assets/visualizations/EventFlow.jsx | 3 +- superset/assets/visualizations/filter_box.jsx | 3 +- superset/config.py | 4 +- .../templates/appbuilder/navbar_right.html | 2 +- superset/templates/superset/dashboard.html | 1 + superset/translations/__init__.py | 0 .../translations/en/LC_MESSAGES/messages.json | 1 + .../translations/en/LC_MESSAGES/messages.mo | Bin 0 -> 58211 bytes .../translations/en/LC_MESSAGES/messages.po | 3327 +++++++++++++++++ .../translations/es/LC_MESSAGES/messages.json | 1 + .../translations/es/LC_MESSAGES/messages.mo | Bin 16501 -> 58211 bytes .../translations/es/LC_MESSAGES/messages.po | 3026 +++++++++++++-- .../translations/fr/LC_MESSAGES/messages.json | 1 + .../translations/fr/LC_MESSAGES/messages.mo | Bin 24674 -> 0 bytes .../translations/fr/LC_MESSAGES/messages.po | 1383 ------- .../translations/it/LC_MESSAGES/messages.json | 1 + .../translations/it/LC_MESSAGES/messages.mo | Bin 17303 -> 59013 bytes .../translations/it/LC_MESSAGES/messages.po | 3064 +++++++++++++-- superset/translations/utils.py | 33 + .../translations/zh/LC_MESSAGES/messages.json | 1 + .../translations/zh/LC_MESSAGES/messages.mo | Bin 15904 -> 53786 bytes .../translations/zh/LC_MESSAGES/messages.po | 3206 ++++++++++++++-- superset/views/base.py | 5 + 92 files changed, 15170 insertions(+), 3424 deletions(-) create mode 100644 superset/assets/javascripts/i18n.jsx create mode 100644 superset/assets/javascripts/locales.jsx create mode 100644 superset/translations/__init__.py create mode 100644 superset/translations/en/LC_MESSAGES/messages.json create mode 100644 superset/translations/en/LC_MESSAGES/messages.mo create mode 100644 superset/translations/en/LC_MESSAGES/messages.po create mode 100644 superset/translations/es/LC_MESSAGES/messages.json create mode 100644 superset/translations/fr/LC_MESSAGES/messages.json delete mode 100644 superset/translations/fr/LC_MESSAGES/messages.mo delete mode 100644 superset/translations/fr/LC_MESSAGES/messages.po create mode 100644 superset/translations/it/LC_MESSAGES/messages.json create mode 100644 superset/translations/utils.py create mode 100644 superset/translations/zh/LC_MESSAGES/messages.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0539ac3c823ab..da403d16f46eb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -331,6 +331,8 @@ key is to instrument the strings that need translation using a module, all you have to do is to `_("Wrap your strings")` using the underscore `_` "function". +We use `import {t, tn, TCT} from locales;` in js, JSX file, locales is in `./superset/assets/javascripts/` directory. + To enable changing language in your environment, you can simply add the `LANGUAGES` parameter to your `superset_config.py`. Having more than one options here will add a language selection dropdown on the right side of the @@ -342,6 +344,10 @@ navigation bar. 'zh': {'flag': 'cn', 'name': 'Chinese'}, } +We need to extract the string to be translated, run the following command: + + pybabel extract -F ./babel/babel.cfg -k _ -k __ -k t -k tn -k tct -o ./babel/messages.pot . + As per the [Flask AppBuilder documentation] about translation, to create a new language dictionary, run the following command: @@ -358,6 +364,14 @@ to take effect, they need to be compiled using this command: fabmanager babel-compile --target superset/translations/ +In the case of JS translation, we need to convert the PO file into a JSON file, and we need the global download of the npm package po2json. +We need to be compiled using this command: + + npm install po2json -g + +Execute this command to convert the en PO file into a json file: + + po2json -d superset -f jed1.x superset/translations/en/LC_MESSAGES/messages.po superset/translations/en/LC_MESSAGES/messages.json ## Adding new datasources diff --git a/babel/babel.cfg b/babel/babel.cfg index 790b262731b74..762ac64e77c96 100644 --- a/babel/babel.cfg +++ b/babel/babel.cfg @@ -1,4 +1,8 @@ [ignore: superset/assets/node_modules/**] [python: superset/**.py] [jinja2: superset/**/templates/**.html] +[javascript: superset/assets/javascripts/**.js] +[javascript: superset/assets/javascripts/**.jsx] +[javascript: superset/assets/visualizations/**.js] +[javascript: superset/assets/visualizations/**.jsx] encoding = utf-8 diff --git a/babel/messages.pot b/babel/messages.pot index 5ebccd2646a4e..810ccb0dbbcfa 100755 --- a/babel/messages.pot +++ b/babel/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2017-08-17 03:23+0200\n" +"POT-Creation-Date: 2017-09-12 20:55-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,76 +17,77 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.4.0\n" +#: superset/assets/javascripts/explore/stores/controls.jsx:505 #: superset/db_engine_specs.py:192 superset/db_engine_specs.py:223 #: superset/db_engine_specs.py:267 superset/db_engine_specs.py:315 -#: superset/db_engine_specs.py:360 superset/db_engine_specs.py:810 -#: superset/db_engine_specs.py:846 superset/db_engine_specs.py:878 -#: superset/db_engine_specs.py:924 superset/db_engine_specs.py:961 -#: superset/db_engine_specs.py:987 +#: superset/db_engine_specs.py:371 superset/db_engine_specs.py:839 +#: superset/db_engine_specs.py:875 superset/db_engine_specs.py:907 +#: superset/db_engine_specs.py:953 superset/db_engine_specs.py:990 +#: superset/db_engine_specs.py:1016 msgid "Time Column" msgstr "" #: superset/db_engine_specs.py:193 superset/db_engine_specs.py:224 -#: superset/db_engine_specs.py:316 superset/db_engine_specs.py:361 -#: superset/db_engine_specs.py:811 superset/db_engine_specs.py:879 -#: superset/db_engine_specs.py:962 +#: superset/db_engine_specs.py:316 superset/db_engine_specs.py:372 +#: superset/db_engine_specs.py:840 superset/db_engine_specs.py:908 +#: superset/db_engine_specs.py:991 msgid "second" msgstr "" #: superset/db_engine_specs.py:194 superset/db_engine_specs.py:227 -#: superset/db_engine_specs.py:319 superset/db_engine_specs.py:363 -#: superset/db_engine_specs.py:813 superset/db_engine_specs.py:847 -#: superset/db_engine_specs.py:881 superset/db_engine_specs.py:925 -#: superset/db_engine_specs.py:963 superset/db_engine_specs.py:988 +#: superset/db_engine_specs.py:319 superset/db_engine_specs.py:374 +#: superset/db_engine_specs.py:842 superset/db_engine_specs.py:876 +#: superset/db_engine_specs.py:910 superset/db_engine_specs.py:954 +#: superset/db_engine_specs.py:992 superset/db_engine_specs.py:1017 msgid "minute" msgstr "" #: superset/db_engine_specs.py:195 superset/db_engine_specs.py:231 -#: superset/db_engine_specs.py:321 superset/db_engine_specs.py:365 -#: superset/db_engine_specs.py:819 superset/db_engine_specs.py:849 -#: superset/db_engine_specs.py:883 superset/db_engine_specs.py:931 -#: superset/db_engine_specs.py:964 superset/db_engine_specs.py:989 +#: superset/db_engine_specs.py:321 superset/db_engine_specs.py:376 +#: superset/db_engine_specs.py:848 superset/db_engine_specs.py:878 +#: superset/db_engine_specs.py:912 superset/db_engine_specs.py:960 +#: superset/db_engine_specs.py:993 superset/db_engine_specs.py:1018 msgid "hour" msgstr "" #: superset/db_engine_specs.py:196 superset/db_engine_specs.py:236 #: superset/db_engine_specs.py:268 superset/db_engine_specs.py:323 -#: superset/db_engine_specs.py:367 superset/db_engine_specs.py:821 -#: superset/db_engine_specs.py:851 superset/db_engine_specs.py:885 -#: superset/db_engine_specs.py:933 superset/db_engine_specs.py:965 -#: superset/db_engine_specs.py:990 +#: superset/db_engine_specs.py:378 superset/db_engine_specs.py:850 +#: superset/db_engine_specs.py:880 superset/db_engine_specs.py:914 +#: superset/db_engine_specs.py:962 superset/db_engine_specs.py:994 +#: superset/db_engine_specs.py:1019 msgid "day" msgstr "" #: superset/db_engine_specs.py:197 superset/db_engine_specs.py:242 #: superset/db_engine_specs.py:269 superset/db_engine_specs.py:324 -#: superset/db_engine_specs.py:369 superset/db_engine_specs.py:823 -#: superset/db_engine_specs.py:853 superset/db_engine_specs.py:887 -#: superset/db_engine_specs.py:966 superset/db_engine_specs.py:991 +#: superset/db_engine_specs.py:380 superset/db_engine_specs.py:852 +#: superset/db_engine_specs.py:882 superset/db_engine_specs.py:916 +#: superset/db_engine_specs.py:995 superset/db_engine_specs.py:1020 msgid "week" msgstr "" #: superset/db_engine_specs.py:198 superset/db_engine_specs.py:244 #: superset/db_engine_specs.py:271 superset/db_engine_specs.py:326 -#: superset/db_engine_specs.py:371 superset/db_engine_specs.py:825 -#: superset/db_engine_specs.py:855 superset/db_engine_specs.py:889 -#: superset/db_engine_specs.py:935 superset/db_engine_specs.py:967 -#: superset/db_engine_specs.py:992 +#: superset/db_engine_specs.py:382 superset/db_engine_specs.py:854 +#: superset/db_engine_specs.py:884 superset/db_engine_specs.py:918 +#: superset/db_engine_specs.py:964 superset/db_engine_specs.py:996 +#: superset/db_engine_specs.py:1021 msgid "month" msgstr "" #: superset/db_engine_specs.py:199 superset/db_engine_specs.py:246 -#: superset/db_engine_specs.py:328 superset/db_engine_specs.py:373 -#: superset/db_engine_specs.py:827 superset/db_engine_specs.py:857 -#: superset/db_engine_specs.py:891 superset/db_engine_specs.py:937 -#: superset/db_engine_specs.py:968 superset/db_engine_specs.py:993 +#: superset/db_engine_specs.py:328 superset/db_engine_specs.py:384 +#: superset/db_engine_specs.py:856 superset/db_engine_specs.py:886 +#: superset/db_engine_specs.py:920 superset/db_engine_specs.py:966 +#: superset/db_engine_specs.py:997 superset/db_engine_specs.py:1022 msgid "quarter" msgstr "" #: superset/db_engine_specs.py:200 superset/db_engine_specs.py:250 -#: superset/db_engine_specs.py:330 superset/db_engine_specs.py:829 -#: superset/db_engine_specs.py:859 superset/db_engine_specs.py:939 -#: superset/db_engine_specs.py:969 superset/db_engine_specs.py:994 +#: superset/db_engine_specs.py:330 superset/db_engine_specs.py:858 +#: superset/db_engine_specs.py:888 superset/db_engine_specs.py:968 +#: superset/db_engine_specs.py:998 superset/db_engine_specs.py:1023 msgid "year" msgstr "" @@ -94,27 +95,27 @@ msgstr "" msgid "week_start_monday" msgstr "" -#: superset/db_engine_specs.py:375 superset/db_engine_specs.py:893 +#: superset/db_engine_specs.py:386 superset/db_engine_specs.py:922 msgid "week_ending_saturday" msgstr "" -#: superset/db_engine_specs.py:378 superset/db_engine_specs.py:896 +#: superset/db_engine_specs.py:389 superset/db_engine_specs.py:925 msgid "week_start_sunday" msgstr "" -#: superset/db_engine_specs.py:815 superset/db_engine_specs.py:927 +#: superset/db_engine_specs.py:844 superset/db_engine_specs.py:956 msgid "5 minute" msgstr "" -#: superset/db_engine_specs.py:817 +#: superset/db_engine_specs.py:846 msgid "half hour" msgstr "" -#: superset/db_engine_specs.py:929 +#: superset/db_engine_specs.py:958 msgid "10 minute" msgstr "" -#: superset/utils.py:499 +#: superset/utils.py:503 #, python-format msgid "[Superset] Access to the datasource %(name)s was granted" msgstr "" @@ -123,237 +124,2569 @@ msgstr "" msgid "Viz is missing a datasource" msgstr "" -#: superset/viz.py:158 +#: superset/viz.py:163 msgid "From date cannot be larger than to date" msgstr "" -#: superset/viz.py:322 +#: superset/assets/javascripts/explore/stores/visTypes.js:321 +#: superset/viz.py:328 msgid "Table View" msgstr "" -#: superset/viz.py:334 +#: superset/viz.py:340 msgid "Pick a granularity in the Time section or uncheck 'Include Time'" msgstr "" -#: superset/viz.py:344 +#: superset/viz.py:350 msgid "Choose either fields to [Group By] and [Metrics] or [Columns], not both" msgstr "" -#: superset/viz.py:378 +#: superset/assets/javascripts/explore/stores/visTypes.js:372 +#: superset/viz.py:384 msgid "Pivot Table" msgstr "" -#: superset/viz.py:392 +#: superset/viz.py:398 msgid "Please choose at least one \"Group by\" field " msgstr "" -#: superset/viz.py:394 -msgid "Please choose at least one metric" +#: superset/viz.py:400 +msgid "Please choose at least one metric" +msgstr "" + +#: superset/viz.py:404 +msgid "'Group By' and 'Columns' can't overlap" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:359 +#: superset/viz.py:437 +msgid "Markup" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:397 +#: superset/viz.py:456 +msgid "Separator" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:419 +#: superset/viz.py:468 +msgid "Word Cloud" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:440 +#: superset/viz.py:491 +msgid "Treemap" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:467 +#: superset/viz.py:517 +msgid "Calendar Heatmap" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:488 +#: superset/viz.py:575 +msgid "Box Plot" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:509 +#: superset/viz.py:664 +msgid "Bubble Chart" +msgstr "" + +#: superset/viz.py:688 +msgid "Pick a metric for x, y and size" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:560 +#: superset/viz.py:714 +msgid "Bullet Chart" +msgstr "" + +#: superset/viz.py:740 +msgid "Pick a metric to display" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:583 +#: superset/viz.py:763 +msgid "Big Number with Trendline" +msgstr "" + +#: superset/viz.py:771 superset/viz.py:800 +msgid "Pick a metric!" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:608 +#: superset/viz.py:792 +msgid "Big Number" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:149 +#: superset/viz.py:819 +msgid "Time Series - Line Chart" +msgstr "" + +#: superset/viz.py:866 superset/viz.py:1011 +msgid "Pick a time granularity for your time series" +msgstr "" + +#: superset/viz.py:954 +msgid "Time Series - Dual Axis Line Chart" +msgstr "" + +#: superset/viz.py:964 +msgid "Pick a metric for left axis!" +msgstr "" + +#: superset/viz.py:966 +msgid "Pick a metric for right axis!" +msgstr "" + +#: superset/viz.py:968 +msgid "Please choose different metrics on left and right axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:228 +#: superset/viz.py:1029 +msgid "Time Series - Bar Chart" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:263 +#: superset/viz.py:1037 +msgid "Time Series - Percent Change" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:285 +#: superset/viz.py:1045 +msgid "Time Series - Stacked" +msgstr "" + +#: superset/viz.py:1054 +msgid "Distribution - NVD3 - Pie Chart" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:633 +#: superset/viz.py:1072 +msgid "Histogram" +msgstr "" + +#: superset/viz.py:1082 +msgid "Must have one numeric column specified" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:88 +#: superset/viz.py:1097 +msgid "Distribution - Bar Chart" +msgstr "" + +#: superset/viz.py:1108 +msgid "Can't have overlap between Series and Breakdowns" +msgstr "" + +#: superset/viz.py:1110 +msgid "Pick at least one metric" +msgstr "" + +#: superset/viz.py:1112 +msgid "Pick at least one field for [Series]" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:665 +#: superset/viz.py:1165 +msgid "Sunburst" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:702 +#: superset/viz.py:1198 +msgid "Sankey" +msgstr "" + +#: superset/viz.py:1205 +msgid "Pick exactly 2 columns as [Source / Target]" +msgstr "" + +#: superset/viz.py:1236 +msgid "" +"There's a loop in your Sankey, please provide a tree. Here's a faulty " +"link: {}" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:729 +#: superset/viz.py:1247 superset/viz.py:1268 +msgid "Directed Force Layout" +msgstr "" + +#: superset/viz.py:1254 +msgid "Pick exactly 2 columns to 'Group By'" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:794 +#: superset/viz.py:1301 +msgid "Country Map" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:827 +#: superset/viz.py:1330 +msgid "World Map" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:72 +#: superset/viz.py:1380 +msgid "Filters" +msgstr "" + +#: superset/viz.py:1388 +msgid "Pick at least one filter field" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:895 +#: superset/viz.py:1415 +msgid "iFrame" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:907 +#: superset/viz.py:1432 +msgid "Parallel Coordinates" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:929 +#: superset/viz.py:1457 +msgid "Heatmap" +msgstr "" + +#: superset/viz.py:1508 +msgid "Horizon Charts" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:974 +#: superset/viz.py:1519 +msgid "Mapbox" +msgstr "" + +#: superset/viz.py:1534 +msgid "Must have a [Group By] column to have 'count' as the [Label]" +msgstr "" + +#: superset/viz.py:1547 +msgid "Choice of [Label] must be present in [Group By]" +msgstr "" + +#: superset/viz.py:1552 +msgid "Choice of [Point Radius] must be present in [Group By]" +msgstr "" + +#: superset/viz.py:1557 +msgid "[Longitude] and [Latitude] columns must be present in [Group By]" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1045 +#: superset/viz.py:1622 +msgid "Event flow" +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:57 +msgid "Your query was saved" +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:58 +msgid "Your query could not be saved" +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:111 +msgid "Failed at retrieving results from the results backend" +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:157 +msgid "Could not connect to server" +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:162 +msgid "Your session timed out, please refresh your page and try again." +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:181 +msgid "Query was stopped." +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:184 +msgid "Failed at stopping query." +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:297 +#: superset/assets/javascripts/SqlLab/actions.js:310 +msgid "Error occurred while fetching table metadata" +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:364 +msgid "shared query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:372 +#: superset/assets/javascripts/SqlLab/actions.js:392 +msgid "The query couldn't be loaded" +msgstr "" + +#: superset/assets/javascripts/SqlLab/actions.js:425 +msgid "An error occurred while creating the data source" +msgstr "" + +#: superset/assets/javascripts/SqlLab/constants.js:30 +msgid "Pick a chart type!" +msgstr "" + +#: superset/assets/javascripts/SqlLab/constants.js:31 +msgid "To use this chart type you need at least one column flagged as a date" +msgstr "" + +#: superset/assets/javascripts/SqlLab/constants.js:32 +msgid "To use this chart type you need at least one dimension" +msgstr "" + +#: superset/assets/javascripts/SqlLab/constants.js:33 +msgid "To use this chart type you need at least one aggregation function" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryTable.jsx:49 +#: superset/assets/javascripts/SqlLab/reducers.js:11 +msgid "Untitled Query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/reducers.js:44 +#, python-format +msgid "Copy of %s" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx:30 +msgid "share query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx:33 +msgid "copy URL to clipboard" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/HighlightedSql.jsx:61 +msgid "Raw SQL" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/HighlightedSql.jsx:71 +msgid "Source SQL" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/HighlightedSql.jsx:83 +#: superset/assets/javascripts/explore/stores/visTypes.js:40 +msgid "SQL" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryHistory.jsx:28 +msgid "No query history yet..." +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QuerySearch.jsx:106 +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:66 +msgid "It seems you don't have access to any database" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QuerySearch.jsx:154 +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:89 +msgid "Search Results" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QuerySearch.jsx:160 +msgid "[From]-" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QuerySearch.jsx:170 +msgid "[To]-" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QuerySearch.jsx:179 +msgid "[Query Status]" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QuerySearch.jsx:188 +msgid "Search" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryTable.jsx:114 +msgid "Open in SQL Editor" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryTable.jsx:133 +msgid "view results" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryTable.jsx:136 +msgid "Data preview" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryTable.jsx:176 +msgid "Visualize the data out of this query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryTable.jsx:182 +msgid "Overwrite text in editor with a query on this table" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryTable.jsx:188 +msgid "Run query in a new tab" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/QueryTable.jsx:193 +msgid "Remove query from log" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:67 +msgid ".CSV" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:78 +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:241 +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:280 +msgid "Visualize" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:162 +#: superset/connectors/sqla/views.py:86 superset/connectors/sqla/views.py:136 +#: superset/connectors/sqla/views.py:214 superset/views/core.py:376 +msgid "Table" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:162 +msgid "was created" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:169 +msgid "Query in a new tab" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:210 +msgid "Fetch data preview" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:230 +msgid "Track Job" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/ResultSet.jsx:236 +msgid "Loading..." +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/RunQueryActionButton.jsx:19 +msgid "Run Selected Query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/RunQueryActionButton.jsx:19 +msgid "Run Query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/RunQueryActionButton.jsx:22 +msgid "Run query asynchronously" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/RunQueryActionButton.jsx:57 +msgid "Stop" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SaveQuery.jsx:16 +msgid "Undefined" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SaveQuery.jsx:66 +#: superset/views/sql_lab.py:53 +msgid "Label" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SaveQuery.jsx:71 +msgid "Label for your query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SaveQuery.jsx:81 +#: superset/connectors/druid/views.py:107 +#: superset/connectors/druid/views.py:227 superset/connectors/sqla/views.py:83 +#: superset/connectors/sqla/views.py:132 superset/connectors/sqla/views.py:227 +#: superset/views/core.py:370 superset/views/sql_lab.py:56 +msgid "Description" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SaveQuery.jsx:85 +msgid "Write a description for your query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SaveQuery.jsx:99 +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:155 +#: superset/assets/javascripts/explore/components/SaveModal.jsx:217 +msgid "Save" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SaveQuery.jsx:102 +#: superset/templates/superset/request_access.html:16 +msgid "Cancel" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SaveQuery.jsx:123 +msgid "Save Query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SouthPane.jsx:52 +msgid "Run a query to display results here" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SouthPane.jsx:57 +#, python-format +msgid "Preview for %s" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SouthPane.jsx:81 +msgid "Results" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SouthPane.jsx:87 +msgid "Query History" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditor.jsx:120 +msgid "Create table as with query results" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditor.jsx:128 +msgid "new table name" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:90 +msgid "Error while fetching table list" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:131 +msgid "Error while fetching schema list" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:153 +msgid "Error while fetching database list" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:159 +msgid "Database:" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:163 +msgid "Select a database" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:170 +#, python-format +msgid "Select a schema (%s)" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:175 +msgid "Schema:" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:190 +#, python-format +msgid "Add a table (%s)" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:203 +msgid "Type to search ..." +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx:226 +msgid "Reset State" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx:105 +msgid "Enter a new title for the tab" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx:124 +#, python-format +msgid "Untitled Query %s" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx:170 +msgid "close tab" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx:173 +msgid "rename tab" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx:181 +msgid "expand tool bar" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx:181 +msgid "hide tool bar" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TableElement.jsx:75 +msgid "Copy partition query to clipboard" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TableElement.jsx:94 +msgid "latest partition:" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TableElement.jsx:110 +msgid "Keys for table" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TableElement.jsx:119 +#, python-format +msgid "View keys & indexes (%s)" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TableElement.jsx:135 +msgid "Sort columns alphabetically" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TableElement.jsx:136 +msgid "Original table column order" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TableElement.jsx:146 +msgid "Copy SELECT statement to clipboard" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/TableElement.jsx:152 +msgid "Remove table preview" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:90 +#, python-format +msgid "%s is not right as a column name, please alias it (as in SELECT count(*) " +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:91 +msgid "AS my_alias" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:91 +msgid "using only alphanumeric characters and underscores" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:166 +msgid "Creating a data source and popping a new tab" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:196 +msgid "No results available for this query" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:248 +msgid "Chart Type" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:251 +msgid "[Chart Type]" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:259 +msgid "Datasource Name" +msgstr "" + +#: superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx:263 +msgid "datasource name" +msgstr "" + +#: superset/assets/javascripts/components/AsyncSelect.jsx:20 +msgid "Select ..." +msgstr "" + +#: superset/assets/javascripts/components/CachedLabel.jsx:26 +msgid "Loaded data cached" +msgstr "" + +#: superset/assets/javascripts/components/CachedLabel.jsx:28 +msgid "Loaded from cache" +msgstr "" + +#: superset/assets/javascripts/components/CachedLabel.jsx:33 +msgid "Click to force-refresh" +msgstr "" + +#: superset/assets/javascripts/components/CopyToClipboard.jsx:21 +#: superset/assets/javascripts/explore/components/EmbedCodeButton.jsx:67 +#: superset/assets/javascripts/explore/components/URLShortLinkButton.jsx:37 +msgid "Copy to clipboard" +msgstr "" + +#: superset/assets/javascripts/components/CopyToClipboard.jsx:65 +msgid "Not successful" +msgstr "" + +#: superset/assets/javascripts/components/CopyToClipboard.jsx:68 +msgid "Sorry, your browser does not support copying. Use Ctrl / Cmd + C!" +msgstr "" + +#: superset/assets/javascripts/components/CopyToClipboard.jsx:79 +msgid "Copied!" +msgstr "" + +#: superset/assets/javascripts/components/EditableTitle.jsx:12 +#: superset/views/core.py:471 superset/views/core.py:538 +msgid "Title" +msgstr "" + +#: superset/assets/javascripts/components/EditableTitle.jsx:75 +msgid "click to edit title" +msgstr "" + +#: superset/assets/javascripts/components/EditableTitle.jsx:75 +msgid "You don't have the rights to alter this title." +msgstr "" + +#: superset/assets/javascripts/components/FaveStar.jsx:32 +#: superset/assets/javascripts/modules/superset.js:33 +msgid "Click to favorite/unfavorite" +msgstr "" + +#: superset/assets/javascripts/dashboard/Dashboard.jsx:42 +#: superset/assets/javascripts/dashboard/Dashboard.jsx:59 +msgid "You have unsaved changes." +msgstr "" + +#: superset/assets/javascripts/dashboard/Dashboard.jsx:59 +msgid "Click the" +msgstr "" + +#: superset/assets/javascripts/dashboard/Dashboard.jsx:61 +msgid "button on the top right to save your changes." +msgstr "" + +#: superset/assets/javascripts/dashboard/Dashboard.jsx:164 +#, python-format +msgid "Served from data cached %s . Click to force refresh." +msgstr "" + +#: superset/assets/javascripts/dashboard/Dashboard.jsx:169 +msgid "Click to force refresh" +msgstr "" + +#: superset/assets/javascripts/dashboard/Dashboard.jsx:353 +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:100 +msgid "Error" +msgstr "" + +#: superset/assets/javascripts/dashboard/Dashboard.jsx:354 +#, python-format +msgid "Sorry, there was an error adding slices to this dashboard: %s" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/CodeModal.jsx:35 +msgid "Active Dashboard Filters" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/Controls.jsx:48 +#, python-format +msgid "Checkout this dashboard: %s" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/Controls.jsx:54 +msgid "Force refresh the whole dashboard" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/Controls.jsx:94 +msgid "Edit this dashboard's properties" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/CssEditor.jsx:65 +msgid "Load a template" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/CssEditor.jsx:68 +msgid "Load a CSS template" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/CssEditor.jsx:80 +#: superset/views/core.py:478 +msgid "CSS" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/CssEditor.jsx:86 +msgid "Live CSS Editor" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/RefreshIntervalModal.jsx:19 +msgid "Don't refresh" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/RefreshIntervalModal.jsx:20 +msgid "10 seconds" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/RefreshIntervalModal.jsx:21 +msgid "30 seconds" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/RefreshIntervalModal.jsx:22 +msgid "1 minute" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/RefreshIntervalModal.jsx:23 +msgid "5 minutes" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/RefreshIntervalModal.jsx:38 +msgid "Refresh Interval" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/RefreshIntervalModal.jsx:41 +msgid "Choose the refresh frequency for this dashboard" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:63 +msgid "This dashboard was saved successfully." +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:69 +msgid "Sorry, there was an error saving this dashboard: " +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:101 +msgid "You must pick a name for the new dashboard" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:115 +msgid "Save Dashboard" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:123 +#, python-format +msgid "Overwrite Dashboard [%s]" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:131 +msgid "Save as:" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SaveModal.jsx:135 +#: superset/assets/javascripts/explore/components/SaveModal.jsx:205 +msgid "[dashboard name]" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceAdder.jsx:142 +#: superset/views/core.py:375 +msgid "Name" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceAdder.jsx:148 +msgid "Viz" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceAdder.jsx:157 +#: superset/views/core.py:476 superset/views/core.py:540 +#: superset/views/sql_lab.py:57 +msgid "Modified" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceAdder.jsx:167 +msgid "Add Slices" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceAdder.jsx:176 +msgid "Add a new slice to the dashboard" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceAdder.jsx:181 +msgid "Add Slices to Dashboard" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceCell.jsx:24 +msgid "Move chart" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceCell.jsx:27 +msgid "Force refresh data" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceCell.jsx:31 +msgid "Toggle chart description" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceCell.jsx:41 +msgid "Edit chart" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceCell.jsx:46 +msgid "Export CSV" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceCell.jsx:49 +msgid "Explore chart" +msgstr "" + +#: superset/assets/javascripts/dashboard/components/SliceCell.jsx:54 +msgid "Remove chart from dashboard" +msgstr "" + +#: superset/assets/javascripts/explore/components/ChartContainer.jsx:173 +#, python-format +msgid "%s - untitled" +msgstr "" + +#: superset/assets/javascripts/explore/components/ChartContainer.jsx:280 +msgid "Edit slice properties" +msgstr "" + +#: superset/assets/javascripts/explore/components/ControlHeader.jsx:32 +msgid "description" +msgstr "" + +#: superset/assets/javascripts/explore/components/ControlHeader.jsx:42 +msgid "bolt" +msgstr "" + +#: superset/assets/javascripts/explore/components/DisplayQueryButton.jsx:61 +msgid "Error..." +msgstr "" + +#: superset/assets/javascripts/explore/components/DisplayQueryButton.jsx:97 +#: superset/assets/javascripts/explore/stores/visTypes.js:49 +#: superset/assets/javascripts/explore/stores/visTypes.js:129 +#: superset/assets/javascripts/explore/stores/visTypes.js:375 +#: superset/assets/javascripts/explore/stores/visTypes.js:422 +#: superset/assets/javascripts/explore/stores/visTypes.js:443 +#: superset/assets/javascripts/explore/stores/visTypes.js:471 +#: superset/assets/javascripts/explore/stores/visTypes.js:491 +#: superset/assets/javascripts/explore/stores/visTypes.js:512 +#: superset/assets/javascripts/explore/stores/visTypes.js:564 +#: superset/assets/javascripts/explore/stores/visTypes.js:586 +#: superset/assets/javascripts/explore/stores/visTypes.js:611 +#: superset/assets/javascripts/explore/stores/visTypes.js:636 +#: superset/assets/javascripts/explore/stores/visTypes.js:668 +#: superset/assets/javascripts/explore/stores/visTypes.js:705 +#: superset/assets/javascripts/explore/stores/visTypes.js:732 +#: superset/assets/javascripts/explore/stores/visTypes.js:759 +#: superset/assets/javascripts/explore/stores/visTypes.js:797 +#: superset/assets/javascripts/explore/stores/visTypes.js:830 +#: superset/assets/javascripts/explore/stores/visTypes.js:867 +#: superset/assets/javascripts/explore/stores/visTypes.js:910 +#: superset/assets/javascripts/explore/stores/visTypes.js:977 +msgid "Query" +msgstr "" + +#: superset/assets/javascripts/explore/components/EmbedCodeButton.jsx:76 +msgid "Height" +msgstr "" + +#: superset/assets/javascripts/explore/components/EmbedCodeButton.jsx:90 +msgid "Width" +msgstr "" + +#: superset/assets/javascripts/explore/components/ExploreActionButtons.jsx:32 +msgid "Export to .json" +msgstr "" + +#: superset/assets/javascripts/explore/components/ExploreActionButtons.jsx:42 +msgid "Export to .csv format" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:74 +msgid "Please enter a slice name" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:89 +msgid "Please select a dashboard" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:97 +msgid "Please enter a dashboard name" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:130 +msgid "Save A Slice" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:151 +#, python-format +msgid "Overwrite slice %s" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:160 +msgid "Save as" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:164 +msgid "[slice name]" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:177 +msgid "Do not add to a dashboard" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:185 +msgid "Add slice to existing dashboard" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:200 +msgid "Add to new dashboard" +msgstr "" + +#: superset/assets/javascripts/explore/components/SaveModal.jsx:226 +msgid "Save & go to dashboard" +msgstr "" + +#: superset/assets/javascripts/explore/components/URLShortLinkButton.jsx:32 +#, python-format +msgid "Check out this slice: %s" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/BoundsControl.jsx:55 +msgid "`Min` value should be numeric or empty" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/BoundsControl.jsx:58 +msgid "`Max` value should be numeric or empty" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/BoundsControl.jsx:75 +#: superset/connectors/druid/views.py:50 superset/connectors/sqla/views.py:89 +msgid "Min" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/BoundsControl.jsx:83 +#: superset/connectors/druid/views.py:51 superset/connectors/sqla/views.py:90 +msgid "Max" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx:70 +msgid "Something went wrong while fetching the datasource list" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx:95 +msgid "Click to point to another datasource" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx:106 +msgid "Edit the datasource's configuration" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx:122 +msgid "Select a datasource" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx:132 +#: superset/assets/javascripts/explore/components/controls/VizTypeControl.jsx:120 +msgid "Search / Filter" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/Filter.jsx:118 +msgid "Filter value" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/Filter.jsx:147 +msgid "Select metric" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/Filter.jsx:147 +msgid "Select column" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/Filter.jsx:159 +msgid "Select operator" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:70 +#: superset/templates/appbuilder/general/widgets/search.html:6 +msgid "Add Filter" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/SelectControl.jsx:106 +#, python-format +msgid "Select %s" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/TextAreaControl.jsx:63 +msgid "textarea" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/TextAreaControl.jsx:81 +msgid "Edit" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/TextAreaControl.jsx:81 +msgid "in modal" +msgstr "" + +#: superset/assets/javascripts/explore/components/controls/VizTypeControl.jsx:110 +msgid "Select a visualization type" +msgstr "" + +#: superset/assets/javascripts/explore/reducers/chartReducer.js:32 +msgid "Updating chart was stopped" +msgstr "" + +#: superset/assets/javascripts/explore/reducers/chartReducer.js:38 +#: superset/assets/javascripts/modules/superset.js:222 +#, python-format +msgid "An error occurred while rendering the visualization: %s" +msgstr "" + +#: superset/assets/javascripts/explore/reducers/chartReducer.js:47 +msgid "" +"Perhaps your data has grown, your database is under unusual load, or you " +"are simply querying a data source that is to large to be processed within" +" the timeout range. If that is the case, we recommend that you summarize " +"your data further." +msgstr "" + +#: superset/assets/javascripts/explore/reducers/chartReducer.js:56 +msgid "Network error." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:36 +msgid "A reference to the [Time] configuration, taking granularity into account" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:44 +msgid "Group by" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:47 +msgid "One or many controls to group by" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:66 +#: superset/connectors/druid/views.py:45 superset/views/core.py:314 +#: superset/views/core.py:338 superset/views/core.py:369 +msgid "Datasource" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:76 +#: superset/views/core.py:377 +msgid "Visualization Type" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:78 +msgid "The type of visualization to display" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:84 +msgid "Metrics" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:93 +#: superset/assets/javascripts/explore/stores/controls.jsx:110 +msgid "One or many metrics to display" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:97 +msgid "Y Axis Bounds" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:100 +msgid "" +"Bounds for the Y axis. When left empty, the bounds are dynamically " +"defined based on the min/max of the data. Note that this feature will " +"only expand the axis range. It won't narrow the data's extent." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:108 +msgid "Ordering" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:118 +#: superset/assets/javascripts/explore/stores/visTypes.js:818 +#: superset/connectors/druid/views.py:106 superset/connectors/sqla/views.py:131 +msgid "Metric" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:120 +msgid "Choose the metric" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:133 +msgid "Right Axis Metric" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:137 +msgid "Choose a metric for right axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:148 +msgid "Stacked Style" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:160 +msgid "Linear Color Scheme" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:177 +msgid "Normalize Across" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:184 +msgid "" +"Color will be rendered based on a ratio of the cell against the sum of " +"across this criteria" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:191 +msgid "Horizon Color Scale" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:198 +msgid "Defines how the color are attributed." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:203 +msgid "Rendering" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:209 +msgid "" +"image-rendering CSS attribute of the canvas object that defines how the " +"browser scales up the image" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:215 +msgid "XScale Interval" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:218 +msgid "Number of steps to take between ticks when displaying the X scale" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:224 +msgid "YScale Interval" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:227 +msgid "Number of steps to take between ticks when displaying the Y scale" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:233 +msgid "Include Time" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:234 +msgid "Whether to include the time granularity as defined in the time section" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:240 +msgid "Stacked Bars" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:248 +msgid "Show totals" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:251 +msgid "Display total row/column" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:256 +msgid "Show Markers" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:259 +msgid "Show data points as circle markers on the lines" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:264 +msgid "Bar Values" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:267 +msgid "Show the value on top of the bar" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:272 +msgid "Sort Bars" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:274 +msgid "Sort bars by x labels." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:279 +msgid "Combine Metrics" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:281 +msgid "" +"Display metrics side by side within each column, as opposed to each " +"column being displayed side by side for each metric." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:287 +msgid "Extra Controls" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:290 +msgid "" +"Whether to show extra controls or not. Extra controls include things like" +" making mulitBar charts stacked or side by side." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:297 +msgid "Reduce X ticks" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:300 +msgid "" +"Reduces the number of X axis ticks to be rendered. If true, the x axis " +"wont overflow and labels may be missing. If false, a minimum width will " +"be applied to columns and the width may overflow into an horizontal " +"scroll." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:309 +msgid "Include Series" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:312 +msgid "Include series name as an axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:317 +msgid "Color Metric" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:319 +msgid "A metric to use for color" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:326 +msgid "Country Name" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:345 +msgid "The name of country that Superset should display" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:349 +msgid "Country Field Type" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:357 +msgid "" +"The country code standard that Superset should expect to find in the " +"[country] column" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:364 +#: superset/assets/javascripts/explore/stores/controls.jsx:371 +msgid "Columns" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:365 +msgid "One or many controls to pivot as columns" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:373 +#: superset/assets/javascripts/explore/stores/controls.jsx:383 +#: superset/assets/javascripts/explore/stores/controls.jsx:393 +msgid "Columns to display" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:402 +msgid "Origin" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:408 +msgid "" +"Defines the origin where time buckets start, accepts natural dates as in " +"`now`, `sunday` or `1970-01-01`" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:415 +msgid "Bottom Margin" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:419 +msgid "Bottom margin, in pixels, allowing for more room for axis labels" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:425 +msgid "Left Margin" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:429 +msgid "Left margin, in pixels, allowing for more room for axis labels" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:435 +msgid "Time Granularity" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:452 +msgid "" +"The time granularity for the visualization. Note that you can type and " +"use simple natural language as in `10 seconds`, `1 day` or `56 weeks`" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:459 +msgid "Domain" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:462 +msgid "The time unit used for the grouping of blocks" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:467 +msgid "Subdomain" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:470 +msgid "" +"The time unit for each block. Should be a smaller unit than " +"domain_granularity. Should be larger or equal to Time Grain" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:477 +msgid "Link Length" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:480 +msgid "Link length in the force layout" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:486 +msgid "Charge" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:500 +msgid "Charge in the force layout" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:508 +msgid "" +"The time column for the visualization. Note that you can define arbitrary" +" expression that return a DATETIME column in the table or. Also note that" +" the filter below is applied against this column or expression" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:520 +msgid "Time Grain" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:522 +msgid "" +"The time granularity for the visualization. This applies a date " +"transformation to alter your time column and defines a new time " +"granularity. The options here are defined on a per database engine basis " +"in the Superset source code." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:535 +msgid "Resample Rule" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:538 +msgid "Pandas resample rule" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:544 +msgid "Resample How" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:547 +msgid "Pandas resample how" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:553 +msgid "Resample Fill Method" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:556 +msgid "Pandas resample fill method" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:562 +msgid "Since" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:563 +msgid "7 days ago" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:569 +msgid "Until" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:576 +msgid "Max Bubble Size" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:584 +msgid "Whisker/outlier options" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:586 +msgid "Determines how whiskers and outliers are calculated." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:597 +msgid "Ratio" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:600 +msgid "Target aspect ratio for treemap tiles." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:606 +#: superset/assets/javascripts/explore/stores/visTypes.js:602 +#: superset/assets/javascripts/explore/stores/visTypes.js:627 +#: superset/assets/javascripts/explore/stores/visTypes.js:776 +msgid "Number format" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:616 +msgid "Row limit" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:624 +msgid "Series limit" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:627 +msgid "Limits the number of time series that get displayed" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:632 +msgid "Sort By" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:634 +msgid "Metric used to define the top series" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:642 +msgid "Rolling" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:645 +msgid "" +"Defines a rolling window function to apply, works along with the " +"[Periods] text box" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:651 +msgid "Periods" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:653 +msgid "" +"Defines the size of the rolling window function, relative to the time " +"granularity selected" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:659 +msgid "Min Periods" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:661 +msgid "" +"The minimum number of rolling periods required to show a value. For " +"instance if you do a cumulative sum on 7 days you may want your \"Min " +"Period\" to be 7, so that all data points shown are the total of 7 " +"periods. This will hide the \"ramp up\" taking place over the first 7 " +"periods" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:670 +#: superset/assets/javascripts/explore/stores/visTypes.js:115 +msgid "Series" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:672 +msgid "" +"Defines the grouping of entities. Each series is shown as a specific " +"color on the chart and has a legend toggle" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:682 +msgid "Entity" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:685 +msgid "This defines the element to be plotted on the chart" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:693 +#: superset/assets/javascripts/explore/stores/visTypes.js:164 +#: superset/assets/javascripts/explore/stores/visTypes.js:533 +msgid "X Axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:694 +msgid "Metric assigned to the [X] axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:707 +#: superset/assets/javascripts/explore/stores/visTypes.js:171 +#: superset/assets/javascripts/explore/stores/visTypes.js:541 +msgid "Y Axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:710 +msgid "Metric assigned to the [Y] axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:721 +msgid "Bubble Size" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:734 +msgid "URL" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:735 +msgid "" +"The URL, this control is templated, so you can integrate {{ width }} " +"and/or {{ height }} in your URL string." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:742 +msgid "X Axis Label" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:749 +msgid "Y Axis Label" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:756 +msgid "Custom WHERE clause" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:758 +msgid "" +"The text in this box gets included in your query's WHERE clause, as an " +"AND to other criteria. You can include complex expression, parenthesis " +"and anything else supported by the backend it is directed towards." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:766 +msgid "Custom HAVING clause" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:768 +msgid "" +"The text in this box gets included in your query's HAVING clause, as an " +"AND to other criteria. You can include complex expression, parenthesis " +"and anything else supported by the backend it is directed towards." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:776 +msgid "Comparison Period Lag" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:778 +msgid "Based on granularity, number of time periods to compare against" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:783 +msgid "Comparison suffix" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:784 +msgid "Suffix to apply after the percentage display" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:790 +msgid "Table Timestamp Format" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:795 +msgid "Timestamp Format" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:801 +msgid "Series Height" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:804 +msgid "Pixel height of each series" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:810 +msgid "Page Length" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:813 +msgid "Rows per page, 0 means no pagination" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:819 +#: superset/assets/javascripts/explore/stores/controls.jsx:829 +msgid "X Axis Format" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:839 +msgid "Y Axis Format" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:849 +msgid "Right Axis Format" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:857 +msgid "Markup Type" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:862 +msgid "Pick your favorite markup language" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:867 +msgid "Rotation" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:870 +msgid "Rotation to apply to words in the cloud" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:875 +msgid "Line Style" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:880 +msgid "Line interpolation as defined by d3.js" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:885 +msgid "Label Type" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:892 +msgid "What should be shown on the label?" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:897 +#: superset/assets/javascripts/explore/stores/visTypes.js:362 +#: superset/assets/javascripts/explore/stores/visTypes.js:400 +msgid "Code" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:898 +msgid "Put your code here" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:907 +msgid "Aggregation function" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:919 +msgid "" +"Aggregate function to apply when pivoting and computing the total rows " +"and columns" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:926 +msgid "Font Size From" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:928 +msgid "Font size for the smallest value in the list" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:934 +msgid "Font Size To" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:936 +msgid "Font size for the biggest value in the list" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:941 +msgid "Instant Filtering" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:952 +msgid "Range Filter" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:955 +msgid "Whether to display the time range interactive selector" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:960 +msgid "Date Filter" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:962 +msgid "Whether to include a time filter" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:967 +msgid "Data Table" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:969 +msgid "Whether to display the interactive data table" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:974 +msgid "Search Box" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:977 +msgid "Whether to include a client side search box" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:982 +msgid "Table Filter" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:984 +msgid "Whether to apply filter when table cell is clicked" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:989 +msgid "Show Bubbles" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:992 +msgid "Whether to display bubbles on top of countries" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:997 +msgid "Legend" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1000 +msgid "Whether to display the legend (toggles)" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1005 +msgid "X bounds" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1008 +msgid "Whether to display the min and max values of the X axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1013 +msgid "Y bounds" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1016 +msgid "Whether to display the min and max values of the Y axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1021 +msgid "Rich Tooltip" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1024 +msgid "The rich tooltip shows a list of all series for that point in time" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1030 +msgid "Y Log Scale" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1033 +msgid "Use a log scale for the Y axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1038 +msgid "X Log Scale" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1041 +msgid "Use a log scale for the X axis" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1046 +msgid "Donut" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1049 +msgid "Do you want a donut or a pie?" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1054 +msgid "Put labels outside" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1057 +msgid "Put the labels outside the pie?" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1062 +msgid "Contribution" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1064 +msgid "Compute the contribution to the total" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1069 +msgid "Period Ratio" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1072 +msgid "" +"[integer] Number of period to compare against, this is relative to the " +"granularity selected" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1078 +msgid "Period Ratio Type" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1081 +msgid "" +"`factor` means (new/previous), `growth` is ((new/previous) - 1), `value` " +"is (new-previous)" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1087 +msgid "Time Shift" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1089 +msgid "" +"Overlay a timeseries from a relative time period. Expects relative time " +"delta in natural language (example: 24 hours, 7 days, 56 weeks, 365 " +"days)" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1097 +msgid "Subheader" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1098 +msgid "Description text that shows up below your Big Number" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1104 +msgid "label" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1106 +msgid "" +"`count` is COUNT(*) if a group by is used. Numerical columns will be " +"aggregated with the aggregator. Non-numerical columns will be used to " +"label points. Leave empty to get a count of points in each cluster." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1117 +msgid "Map Style" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1127 +msgid "Base layer map style" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1133 +msgid "Clustering Radius" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1146 +msgid "" +"The radius (in pixels) the algorithm uses to define a cluster. Choose 0 " +"to turn off clustering, but beware that a large number of points (>1000) " +"will cause lag." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1153 +msgid "Point Radius" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1155 +msgid "" +"The radius of individual points (ones that are not in a cluster). Either " +"a numerical column or `Auto`, which scales the point based on the largest" +" cluster" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1165 +msgid "Point Radius Unit" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1168 +msgid "The unit of measure for the specified point radius" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1173 +msgid "Opacity" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1176 +msgid "Opacity of all clusters, points, and labels. Between 0 and 1." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1182 +msgid "Zoom" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1185 +msgid "Zoom level of the map" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1191 +msgid "Default latitude" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1194 +msgid "Latitude of default viewport" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1200 +msgid "Default longitude" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1203 +msgid "Longitude of default viewport" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1209 +msgid "Live render" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1211 +msgid "Points and clusters will update as viewport is being changed" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1217 +msgid "RGB Color" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1227 +msgid "The color for points and clusters in RGB" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1232 +msgid "Ranges" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1234 +msgid "Ranges to highlight with shading" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1239 +msgid "Range labels" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1241 +msgid "Labels for the ranges" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1246 +msgid "Markers" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1248 +msgid "List of values to mark with triangles" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1253 +msgid "Marker labels" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1255 +msgid "Labels for the markers" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1260 +msgid "Marker lines" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1262 +msgid "List of values to mark with lines" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1267 +msgid "Marker line labels" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1269 +msgid "Labels for the marker lines" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1296 +msgid "Slice ID" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1298 +msgid "The id of the active slice" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1303 +msgid "Cache Timeout (seconds)" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1305 +msgid "The number of seconds before expiring the cache" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1310 +msgid "Order by entity id" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1311 +msgid "" +"Important! Select this if the table is not already sorted by entity id, " +"else there is no guarantee that all events for each entity are returned." +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1319 +msgid "Minimum leaf node event count" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1322 +msgid "" +"Leaf nodes that represent fewer than this number of events will be " +"initially hidden in the visualization" +msgstr "" + +#: superset/assets/javascripts/explore/stores/controls.jsx:1328 +#: superset/assets/javascripts/explore/stores/visTypes.js:25 +msgid "Color Scheme" msgstr "" -#: superset/viz.py:398 -msgid "'Group By' and 'Columns' can't overlap" +#: superset/assets/javascripts/explore/stores/controls.jsx:1332 +msgid "The color scheme for rendering chart" msgstr "" -#: superset/viz.py:431 -msgid "Markup" +#: superset/assets/javascripts/explore/stores/visTypes.js:7 +#: superset/assets/javascripts/explore/stores/visTypes.js:31 +msgid "Time" msgstr "" -#: superset/viz.py:450 -msgid "Separator" +#: superset/assets/javascripts/explore/stores/visTypes.js:9 +#: superset/assets/javascripts/explore/stores/visTypes.js:32 +msgid "Time related form attributes" msgstr "" -#: superset/viz.py:466 -msgid "Word Cloud" +#: superset/assets/javascripts/explore/stores/visTypes.js:16 +msgid "Datasource & Chart Type" msgstr "" -#: superset/viz.py:489 -msgid "Treemap" +#: superset/assets/javascripts/explore/stores/visTypes.js:45 +msgid "This section exposes ways to include snippets of SQL in your query" msgstr "" -#: superset/viz.py:515 -msgid "Calendar Heatmap" +#: superset/assets/javascripts/explore/stores/visTypes.js:58 +msgid "Advanced Analytics" msgstr "" -#: superset/viz.py:573 -msgid "Box Plot" +#: superset/assets/javascripts/explore/stores/visTypes.js:59 +msgid "" +"This section contains options that allow for advanced analytical post " +"processing of query results" msgstr "" -#: superset/viz.py:662 -msgid "Bubble Chart" +#: superset/assets/javascripts/explore/stores/visTypes.js:77 +msgid "Result Filters" msgstr "" -#: superset/viz.py:686 -msgid "Pick a metric for x, y and size" +#: superset/assets/javascripts/explore/stores/visTypes.js:79 +msgid "" +"The filters to apply after post-aggregation.Leave the value control empty" +" to filter empty strings or nulls" msgstr "" -#: superset/viz.py:712 -msgid "Bullet Chart" +#: superset/assets/javascripts/explore/stores/visTypes.js:92 +#: superset/assets/javascripts/explore/stores/visTypes.js:101 +#: superset/assets/javascripts/explore/stores/visTypes.js:137 +#: superset/assets/javascripts/explore/stores/visTypes.js:155 +#: superset/assets/javascripts/explore/stores/visTypes.js:193 +#: superset/assets/javascripts/explore/stores/visTypes.js:234 +#: superset/assets/javascripts/explore/stores/visTypes.js:268 +#: superset/assets/javascripts/explore/stores/visTypes.js:290 +#: superset/assets/javascripts/explore/stores/visTypes.js:451 +#: superset/assets/javascripts/explore/stores/visTypes.js:499 +#: superset/assets/javascripts/explore/stores/visTypes.js:520 +#: superset/assets/javascripts/explore/stores/visTypes.js:644 +#: superset/assets/javascripts/explore/stores/visTypes.js:677 +#: superset/assets/javascripts/explore/stores/visTypes.js:714 +#: superset/assets/javascripts/explore/stores/visTypes.js:767 +#: superset/assets/javascripts/explore/stores/visTypes.js:965 +msgid "Chart Options" msgstr "" -#: superset/viz.py:738 -msgid "Pick a metric to display" +#: superset/assets/javascripts/explore/stores/visTypes.js:118 +msgid "Breakdowns" msgstr "" -#: superset/viz.py:761 -msgid "Big Number with Trendline" +#: superset/assets/javascripts/explore/stores/visTypes.js:119 +msgid "Defines how each series is broken down" msgstr "" -#: superset/viz.py:769 superset/viz.py:798 -msgid "Pick a metric!" +#: superset/assets/javascripts/explore/stores/visTypes.js:125 +msgid "Pie Chart" msgstr "" -#: superset/viz.py:790 -msgid "Big Number" +#: superset/assets/javascripts/explore/stores/visTypes.js:189 +msgid "Dual Axis Line Chart" msgstr "" -#: superset/viz.py:817 -msgid "Time Series - Line Chart" +#: superset/assets/javascripts/explore/stores/visTypes.js:200 +msgid "Y Axis 1" msgstr "" -#: superset/viz.py:864 superset/viz.py:1001 -msgid "Pick a time granularity for your time series" +#: superset/assets/javascripts/explore/stores/visTypes.js:206 +msgid "Y Axis 2" msgstr "" -#: superset/viz.py:944 -msgid "Time Series - Dual Axis Line Chart" +#: superset/assets/javascripts/explore/stores/visTypes.js:214 +msgid "Left Axis Metric" msgstr "" -#: superset/viz.py:954 -msgid "Pick a metric for left axis!" +#: superset/assets/javascripts/explore/stores/visTypes.js:215 +msgid "Choose a metric for left axis" msgstr "" -#: superset/viz.py:956 -msgid "Pick a metric for right axis!" +#: superset/assets/javascripts/explore/stores/visTypes.js:218 +msgid "Left Axis Format" msgstr "" -#: superset/viz.py:958 -msgid "Please choose different metrics on left and right axis" +#: superset/assets/javascripts/explore/stores/visTypes.js:244 +#: superset/assets/javascripts/explore/stores/visTypes.js:300 +msgid "Axes" msgstr "" -#: superset/viz.py:1019 -msgid "Time Series - Bar Chart" +#: superset/assets/javascripts/explore/stores/visTypes.js:324 +msgid "GROUP BY" msgstr "" -#: superset/viz.py:1027 -msgid "Time Series - Percent Change" +#: superset/assets/javascripts/explore/stores/visTypes.js:325 +msgid "Use this section if you want a query that aggregates" msgstr "" -#: superset/viz.py:1035 -msgid "Time Series - Stacked" +#: superset/assets/javascripts/explore/stores/visTypes.js:332 +msgid "NOT GROUPED BY" msgstr "" -#: superset/viz.py:1044 -msgid "Distribution - NVD3 - Pie Chart" +#: superset/assets/javascripts/explore/stores/visTypes.js:333 +msgid "Use this section if you want to query atomic rows" msgstr "" -#: superset/viz.py:1062 -msgid "Histogram" +#: superset/assets/javascripts/explore/stores/visTypes.js:340 +#: superset/assets/javascripts/explore/stores/visTypes.js:741 +#: superset/assets/javascripts/explore/stores/visTypes.js:805 +#: superset/assets/javascripts/explore/stores/visTypes.js:898 +msgid "Options" msgstr "" -#: superset/viz.py:1072 -msgid "Must have one numeric column specified" +#: superset/assets/javascripts/explore/stores/visTypes.js:527 +#: superset/assets/javascripts/explore/stores/visTypes.js:839 +msgid "Bubbles" msgstr "" -#: superset/viz.py:1087 -msgid "Distribution - Bar Chart" +#: superset/assets/javascripts/explore/stores/visTypes.js:653 +msgid "Numeric Column" msgstr "" -#: superset/viz.py:1098 -msgid "Can't have overlap between Series and Breakdowns" +#: superset/assets/javascripts/explore/stores/visTypes.js:654 +msgid "Select the numeric column to draw the histogram" msgstr "" -#: superset/viz.py:1100 -msgid "Pick at least one metric" +#: superset/assets/javascripts/explore/stores/visTypes.js:657 +msgid "No of Bins" msgstr "" -#: superset/viz.py:1102 -msgid "Pick at least one field for [Series]" +#: superset/assets/javascripts/explore/stores/visTypes.js:658 +msgid "Select number of bins for the histogram" msgstr "" -#: superset/viz.py:1155 -msgid "Sunburst" +#: superset/assets/javascripts/explore/stores/visTypes.js:685 +msgid "Primary Metric" msgstr "" -#: superset/viz.py:1188 -msgid "Sankey" +#: superset/assets/javascripts/explore/stores/visTypes.js:686 +msgid "The primary metric is used to define the arc segment sizes" msgstr "" -#: superset/viz.py:1195 -msgid "Pick exactly 2 columns as [Source / Target]" +#: superset/assets/javascripts/explore/stores/visTypes.js:689 +msgid "Secondary Metric" msgstr "" -#: superset/viz.py:1226 +#: superset/assets/javascripts/explore/stores/visTypes.js:690 msgid "" -"There's a loop in your Sankey, please provide a tree. Here's a faulty " -"link: {}" +"This secondary metric is used to define the color as a ratio against the " +"primary metric. If the two metrics match, color is mapped level groups" msgstr "" -#: superset/viz.py:1237 superset/viz.py:1258 -msgid "Directed Force Layout" +#: superset/assets/javascripts/explore/stores/visTypes.js:695 +msgid "Hierarchy" msgstr "" -#: superset/viz.py:1244 -msgid "Pick exactly 2 columns to 'Group By'" +#: superset/assets/javascripts/explore/stores/visTypes.js:696 +msgid "This defines the level of the hierarchy" msgstr "" -#: superset/viz.py:1291 -msgid "Country Map" +#: superset/assets/javascripts/explore/stores/visTypes.js:722 +#: superset/assets/javascripts/explore/stores/visTypes.js:750 +msgid "Source / Target" msgstr "" -#: superset/viz.py:1320 -msgid "World Map" +#: superset/assets/javascripts/explore/stores/visTypes.js:723 +#: superset/assets/javascripts/explore/stores/visTypes.js:751 +msgid "Choose a source and a target" msgstr "" -#: superset/viz.py:1370 -msgid "Filters" +#: superset/assets/javascripts/explore/stores/visTypes.js:756 +msgid "Chord Diagram" msgstr "" -#: superset/viz.py:1378 -msgid "Pick at least one filter field" +#: superset/assets/javascripts/explore/stores/visTypes.js:777 +msgid "Choose a number format" msgstr "" -#: superset/viz.py:1405 -msgid "iFrame" +#: superset/assets/javascripts/explore/stores/visTypes.js:780 +msgid "Source" msgstr "" -#: superset/viz.py:1422 -msgid "Parallel Coordinates" +#: superset/assets/javascripts/explore/stores/visTypes.js:783 +msgid "Choose a source" msgstr "" -#: superset/viz.py:1447 -msgid "Heatmap" +#: superset/assets/javascripts/explore/stores/visTypes.js:786 +msgid "Target" msgstr "" -#: superset/viz.py:1498 -msgid "Horizon Charts" +#: superset/assets/javascripts/explore/stores/visTypes.js:789 +msgid "Choose a target" msgstr "" -#: superset/viz.py:1509 -msgid "Mapbox" +#: superset/assets/javascripts/explore/stores/visTypes.js:814 +msgid "ISO 3166-1 codes of region/province/department" msgstr "" -#: superset/viz.py:1524 -msgid "Must have a [Group By] column to have 'count' as the [Label]" +#: superset/assets/javascripts/explore/stores/visTypes.js:815 +msgid "" +"It's ISO 3166-1 of your region/province/department in your table. (see " +"documentation for list of ISO 3166-1)" msgstr "" -#: superset/viz.py:1537 -msgid "Choice of [Label] must be present in [Group By]" +#: superset/assets/javascripts/explore/stores/visTypes.js:849 +msgid "Country Control" msgstr "" -#: superset/viz.py:1542 -msgid "Choice of [Point Radius] must be present in [Group By]" +#: superset/assets/javascripts/explore/stores/visTypes.js:850 +msgid "3 letter code of the country" msgstr "" -#: superset/viz.py:1547 -msgid "[Longitude] and [Latitude] columns must be present in [Group By]" +#: superset/assets/javascripts/explore/stores/visTypes.js:853 +msgid "Metric for color" msgstr "" -#: superset/viz.py:1612 -msgid "Event flow" +#: superset/assets/javascripts/explore/stores/visTypes.js:854 +msgid "Metric that defines the color of the country" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:857 +msgid "Bubble size" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:858 +msgid "Metric that defines the size of the bubble" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:864 +msgid "Filter Box" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:883 +msgid "Filter controls" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:884 +msgid "" +"The controls you want to filter on. Note that only columns checked as " +"\"filterable\" will show up on this list." +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:932 +msgid "Axis & Metrics" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:940 +msgid "Heatmap Options" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:961 +msgid "Horizon" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:987 +msgid "Points" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:994 +msgid "Labelling" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1001 +msgid "Visual Tweaks" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1010 +msgid "Viewport" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1020 +msgid "Longitude" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1021 +msgid "Column containing longitude data" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1024 +msgid "Latitude" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1025 +msgid "Column containing latitude data" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1028 +msgid "Cluster label aggregator" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1029 +msgid "" +"Aggregate function applied to the list of points in each cluster to " +"produce the cluster label." +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1033 +msgid "Tooltip" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1034 +msgid "Show a tooltip when hovering over points and clusters describing the label" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1038 +msgid "" +"One or many controls to group by. If grouping, latitude and longitude " +"columns must be present." +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1049 +msgid "Event definition" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1059 +msgid "Additional meta data" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1067 +msgid "Column containing entity ids" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1068 +msgid "e.g., a \"user id\" column" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1071 +msgid "Column containing event names" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1079 +msgid "Event count limit" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1080 +msgid "The maximum number of events to return, equivalent to number of rows" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1083 +msgid "Meta data" +msgstr "" + +#: superset/assets/javascripts/explore/stores/visTypes.js:1084 +msgid "Select any columns for meta data inspection" +msgstr "" + +#: superset/assets/javascripts/modules/superset.js:134 +msgid "" +"The server could not be reached. You may want to verify your connection " +"and try again." +msgstr "" + +#: superset/assets/javascripts/modules/superset.js:137 +#, python-format +msgid "An unknown error occurred. (Status: %s )" msgstr "" -#: superset/connectors/druid/models.py:975 +#: superset/assets/javascripts/profile/components/App.jsx:24 +msgid "Favorites" +msgstr "" + +#: superset/assets/javascripts/profile/components/App.jsx:30 +msgid "Created Content" +msgstr "" + +#: superset/assets/javascripts/profile/components/App.jsx:37 +msgid "Recent Activity" +msgstr "" + +#: superset/assets/javascripts/profile/components/App.jsx:42 +msgid "Security & Access" +msgstr "" + +#: superset/assets/javascripts/profile/components/CreatedContent.jsx:33 +msgid "No slices" +msgstr "" + +#: superset/assets/javascripts/profile/components/CreatedContent.jsx:49 +msgid "No dashboards" +msgstr "" + +#: superset/assets/javascripts/profile/components/CreatedContent.jsx:58 +#: superset/assets/javascripts/profile/components/Favorites.jsx:59 +#: superset/templates/superset/welcome.html:13 superset/views/core.py:368 +#: superset/views/core.py:528 +msgid "Dashboards" +msgstr "" + +#: superset/assets/javascripts/profile/components/CreatedContent.jsx:61 +#: superset/assets/javascripts/profile/components/Favorites.jsx:62 +#: superset/views/core.py:404 superset/views/core.py:473 +msgid "Slices" +msgstr "" + +#: superset/assets/javascripts/profile/components/Favorites.jsx:34 +msgid "No favorite slices yet, go click on stars!" +msgstr "" + +#: superset/assets/javascripts/profile/components/Favorites.jsx:50 +msgid "No favorite dashboards yet, go click on stars!" +msgstr "" + +#: superset/assets/javascripts/profile/components/Security.jsx:14 +msgid "Roles" +msgstr "" + +#: superset/assets/javascripts/profile/components/Security.jsx:23 +#: superset/views/core.py:280 +msgid "Databases" +msgstr "" + +#: superset/assets/javascripts/profile/components/Security.jsx:34 +msgid "Datasources" +msgstr "" + +#: superset/assets/javascripts/profile/components/UserInfo.jsx:18 +msgid "Profile picture provided by Gravatar" +msgstr "" + +#: superset/assets/javascripts/profile/components/UserInfo.jsx:33 +msgid "joined" +msgstr "" + +#: superset/assets/javascripts/profile/components/UserInfo.jsx:43 +msgid "id:" +msgstr "" + +#: superset/assets/visualizations/EventFlow.jsx:56 +msgid "Sorry, there appears to be no data" +msgstr "" + +#: superset/assets/visualizations/filter_box.jsx:106 +#, python-format +msgid "Select [%s]" +msgstr "" + +#: superset/connectors/druid/models.py:976 msgid "No data was returned." msgstr "" @@ -382,11 +2715,6 @@ msgstr "" msgid "Type" msgstr "" -#: superset/connectors/druid/views.py:45 superset/views/core.py:314 -#: superset/views/core.py:338 superset/views/core.py:369 -msgid "Datasource" -msgstr "" - #: superset/connectors/druid/views.py:46 superset/connectors/sqla/views.py:84 msgid "Groupable" msgstr "" @@ -403,14 +2731,6 @@ msgstr "" msgid "Sum" msgstr "" -#: superset/connectors/druid/views.py:50 superset/connectors/sqla/views.py:89 -msgid "Min" -msgstr "" - -#: superset/connectors/druid/views.py:51 superset/connectors/sqla/views.py:90 -msgid "Max" -msgstr "" - #: superset/connectors/druid/views.py:54 superset/connectors/sqla/views.py:50 msgid "" "Whether this column is exposed in the `Filters` section of the explore " @@ -440,17 +2760,6 @@ msgid "" "metric)' are allowed to access this metric" msgstr "" -#: superset/connectors/druid/views.py:106 superset/connectors/sqla/views.py:131 -msgid "Metric" -msgstr "" - -#: superset/connectors/druid/views.py:107 -#: superset/connectors/druid/views.py:229 superset/connectors/sqla/views.py:83 -#: superset/connectors/sqla/views.py:132 superset/connectors/sqla/views.py:229 -#: superset/views/core.py:370 superset/views/sql_lab.py:56 -msgid "Description" -msgstr "" - #: superset/connectors/druid/views.py:108 superset/connectors/sqla/views.py:82 #: superset/connectors/sqla/views.py:133 msgid "Verbose Name" @@ -481,7 +2790,7 @@ msgid "Edit Druid Cluster" msgstr "" #: superset/connectors/druid/views.py:142 -#: superset/connectors/druid/views.py:228 +#: superset/connectors/druid/views.py:226 msgid "Cluster" msgstr "" @@ -514,8 +2823,8 @@ msgid "Druid Clusters" msgstr "" #: superset/connectors/druid/views.py:166 -#: superset/connectors/druid/views.py:268 -#: superset/connectors/druid/views.py:307 superset/connectors/sqla/views.py:287 +#: superset/connectors/druid/views.py:266 +#: superset/connectors/druid/views.py:305 superset/connectors/sqla/views.py:285 #: superset/views/core.py:283 msgid "Sources" msgstr "" @@ -536,7 +2845,7 @@ msgstr "" msgid "Edit Druid Datasource" msgstr "" -#: superset/connectors/druid/views.py:197 superset/connectors/sqla/views.py:178 +#: superset/connectors/druid/views.py:195 superset/connectors/sqla/views.py:176 msgid "" "The list of slices associated with this table. By altering this " "datasource, you may change how these associated slices behave. Also note " @@ -545,11 +2854,11 @@ msgid "" "datasource for a slice, overwrite the slice from the 'explore view'" msgstr "" -#: superset/connectors/druid/views.py:205 superset/connectors/sqla/views.py:186 +#: superset/connectors/druid/views.py:203 superset/connectors/sqla/views.py:184 msgid "Timezone offset (in hours) for this datasource" msgstr "" -#: superset/connectors/druid/views.py:209 +#: superset/connectors/druid/views.py:207 msgid "" "Time expression to use as a predicate when retrieving distinct values to " "populate the filter component. Only applies when `Enable Filter Select` " @@ -557,71 +2866,71 @@ msgid "" "filter will be populated based on the distinct value over the past week" msgstr "" -#: superset/connectors/druid/views.py:216 superset/connectors/sqla/views.py:208 +#: superset/connectors/druid/views.py:214 superset/connectors/sqla/views.py:206 msgid "" "Whether to populate the filter's dropdown in the explore view's filter " "section with a list of distinct values fetched from the backend on the " "fly" msgstr "" -#: superset/connectors/druid/views.py:220 +#: superset/connectors/druid/views.py:218 msgid "" "Redirects to this endpoint when clicking on the datasource from the " "datasource list" msgstr "" -#: superset/connectors/druid/views.py:226 superset/connectors/sqla/views.py:215 +#: superset/connectors/druid/views.py:224 superset/connectors/sqla/views.py:213 msgid "Associated Slices" msgstr "" -#: superset/connectors/druid/views.py:227 +#: superset/connectors/druid/views.py:225 msgid "Data Source" msgstr "" -#: superset/connectors/druid/views.py:230 superset/connectors/sqla/views.py:227 +#: superset/connectors/druid/views.py:228 superset/connectors/sqla/views.py:225 msgid "Owner" msgstr "" -#: superset/connectors/druid/views.py:231 +#: superset/connectors/druid/views.py:229 msgid "Is Hidden" msgstr "" -#: superset/connectors/druid/views.py:232 superset/connectors/sqla/views.py:220 +#: superset/connectors/druid/views.py:230 superset/connectors/sqla/views.py:218 msgid "Enable Filter Select" msgstr "" -#: superset/connectors/druid/views.py:233 superset/connectors/sqla/views.py:222 +#: superset/connectors/druid/views.py:231 superset/connectors/sqla/views.py:220 msgid "Default Endpoint" msgstr "" -#: superset/connectors/druid/views.py:234 +#: superset/connectors/druid/views.py:232 msgid "Time Offset" msgstr "" -#: superset/connectors/druid/views.py:235 superset/connectors/sqla/views.py:224 +#: superset/connectors/druid/views.py:233 superset/connectors/sqla/views.py:222 #: superset/views/core.py:248 superset/views/core.py:366 msgid "Cache Timeout" msgstr "" -#: superset/connectors/druid/views.py:266 +#: superset/connectors/druid/views.py:264 msgid "Druid Datasources" msgstr "" -#: superset/connectors/druid/views.py:304 +#: superset/connectors/druid/views.py:302 msgid "Refresh Druid Metadata" msgstr "" -#: superset/connectors/sqla/models.py:390 +#: superset/connectors/sqla/models.py:392 msgid "" "Datetime column not provided as part table configuration and is required " "by this type of chart" msgstr "" -#: superset/connectors/sqla/models.py:395 +#: superset/connectors/sqla/models.py:397 msgid "Metric '{}' is not valid" msgstr "" -#: superset/connectors/sqla/models.py:581 +#: superset/connectors/sqla/models.py:585 msgid "" "Table doesn't seem to exist in the specified database, couldn't fetch " "column information" @@ -656,11 +2965,6 @@ msgid "" "most case users should not need to alter this." msgstr "" -#: superset/connectors/sqla/views.py:86 superset/connectors/sqla/views.py:136 -#: superset/connectors/sqla/views.py:216 superset/views/core.py:376 -msgid "Table" -msgstr "" - #: superset/connectors/sqla/views.py:91 msgid "Expression" msgstr "" @@ -721,77 +3025,77 @@ msgstr "" msgid "Edit Table" msgstr "" -#: superset/connectors/sqla/views.py:187 +#: superset/connectors/sqla/views.py:185 msgid "Name of the table that exists in the source database" msgstr "" -#: superset/connectors/sqla/views.py:189 +#: superset/connectors/sqla/views.py:187 msgid "Schema, as used only in some databases like Postgres, Redshift and DB2" msgstr "" -#: superset/connectors/sqla/views.py:195 +#: superset/connectors/sqla/views.py:193 msgid "" "This fields acts a Superset view, meaning that Superset will run a query " "against this string as a subquery." msgstr "" -#: superset/connectors/sqla/views.py:199 +#: superset/connectors/sqla/views.py:197 msgid "" "Predicate applied when fetching distinct value to populate the filter " "control component. Supports jinja template syntax. Applies only when " "`Enable Filter Select` is on." msgstr "" -#: superset/connectors/sqla/views.py:205 +#: superset/connectors/sqla/views.py:203 msgid "Redirects to this endpoint when clicking on the table from the table list" msgstr "" -#: superset/connectors/sqla/views.py:217 +#: superset/connectors/sqla/views.py:215 msgid "Changed By" msgstr "" -#: superset/connectors/sqla/views.py:218 superset/views/core.py:244 +#: superset/connectors/sqla/views.py:216 superset/views/core.py:244 #: superset/views/sql_lab.py:19 superset/views/sql_lab.py:55 msgid "Database" msgstr "" -#: superset/connectors/sqla/views.py:219 superset/views/core.py:246 +#: superset/connectors/sqla/views.py:217 superset/views/core.py:246 msgid "Last Changed" msgstr "" -#: superset/connectors/sqla/views.py:221 +#: superset/connectors/sqla/views.py:219 msgid "Schema" msgstr "" -#: superset/connectors/sqla/views.py:223 +#: superset/connectors/sqla/views.py:221 msgid "Offset" msgstr "" -#: superset/connectors/sqla/views.py:225 +#: superset/connectors/sqla/views.py:223 msgid "Table Name" msgstr "" -#: superset/connectors/sqla/views.py:226 +#: superset/connectors/sqla/views.py:224 msgid "Fetch Values Predicate" msgstr "" -#: superset/connectors/sqla/views.py:228 +#: superset/connectors/sqla/views.py:226 msgid "Main Datetime Column" msgstr "" -#: superset/connectors/sqla/views.py:248 +#: superset/connectors/sqla/views.py:246 msgid "" "Table [{}] could not be found, please double check your database " "connection, schema, and table name" msgstr "" -#: superset/connectors/sqla/views.py:261 +#: superset/connectors/sqla/views.py:259 msgid "" "The table was created. As part of this two phase configuration process, " "you should now click the edit button by the new table to configure it." msgstr "" -#: superset/connectors/sqla/views.py:285 +#: superset/connectors/sqla/views.py:283 msgid "Tables" msgstr "" @@ -815,10 +3119,6 @@ msgstr "" msgid "No records found" msgstr "" -#: superset/templates/appbuilder/general/widgets/search.html:6 -msgid "Add Filter" -msgstr "" - #: superset/templates/superset/import_dashboards.html:11 msgid "Import" msgstr "" @@ -836,37 +3136,28 @@ msgstr "" msgid "Request Permissions" msgstr "" -#: superset/templates/superset/request_access.html:16 -msgid "Cancel" -msgstr "" - #: superset/templates/superset/welcome.html:3 msgid "Welcome!" msgstr "" -#: superset/templates/superset/welcome.html:13 superset/views/core.py:368 -#: superset/views/core.py:528 -msgid "Dashboards" -msgstr "" - #: superset/templates/superset/models/database/macros.html:4 msgid "Test Connection" msgstr "" -#: superset/views/base.py:58 +#: superset/views/base.py:62 #, python-format msgid "Datasource %(name)s already exists" msgstr "" -#: superset/views/base.py:206 +#: superset/views/base.py:221 msgid "json isn't valid" msgstr "" -#: superset/views/base.py:257 +#: superset/views/base.py:272 msgid "Delete" msgstr "" -#: superset/views/base.py:258 +#: superset/views/base.py:273 msgid "Delete all Really?" msgstr "" @@ -994,15 +3285,11 @@ msgstr "" msgid "Import Dashboards" msgstr "" -#: superset/views/core.py:273 superset/views/core.py:2330 +#: superset/views/core.py:273 superset/views/core.py:2334 #: superset/views/sql_lab.py:30 msgid "Manage" msgstr "" -#: superset/views/core.py:280 -msgid "Databases" -msgstr "" - #: superset/views/core.py:311 superset/views/core.py:552 #: superset/views/sql_lab.py:18 superset/views/sql_lab.py:54 msgid "User" @@ -1076,18 +3363,6 @@ msgstr "" msgid "Slice" msgstr "" -#: superset/views/core.py:375 -msgid "Name" -msgstr "" - -#: superset/views/core.py:377 -msgid "Visualization Type" -msgstr "" - -#: superset/views/core.py:404 superset/views/core.py:473 -msgid "Slices" -msgstr "" - #: superset/views/core.py:433 msgid "List Dashboards" msgstr "" @@ -1136,27 +3411,14 @@ msgstr "" msgid "Dashboard" msgstr "" -#: superset/views/core.py:471 superset/views/core.py:538 -msgid "Title" -msgstr "" - #: superset/views/core.py:472 msgid "Slug" msgstr "" -#: superset/views/core.py:476 superset/views/core.py:540 -#: superset/views/sql_lab.py:57 -msgid "Modified" -msgstr "" - #: superset/views/core.py:477 msgid "Position JSON" msgstr "" -#: superset/views/core.py:478 -msgid "CSS" -msgstr "" - #: superset/views/core.py:479 msgid "JSON Metadata" msgstr "" @@ -1205,53 +3467,53 @@ msgstr "" msgid "You have no permission to approve this request" msgstr "" -#: superset/views/core.py:1618 +#: superset/views/core.py:1625 msgid "" "Malformed request. slice_id or table_name and db_name arguments are " "expected" msgstr "" -#: superset/views/core.py:1624 +#: superset/views/core.py:1631 #, python-format msgid "Slice %(id)s not found" msgstr "" -#: superset/views/core.py:1636 +#: superset/views/core.py:1643 #, python-format msgid "Table %(t)s wasn't found in the database %(d)s" msgstr "" -#: superset/views/core.py:1774 +#: superset/views/core.py:1782 #, python-format msgid "Can't find User '%(name)s', please ask your admin to create one." msgstr "" -#: superset/views/core.py:1781 +#: superset/views/core.py:1789 #, python-format msgid "Can't find DruidCluster with cluster_name = '%(name)s'" msgstr "" -#: superset/views/core.py:2042 +#: superset/views/core.py:2050 msgid "Query record was not created as expected." msgstr "" -#: superset/views/core.py:2316 +#: superset/views/core.py:2320 msgid "Template Name" msgstr "" -#: superset/views/core.py:2327 +#: superset/views/core.py:2331 msgid "CSS Templates" msgstr "" -#: superset/views/core.py:2337 +#: superset/views/core.py:2341 msgid "SQL Editor" msgstr "" -#: superset/views/core.py:2342 superset/views/core.py:2351 +#: superset/views/core.py:2346 superset/views/core.py:2355 msgid "SQL Lab" msgstr "" -#: superset/views/core.py:2346 +#: superset/views/core.py:2350 msgid "Query Search" msgstr "" @@ -1287,10 +3549,6 @@ msgstr "" msgid "Edit Saved Query" msgstr "" -#: superset/views/sql_lab.py:53 -msgid "Label" -msgstr "" - #: superset/views/sql_lab.py:59 msgid "Pop Tab Link" msgstr "" diff --git a/superset/assets/javascripts/SqlLab/actions.js b/superset/assets/javascripts/SqlLab/actions.js index 517c5d28d933d..6438226e74a76 100644 --- a/superset/assets/javascripts/SqlLab/actions.js +++ b/superset/assets/javascripts/SqlLab/actions.js @@ -1,6 +1,7 @@ /* global notify */ import shortid from 'shortid'; import { now } from '../modules/dates'; +import { t } from '../locales'; const $ = require('jquery'); @@ -53,8 +54,8 @@ export function saveQuery(query) { type: 'POST', url, data: query, - success: () => notify.success('Your query was saved'), - error: () => notify.error('Your query could not be saved'), + success: () => notify.success(t('Your query was saved')), + error: () => notify.error(t('Your query could not be saved')), dataType: 'json', }); return { type: SAVE_QUERY }; @@ -107,7 +108,7 @@ export function fetchQueryResults(query) { dispatch(querySuccess(query, results)); }, error(err) { - let msg = 'Failed at retrieving results from the results backend'; + let msg = t('Failed at retrieving results from the results backend'); if (err.responseJSON && err.responseJSON.error) { msg = err.responseJSON.error; } @@ -153,12 +154,12 @@ export function runQuery(query) { } } if (textStatus === 'error' && errorThrown === '') { - msg = 'Could not connect to server'; + msg = t('Could not connect to server'); } else if (msg === null) { msg = `[${textStatus}] ${errorThrown}`; } if (msg.indexOf('CSRF token') > 0) { - msg = 'Your session timed out, please refresh your page and try again.'; + msg = t('Your session timed out, please refresh your page and try again.'); } dispatch(queryFailed(query, msg)); }, @@ -177,10 +178,10 @@ export function postStopQuery(query) { url: stopQueryUrl, data: stopQueryRequestData, success() { - notify.success('Query was stopped.'); + notify.success(t('Query was stopped.')); }, error() { - notify.error('Failed at stopping query.'); + notify.error(t('Failed at stopping query.')); }, }); }; @@ -293,7 +294,7 @@ export function addTable(query, tableName, schemaName) { isMetadataLoading: false, }); dispatch(mergeTable(newTable)); - notify.error('Error occurred while fetching table metadata'); + notify.error(t('Error occurred while fetching table metadata')); }); url = `/superset/extra_table_metadata/${query.dbId}/${tableName}/${schemaName}/`; @@ -306,7 +307,7 @@ export function addTable(query, tableName, schemaName) { isExtraMetadataLoading: false, }); dispatch(mergeTable(newTable)); - notify.error('Error occurred while fetching table metadata'); + notify.error(t('Error occurred while fetching table metadata')); }); }; } @@ -360,7 +361,7 @@ export function popStoredQuery(urlId) { success: (data) => { const newQuery = JSON.parse(data); const queryEditorProps = { - title: newQuery.title ? newQuery.title : 'shared query', + title: newQuery.title ? newQuery.title : t('shared query'), dbId: newQuery.dbId ? parseInt(newQuery.dbId, 10) : null, schema: newQuery.schema ? newQuery.schema : null, autorun: newQuery.autorun ? newQuery.autorun : false, @@ -368,7 +369,7 @@ export function popStoredQuery(urlId) { }; dispatch(addQueryEditor(queryEditorProps)); }, - error: () => notify.error("The query couldn't be loaded"), + error: () => notify.error(t('The query couldn\'t be loaded')), }); }; } @@ -388,7 +389,7 @@ export function popSavedQuery(saveQueryId) { }; dispatch(addQueryEditor(queryEditorProps)); }, - error: () => notify.error("The query couldn't be loaded"), + error: () => notify.error(t('The query couldn\'t be loaded')), }); }; } @@ -421,7 +422,7 @@ export function createDatasource(vizOptions, context) { dispatch(createDatasourceSuccess(resp)); }, error: () => { - dispatch(createDatasourceFailed('An error occurred while creating the data source')); + dispatch(createDatasourceFailed(t('An error occurred while creating the data source'))); }, }); }; diff --git a/superset/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx b/superset/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx index 5491eed545b3e..66e60c9b2b4f7 100644 --- a/superset/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx +++ b/superset/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import CopyToClipboard from '../../components/CopyToClipboard'; import { storeQuery } from '../../../utils/common'; +import { t } from '../../locales'; const propTypes = { queryEditor: PropTypes.object.isRequired, @@ -26,10 +27,10 @@ export default class CopyQueryTabUrl extends React.PureComponent { inMenu copyNode={(
- share query + {t('share query')}
)} - tooltipText="copy URL to clipboard" + tooltipText={t('copy URL to clipboard')} shouldShowText={false} getText={this.getUrl.bind(this)} /> diff --git a/superset/assets/javascripts/SqlLab/components/HighlightedSql.jsx b/superset/assets/javascripts/SqlLab/components/HighlightedSql.jsx index f7d69416de4d4..3efab4c9c341a 100644 --- a/superset/assets/javascripts/SqlLab/components/HighlightedSql.jsx +++ b/superset/assets/javascripts/SqlLab/components/HighlightedSql.jsx @@ -6,6 +6,7 @@ import sql from 'react-syntax-highlighter/dist/languages/sql'; import github from 'react-syntax-highlighter/dist/styles/github'; import ModalTrigger from '../../components/ModalTrigger'; +import { t } from '../../locales'; registerLanguage('sql', sql); @@ -57,7 +58,7 @@ class HighlightedSql extends React.Component { if (this.props.rawSql && this.props.rawSql !== this.props.sql) { rawSql = (
-

Raw SQL

+

{t('Raw SQL')}

{this.props.rawSql} @@ -67,7 +68,7 @@ class HighlightedSql extends React.Component { this.setState({ modalBody: (
-

Source SQL

+

{t('Source SQL')}

{this.props.sql} @@ -79,7 +80,7 @@ class HighlightedSql extends React.Component { render() { return ( { } return ( - No query history yet... + {t('No query history yet...')} ); }; diff --git a/superset/assets/javascripts/SqlLab/components/QuerySearch.jsx b/superset/assets/javascripts/SqlLab/components/QuerySearch.jsx index 5101f09f8b723..10c1c88e79c72 100644 --- a/superset/assets/javascripts/SqlLab/components/QuerySearch.jsx +++ b/superset/assets/javascripts/SqlLab/components/QuerySearch.jsx @@ -7,6 +7,7 @@ import { now, epochTimeXHoursAgo, epochTimeXDaysAgo, epochTimeXYearsAgo } from '../../modules/dates'; import { STATUS_OPTIONS, TIME_OPTIONS } from '../constants'; import AsyncSelect from '../../components/AsyncSelect'; +import { t } from '../../locales'; const $ = window.$ = require('jquery'); @@ -102,7 +103,7 @@ class QuerySearch extends React.PureComponent { if (data.result.length === 0) { this.props.actions.addAlert({ bsStyle: 'danger', - msg: "It seems you don't have access to any database", + msg: t('It seems you don\'t have access to any database'), }); } return options; @@ -150,15 +151,15 @@ class QuerySearch extends React.PureComponent { type="text" onChange={this.changeSearch.bind(this)} className="form-control input-sm" - placeholder="Search Results" + placeholder={t('Search Results')} />
({ value: t, label: t }))} + placeholder={t('[To]-')} + options={TIME_OPTIONS.map(xt => ({ value: xt, label: xt }))} value={this.state.to} autosize={false} onChange={this.changeTo.bind(this)} @@ -175,7 +176,7 @@ class QuerySearch extends React.PureComponent { (
- Schema: {o.label} + {t('Schema:')} {o.label}
)} isLoading={this.state.schemaLoading} @@ -186,7 +187,7 @@ class SqlEditorLeftBar extends React.PureComponent { ref="selectTable" isLoading={this.state.tableLoading} value={this.state.tableName} - placeholder={`Add a table (${this.state.tableOptions.length})`} + placeholder={t('Add a table (%s)', this.state.tableOptions.length)} autosize={false} onChange={this.changeTable.bind(this)} filterOptions={this.state.filterOptions} @@ -199,7 +200,7 @@ class SqlEditorLeftBar extends React.PureComponent { name="async-select-table" ref="selectTable" value={this.state.tableName} - placeholder={'Type to search ...'} + placeholder={t('Type to search ...')} autosize={false} onChange={this.changeTable.bind(this)} loadOptions={this.getTableNamesBySubStr.bind(this)} @@ -222,7 +223,7 @@ class SqlEditorLeftBar extends React.PureComponent {
{shouldShowReset && }
diff --git a/superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx b/superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx index 73ba6bcd29c6f..4f716d9a71ab3 100644 --- a/superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx +++ b/superset/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx @@ -9,6 +9,7 @@ import * as Actions from '../actions'; import SqlEditor from './SqlEditor'; import CopyQueryTabUrl from './CopyQueryTabUrl'; import { areArraysShallowEqual } from '../../reduxUtils'; +import { t } from '../../locales'; const propTypes = { actions: PropTypes.object.isRequired, @@ -101,7 +102,7 @@ class TabbedSqlEditors extends React.PureComponent { } renameTab(qe) { /* eslint no-alert: 0 */ - const newTitle = prompt('Enter a new title for the tab'); + const newTitle = prompt(t('Enter a new title for the tab')); if (newTitle) { this.props.actions.queryEditorSetTitle(qe, newTitle); } @@ -120,7 +121,7 @@ class TabbedSqlEditors extends React.PureComponent { queryCount++; const activeQueryEditor = this.activeQueryEditor(); const qe = { - title: `Untitled Query ${queryCount}`, + title: t('Untitled Query %s', queryCount), dbId: (activeQueryEditor && activeQueryEditor.dbId) ? activeQueryEditor.dbId : this.props.defaultDbId, @@ -166,10 +167,10 @@ class TabbedSqlEditors extends React.PureComponent { title="" > - close tab + {t('close tab')} - rename tab + {t('rename tab')} {qe && @@ -177,7 +178,7 @@ class TabbedSqlEditors extends React.PureComponent {   - {this.state.hideLeftBar ? 'expand tool bar' : 'hide tool bar'} + {this.state.hideLeftBar ? t('expand tool bar') : t('hide tool bar')} @@ -193,7 +194,7 @@ class TabbedSqlEditors extends React.PureComponent { {isSelected && (t.queryEditorId === qe.id))} + tables={this.props.tables.filter(xt => (xt.queryEditorId === qe.id))} queryEditor={qe} editorQueries={this.state.queriesArray} dataPreviewQueries={this.state.dataPreviewQueries} diff --git a/superset/assets/javascripts/SqlLab/components/TableElement.jsx b/superset/assets/javascripts/SqlLab/components/TableElement.jsx index fc8ae0c669991..624a0ed1c7650 100644 --- a/superset/assets/javascripts/SqlLab/components/TableElement.jsx +++ b/superset/assets/javascripts/SqlLab/components/TableElement.jsx @@ -9,6 +9,7 @@ import Link from './Link'; import ColumnElement from './ColumnElement'; import ModalTrigger from '../../components/ModalTrigger'; import Loading from '../../components/Loading'; +import { t } from '../../locales'; const propTypes = { table: PropTypes.object, @@ -71,7 +72,7 @@ class TableElement extends React.PureComponent { let partitionClipBoard; if (table.partitions.partitionQuery) { partitionQuery = table.partitions.partitionQuery; - const tt = 'Copy partition query to clipboard'; + const tt = t('Copy partition query to clipboard'); partitionClipBoard = (
- latest partition: {latest} + {t('latest partition:')} {latest} {partitionClipBoard}
@@ -106,7 +107,7 @@ class TableElement extends React.PureComponent { - Keys for table {table.name} + {t('Keys for table')} {table.name} } modalBody={table.indexes.map((ix, i) => ( @@ -115,7 +116,7 @@ class TableElement extends React.PureComponent { triggerNode={ } /> @@ -131,8 +132,8 @@ class TableElement extends React.PureComponent { onClick={this.toggleSortColumns.bind(this)} tooltip={ !this.state.sortColumns ? - 'Sort columns alphabetically' : - 'Original table column order'} + t('Sort columns alphabetically') : + t('Original table column order')} href="#" /> {table.selectStar && @@ -142,13 +143,13 @@ class TableElement extends React.PureComponent { } text={table.selectStar} shouldShowText={false} - tooltipText="Copy SELECT statement to clipboard" + tooltipText={t('Copy SELECT statement to clipboard')} /> } diff --git a/superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx b/superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx index ff9119a84ecf4..965718df78884 100644 --- a/superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx +++ b/superset/assets/javascripts/SqlLab/components/VisualizeModal.jsx @@ -13,6 +13,7 @@ import { getExploreUrl } from '../../explore/exploreUtils'; import * as actions from '../actions'; import { VISUALIZE_VALIDATION_ERRORS } from '../constants'; import visTypes from '../../explore/stores/visTypes'; +import { t } from '../../locales'; const CHART_TYPES = Object.keys(visTypes) .filter(typeName => !!visTypes[typeName].showOnExplore) @@ -86,9 +87,9 @@ class VisualizeModal extends React.PureComponent { if (!re.test(colName)) { hints.push(
- "{colName}" is not right as a column name, please alias it - (as in SELECT count(*) AS my_alias) using only - alphanumeric characters and underscores + {t('%s is not right as a column name, please alias it ' + + '(as in SELECT count(*) ', colName)} {t('AS my_alias')}) {t('using only ' + + 'alphanumeric characters and underscores')}
); } }); @@ -162,7 +163,7 @@ class VisualizeModal extends React.PureComponent { if (mainGroupBy) { formData.groupby = [mainGroupBy.name]; } - notify.info('Creating a data source and popping a new tab'); + notify.info(t('Creating a data source and popping a new tab')); window.open(getExploreUrl(formData)); }) @@ -192,7 +193,7 @@ class VisualizeModal extends React.PureComponent {
- No results available for this query + {t('No results available for this query')}
@@ -237,17 +238,17 @@ class VisualizeModal extends React.PureComponent {
- Visualize + {t('Visualize')} {alerts} {this.buildVisualizeAdvise()}
- Chart Type + {t('Chart Type')} @@ -276,7 +277,7 @@ class VisualizeModal extends React.PureComponent { bsStyle="primary" disabled={(this.state.hints.length > 0)} > - Visualize + {t('Visualize')} diff --git a/superset/assets/javascripts/SqlLab/constants.js b/superset/assets/javascripts/SqlLab/constants.js index 6d678067cc7a1..6af44e4651b2e 100644 --- a/superset/assets/javascripts/SqlLab/constants.js +++ b/superset/assets/javascripts/SqlLab/constants.js @@ -1,3 +1,5 @@ +import { t } from '../locales'; + export const STATE_BSSTYLE_MAP = { failed: 'danger', pending: 'info', @@ -25,8 +27,8 @@ export const TIME_OPTIONS = [ ]; export const VISUALIZE_VALIDATION_ERRORS = { - REQUIRE_CHART_TYPE: 'Pick a chart type!', - REQUIRE_TIME: 'To use this chart type you need at least one column flagged as a date', - REQUIRE_DIMENSION: 'To use this chart type you need at least one dimension', - REQUIRE_AGGREGATION_FUNCTION: 'To use this chart type you need at least one aggregation function', + REQUIRE_CHART_TYPE: t('Pick a chart type!'), + REQUIRE_TIME: t('To use this chart type you need at least one column flagged as a date'), + REQUIRE_DIMENSION: t('To use this chart type you need at least one dimension'), + REQUIRE_AGGREGATION_FUNCTION: t('To use this chart type you need at least one aggregation function'), }; diff --git a/superset/assets/javascripts/SqlLab/reducers.js b/superset/assets/javascripts/SqlLab/reducers.js index c85d27d240b50..3a49bd1b881cc 100644 --- a/superset/assets/javascripts/SqlLab/reducers.js +++ b/superset/assets/javascripts/SqlLab/reducers.js @@ -3,11 +3,12 @@ import * as actions from './actions'; import { now } from '../modules/dates'; import { addToObject, alterInObject, alterInArr, removeFromArr, getFromArr, addToArr } from '../reduxUtils'; +import { t } from '../locales'; export function getInitialState(defaultDbId) { const defaultQueryEditor = { id: shortid.generate(), - title: 'Untitled Query', + title: t('Untitled Query'), sql: 'SELECT *\nFROM\nWHERE', selectedText: null, latestQueryId: null, @@ -40,7 +41,7 @@ export const sqlLabReducer = function (state, action) { qe.id === state.tabHistory[state.tabHistory.length - 1]); const qe = { id: shortid.generate(), - title: `Copy of ${progenitor.title}`, + title: t('Copy of %s', progenitor.title), dbId: (action.query.dbId) ? action.query.dbId : null, schema: (action.query.schema) ? action.query.schema : null, autorun: true, @@ -76,13 +77,13 @@ export const sqlLabReducer = function (state, action) { [actions.MERGE_TABLE]() { const at = Object.assign({}, action.table); let existingTable; - state.tables.forEach((t) => { + state.tables.forEach((xt) => { if ( - t.dbId === at.dbId && - t.queryEditorId === at.queryEditorId && - t.schema === at.schema && - t.name === at.name) { - existingTable = t; + xt.dbId === at.dbId && + xt.queryEditorId === at.queryEditorId && + xt.schema === at.schema && + xt.name === at.name) { + existingTable = xt; } }); if (existingTable) { @@ -115,11 +116,11 @@ export const sqlLabReducer = function (state, action) { delete queries[action.oldQueryId]; const newTables = []; - state.tables.forEach((t) => { - if (t.dataPreviewQueryId === action.oldQueryId) { - newTables.push(Object.assign({}, t, { dataPreviewQueryId: action.newQuery.id })); + state.tables.forEach((xt) => { + if (xt.dataPreviewQueryId === action.oldQueryId) { + newTables.push(Object.assign({}, xt, { dataPreviewQueryId: action.newQuery.id })); } else { - newTables.push(t); + newTables.push(xt); } }); return Object.assign( diff --git a/superset/assets/javascripts/addSlice/AddSliceContainer.jsx b/superset/assets/javascripts/addSlice/AddSliceContainer.jsx index c316e2c76ed09..f0fc1218622b2 100644 --- a/superset/assets/javascripts/addSlice/AddSliceContainer.jsx +++ b/superset/assets/javascripts/addSlice/AddSliceContainer.jsx @@ -50,30 +50,30 @@ export default class AddSliceContainer extends React.PureComponent { render() { return (
- Create a new slice}> + {('Create a new slice')}}>
-

Choose a datasource

+

{('Choose a datasource')}

@@ -83,7 +83,7 @@ export default class AddSliceContainer extends React.PureComponent { disabled={this.isBtnDisabled()} onClick={this.gotoSlice.bind(this)} > - Create new slice + {('Create new slice')}

diff --git a/superset/assets/javascripts/common.js b/superset/assets/javascripts/common.js index 8ecfbe5822137..d84f064065e82 100644 --- a/superset/assets/javascripts/common.js +++ b/superset/assets/javascripts/common.js @@ -10,6 +10,17 @@ $(document).ready(function () { const id = $this.attr('id'); utils.toggleCheckbox(prefix, '#' + id); }); + + // for language picker dropdown + $('#language-picker a').click(function (ev) { + ev.preventDefault(); + + const targetUrl = ev.currentTarget.href; + $.ajax(targetUrl) + .then(() => { + location.reload(); + }); + }); }); export function appSetup() { diff --git a/superset/assets/javascripts/components/AsyncSelect.jsx b/superset/assets/javascripts/components/AsyncSelect.jsx index e045dc973c987..007281a116a04 100644 --- a/superset/assets/javascripts/components/AsyncSelect.jsx +++ b/superset/assets/javascripts/components/AsyncSelect.jsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Select from 'react-select'; +import { t } from '../locales'; const $ = window.$ = require('jquery'); @@ -16,7 +17,7 @@ const propTypes = { }; const defaultProps = { - placeholder: 'Select ...', + placeholder: t('Select ...'), valueRenderer: o => (
{o.label}
), onAsyncError: () => {}, }; diff --git a/superset/assets/javascripts/components/CachedLabel.jsx b/superset/assets/javascripts/components/CachedLabel.jsx index e78e4f7996a81..8d0c0f2f2a740 100644 --- a/superset/assets/javascripts/components/CachedLabel.jsx +++ b/superset/assets/javascripts/components/CachedLabel.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { Label } from 'react-bootstrap'; import moment from 'moment'; import TooltipWrapper from './TooltipWrapper'; +import { t } from '../locales'; const propTypes = { onClick: PropTypes.func, @@ -22,14 +23,14 @@ class CacheLabel extends React.PureComponent { updateTooltipContent() { const cachedText = this.props.cachedTimestamp ? ( - Loaded data cached {moment.utc(this.props.cachedTimestamp).fromNow()} + t('Loaded data cached') {moment.utc(this.props.cachedTimestamp).fromNow()} ) : - 'Loaded from cache'; + t('Loaded from cache'); const tooltipContent = ( {cachedText}. - Click to force-refresh + {t('Click to force-refresh')} ); this.setState({ tooltipContent }); diff --git a/superset/assets/javascripts/components/CopyToClipboard.jsx b/superset/assets/javascripts/components/CopyToClipboard.jsx index c9120b2b3ef28..d00347d998987 100644 --- a/superset/assets/javascripts/components/CopyToClipboard.jsx +++ b/superset/assets/javascripts/components/CopyToClipboard.jsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Tooltip, OverlayTrigger, MenuItem } from 'react-bootstrap'; +import { t } from '../locales'; const propTypes = { copyNode: PropTypes.node, @@ -17,7 +18,7 @@ const defaultProps = { onCopyEnd: () => {}, shouldShowText: true, inMenu: false, - tooltipText: 'Copy to clipboard', + tooltipText: t('Copy to clipboard'), }; export default class CopyToClipboard extends React.Component { @@ -61,10 +62,10 @@ export default class CopyToClipboard extends React.Component { textArea.select(); try { if (!document.execCommand('copy')) { - throw new Error('Not successful'); + throw new Error(t('Not successful')); } } catch (err) { - window.alert('Sorry, your browser does not support copying. Use Ctrl / Cmd + C!'); // eslint-disable-line + window.alert(t('Sorry, your browser does not support copying. Use Ctrl / Cmd + C!')); // eslint-disable-line } document.body.removeChild(textArea); @@ -75,7 +76,7 @@ export default class CopyToClipboard extends React.Component { tooltipText() { if (this.state.hasCopied) { - return 'Copied!'; + return t('Copied!'); } return this.props.tooltipText; } diff --git a/superset/assets/javascripts/components/EditableTitle.jsx b/superset/assets/javascripts/components/EditableTitle.jsx index 9d71388828ad2..26f474420d0ae 100644 --- a/superset/assets/javascripts/components/EditableTitle.jsx +++ b/superset/assets/javascripts/components/EditableTitle.jsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import TooltipWrapper from './TooltipWrapper'; +import { t } from '../locales'; const propTypes = { title: PropTypes.string, @@ -8,7 +9,7 @@ const propTypes = { onSaveTitle: PropTypes.func.isRequired, }; const defaultProps = { - title: 'Title', + title: t('Title'), canEdit: false, }; @@ -71,7 +72,7 @@ class EditableTitle extends React.PureComponent { - You have unsaved changes. Click the  + {t('You have unsaved changes.')} {t('Click the')}     - button on the top right to save your changes. + {t('button on the top right to save your changes.')}
, document.getElementById('alert-container'), @@ -161,13 +161,12 @@ export function dashboardContainer(dashboard, datasources, userid) { .addClass('danger') .attr( 'title', - `Served from data cached ${cachedWhen}. ` + - 'Click to force refresh') + t('Served from data cached %s . Click to force refresh.', cachedWhen)) .tooltip('fixTitle'); } else { refresh .removeClass('danger') - .attr('title', 'Click to force refresh') + .attr('title', t('Click to force refresh')) .tooltip('fixTitle'); } }, @@ -351,8 +350,8 @@ export function dashboardContainer(dashboard, datasources, userid) { error(error) { const errorMsg = getAjaxErrorMsg(error); utils.showModal({ - title: 'Error', - body: 'Sorry, there was an error adding slices to this dashboard: ' + errorMsg, + title: t('Error'), + body: t('Sorry, there was an error adding slices to this dashboard: %s', errorMsg), }); }, }); diff --git a/superset/assets/javascripts/dashboard/components/CodeModal.jsx b/superset/assets/javascripts/dashboard/components/CodeModal.jsx index 77f2dafea08a0..f9c1535a7708a 100644 --- a/superset/assets/javascripts/dashboard/components/CodeModal.jsx +++ b/superset/assets/javascripts/dashboard/components/CodeModal.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import ModalTrigger from '../../components/ModalTrigger'; +import { t } from '../../locales'; const propTypes = { triggerNode: PropTypes.node.isRequired, @@ -31,7 +32,7 @@ export default class CodeModal extends React.PureComponent { triggerNode={this.props.triggerNode} isButton beforeOpen={this.beforeOpen.bind(this)} - modalTitle="Active Dashboard Filters" + modalTitle={t('Active Dashboard Filters')} modalBody={
diff --git a/superset/assets/javascripts/dashboard/components/Controls.jsx b/superset/assets/javascripts/dashboard/components/Controls.jsx
index 1169642ff60e5..e18c2701fee9b 100644
--- a/superset/assets/javascripts/dashboard/components/Controls.jsx
+++ b/superset/assets/javascripts/dashboard/components/Controls.jsx
@@ -8,6 +8,7 @@ import RefreshIntervalModal from './RefreshIntervalModal';
 import SaveModal from './SaveModal';
 import CodeModal from './CodeModal';
 import SliceAdder from './SliceAdder';
+import { t } from '../../locales';
 
 const $ = window.$ = require('jquery');
 
@@ -44,13 +45,13 @@ class Controls extends React.PureComponent {
   }
   render() {
     const dashboard = this.props.dashboard;
-    const emailBody = `Checkout this dashboard: ${window.location.href}`;
+    const emailBody = t('Checkout this dashboard: %s', window.location.href);
     const emailLink = 'mailto:?Subject=Superset%20Dashboard%20'
       + `${dashboard.dashboard_title}&Body=${emailBody}`;
     return (
       
         
diff --git a/superset/assets/javascripts/dashboard/components/CssEditor.jsx b/superset/assets/javascripts/dashboard/components/CssEditor.jsx
index b77ab9d0ff5e6..bbcc19f078604 100644
--- a/superset/assets/javascripts/dashboard/components/CssEditor.jsx
+++ b/superset/assets/javascripts/dashboard/components/CssEditor.jsx
@@ -7,6 +7,7 @@ import 'brace/mode/css';
 import 'brace/theme/github';
 
 import ModalTrigger from '../../components/ModalTrigger';
+import { t } from '../../locales';
 
 const propTypes = {
   initialCss: PropTypes.string,
@@ -61,10 +62,10 @@ class CssEditor extends React.PureComponent {
     if (this.props.templates) {
       return (
         
-
Load a template
+
{t('Load a template')}
' + errorMsg); + notify.error(t('Sorry, there was an error saving this dashboard: ') + '' + errorMsg); }, }); } @@ -96,8 +97,8 @@ class SaveModal extends React.PureComponent { if (!newDashboardTitle) { this.modal.close(); showModal({ - title: 'Error', - body: 'You must pick a name for the new dashboard', + title: t('Error'), + body: t('You must pick a name for the new dashboard'), }); } else { data.dashboard_title = newDashboardTitle; @@ -111,7 +112,7 @@ class SaveModal extends React.PureComponent { { this.modal = modal; }} triggerNode={this.props.triggerNode} - modalTitle="Save Dashboard" + modalTitle={t('Save Dashboard')} modalBody={ - Overwrite Dashboard [{this.props.dashboard.dashboard_title}] + {t('Overwrite Dashboard [%s]', this.props.dashboard.dashboard_title)}
- Save as: + {t('Save as:')} { this.saveDashboard(this.state.saveType, this.state.newDashName); }} > - Save + {t('Save')}
} diff --git a/superset/assets/javascripts/dashboard/components/SliceAdder.jsx b/superset/assets/javascripts/dashboard/components/SliceAdder.jsx index 9d8965cce42c6..4c5f462a02ba9 100644 --- a/superset/assets/javascripts/dashboard/components/SliceAdder.jsx +++ b/superset/assets/javascripts/dashboard/components/SliceAdder.jsx @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table'; import ModalTrigger from '../../components/ModalTrigger'; +import { t } from '../../locales'; require('react-bootstrap-table/css/react-bootstrap-table.css'); @@ -138,13 +139,13 @@ class SliceAdder extends React.Component { dataField="sliceName" dataSort > - Name + {t('Name')} - Viz + {t('Viz')} modified} > - Modified + {t('Modified')}
@@ -172,12 +173,12 @@ class SliceAdder extends React.Component { return ( ); } diff --git a/superset/assets/javascripts/dashboard/components/SliceCell.jsx b/superset/assets/javascripts/dashboard/components/SliceCell.jsx index 0a179034825bf..3cd1334dc0db6 100644 --- a/superset/assets/javascripts/dashboard/components/SliceCell.jsx +++ b/superset/assets/javascripts/dashboard/components/SliceCell.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { t } from '../../locales'; import { getExploreUrl } from '../../explore/exploreUtils'; const propTypes = { @@ -20,14 +21,14 @@ function SliceCell({ expandedSlices, removeSlice, slice }) {
@@ -72,7 +73,7 @@ export default class EmbedCodeButton extends React.Component {
- +
- + @@ -38,7 +39,7 @@ export default function ExploreActionButtons({ diff --git a/superset/assets/javascripts/explore/components/SaveModal.jsx b/superset/assets/javascripts/explore/components/SaveModal.jsx index 45f5ac138ff2e..ac76e3a5ad68e 100644 --- a/superset/assets/javascripts/explore/components/SaveModal.jsx +++ b/superset/assets/javascripts/explore/components/SaveModal.jsx @@ -6,6 +6,7 @@ import { connect } from 'react-redux'; import { Modal, Alert, Button, Radio } from 'react-bootstrap'; import Select from 'react-select'; import { getExploreUrl } from '../exploreUtils'; +import { t } from '../../locales'; const propTypes = { can_overwrite: PropTypes.bool, @@ -70,7 +71,7 @@ class SaveModal extends React.Component { if (sliceParams.action === 'saveas') { sliceName = this.state.newSliceName; if (sliceName === '') { - this.setState({ alert: 'Please enter a slice name' }); + this.setState({ alert: t('Please enter a slice name') }); return; } sliceParams.slice_name = sliceName; @@ -85,7 +86,7 @@ class SaveModal extends React.Component { case ('existing'): dashboard = this.state.saveToDashboardId; if (!dashboard) { - this.setState({ alert: 'Please select a dashboard' }); + this.setState({ alert: t('Please select a dashboard') }); return; } sliceParams.save_to_dashboard_id = dashboard; @@ -93,7 +94,7 @@ class SaveModal extends React.Component { case ('new'): dashboard = this.state.newDashboardName; if (dashboard === '') { - this.setState({ alert: 'Please enter a dashboard name' }); + this.setState({ alert: t('Please enter a dashboard name') }); return; } sliceParams.new_dashboard_name = dashboard; @@ -126,7 +127,7 @@ class SaveModal extends React.Component { > - Save A Slice + {t('Save A Slice')} @@ -147,7 +148,7 @@ class SaveModal extends React.Component { checked={this.state.action === 'overwrite'} onChange={this.changeAction.bind(this, 'overwrite')} > - {`Overwrite slice ${this.props.slice.slice_name}`} + {t('Overwrite slice %s', this.props.slice.slice_name)} } @@ -156,11 +157,11 @@ class SaveModal extends React.Component { inline checked={this.state.action === 'saveas'} onChange={this.changeAction.bind(this, 'saveas')} - > Save as   + > {t('Save as')}   @@ -173,7 +174,7 @@ class SaveModal extends React.Component { checked={this.state.addToDash === 'noSave'} onChange={this.changeDash.bind(this, 'noSave')} > - Do not add to a dashboard + {t('Do not add to a dashboard')} - Add slice to existing dashboard + {t('Add slice to existing dashboard')} + @@ -212,7 +214,7 @@ class SaveModal extends React.Component { className="btn pull-left" onClick={this.saveOrOverwrite.bind(this, false)} > - Save + {t('Save')} diff --git a/superset/assets/javascripts/explore/components/URLShortLinkButton.jsx b/superset/assets/javascripts/explore/components/URLShortLinkButton.jsx index 4dbf0f1103a4e..ddae9a2206429 100644 --- a/superset/assets/javascripts/explore/components/URLShortLinkButton.jsx +++ b/superset/assets/javascripts/explore/components/URLShortLinkButton.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { Popover, OverlayTrigger } from 'react-bootstrap'; import CopyToClipboard from './../../components/CopyToClipboard'; import { getShortUrl } from '../../../utils/common'; +import { t } from '../../locales'; const propTypes = { slice: PropTypes.object.isRequired, @@ -28,12 +29,12 @@ export default class URLShortLinkButton extends React.Component { } renderPopover() { - const emailBody = `Check out this slice: ${this.state.shortUrl}`; + const emailBody = t('Check out this slice: %s', this.state.shortUrl); return ( } + copyNode={} />    diff --git a/superset/assets/javascripts/explore/components/controls/BoundsControl.jsx b/superset/assets/javascripts/explore/components/controls/BoundsControl.jsx index 313ab93ae3f8e..776f7a499bde0 100644 --- a/superset/assets/javascripts/explore/components/controls/BoundsControl.jsx +++ b/superset/assets/javascripts/explore/components/controls/BoundsControl.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Col, Row, FormGroup, FormControl } from 'react-bootstrap'; import ControlHeader from '../ControlHeader'; +import { t } from '../../../locales'; const propTypes = { name: PropTypes.string.isRequired, @@ -51,10 +52,10 @@ export default class BoundsControl extends React.Component { const mm = this.state.minMax; const errors = []; if (mm[0] && isNaN(mm[0])) { - errors.push('`Min` value should be numeric or empty'); + errors.push(t('`Min` value should be numeric or empty')); } if (mm[1] && isNaN(mm[1])) { - errors.push('`Max` value should be numeric or empty'); + errors.push(t('`Max` value should be numeric or empty')); } if (errors.length === 0) { this.props.onChange([parseFloat(mm[0]), parseFloat(mm[1])], errors); @@ -71,7 +72,7 @@ export default class BoundsControl extends React.Component { @@ -79,7 +80,7 @@ export default class BoundsControl extends React.Component { diff --git a/superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx b/superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx index b00fe3fc79368..eb7a63367cf0f 100644 --- a/superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx +++ b/superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx @@ -5,6 +5,7 @@ import { Table } from 'reactable'; import { Label, FormControl, Modal, OverlayTrigger, Tooltip } from 'react-bootstrap'; import ControlHeader from '../ControlHeader'; +import { t } from '../../../locales'; const propTypes = { description: PropTypes.string, @@ -66,7 +67,7 @@ export default class DatasourceControl extends React.PureComponent { }, error() { that.setState({ loading: false }); - notify.error('Something went wrong while fetching the datasource list'); + notify.error(t('Something went wrong while fetching the datasource list')); }, }); } @@ -91,7 +92,7 @@ export default class DatasourceControl extends React.PureComponent { Click to point to another datasource + {t('Click to point to another datasource')} } >
}> Created Content
+
{t('Created Content')}
} > - Recent Activity
}> + {t('Recent Activity')}}> - Security & Access}> + {t('Security & Access')}}> diff --git a/superset/assets/javascripts/profile/components/CreatedContent.jsx b/superset/assets/javascripts/profile/components/CreatedContent.jsx index 87921c6872e6c..895be78434ce9 100644 --- a/superset/assets/javascripts/profile/components/CreatedContent.jsx +++ b/superset/assets/javascripts/profile/components/CreatedContent.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import moment from 'moment'; import TableLoader from './TableLoader'; +import { t } from '../../locales'; const propTypes = { user: PropTypes.object.isRequired, @@ -29,7 +30,7 @@ class CreatedContent extends React.PureComponent { className="table table-condensed" columns={['slice', 'favorited']} mutator={mutator} - noDataText="No slices" + noDataText={t('No slices')} sortable /> ); @@ -45,7 +46,7 @@ class CreatedContent extends React.PureComponent { className="table table-condensed" mutator={mutator} dataEndpoint={`/superset/created_dashboards/${this.props.user.userId}/`} - noDataText="No dashboards" + noDataText={t('No dashboards')} columns={['dashboard', 'favorited']} sortable /> @@ -54,10 +55,10 @@ class CreatedContent extends React.PureComponent { render() { return (
-

Dashboards

+

{t('Dashboards')}

{this.renderDashboardTable()}
-

Slices

+

{t('Slices')}

{this.renderSliceTable()}
); diff --git a/superset/assets/javascripts/profile/components/Favorites.jsx b/superset/assets/javascripts/profile/components/Favorites.jsx index 9039d916518b4..3141bb0c32850 100644 --- a/superset/assets/javascripts/profile/components/Favorites.jsx +++ b/superset/assets/javascripts/profile/components/Favorites.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import moment from 'moment'; import TableLoader from './TableLoader'; +import { t } from '../../locales'; const propTypes = { user: PropTypes.object.isRequired, @@ -30,7 +31,7 @@ export default class Favorites extends React.PureComponent { className="table table-condensed" columns={['slice', 'creator', 'favorited']} mutator={mutator} - noDataText="No favorite slices yet, go click on stars!" + noDataText={t('No favorite slices yet, go click on stars!')} sortable /> ); @@ -46,7 +47,7 @@ export default class Favorites extends React.PureComponent { className="table table-condensed" mutator={mutator} dataEndpoint={`/superset/fave_dashboards/${this.props.user.userId}/`} - noDataText="No favorite dashboards yet, go click on stars!" + noDataText={t('No favorite dashboards yet, go click on stars!')} columns={['dashboard', 'creator', 'favorited']} sortable /> @@ -55,10 +56,10 @@ export default class Favorites extends React.PureComponent { render() { return (
-

Dashboards

+

{t('Dashboards')}

{this.renderDashboardTable()}
-

Slices

+

{t('Slices')}

{this.renderSliceTable()}
); diff --git a/superset/assets/javascripts/profile/components/Security.jsx b/superset/assets/javascripts/profile/components/Security.jsx index 0d942dd83fdee..748be6b84043c 100644 --- a/superset/assets/javascripts/profile/components/Security.jsx +++ b/superset/assets/javascripts/profile/components/Security.jsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Badge, Label } from 'react-bootstrap'; +import { t } from '../../locales'; const propTypes = { user: PropTypes.object.isRequired, @@ -10,7 +11,7 @@ export default function Security({ user }) {

- Roles {Object.keys(user.roles).length} + {t('Roles')} {Object.keys(user.roles).length}

{Object.keys(user.roles).map(role => )}
@@ -19,7 +20,7 @@ export default function Security({ user }) { {user.permissions.database_access &&

- Databases {user.permissions.database_access.length} + {t('Databases')} {user.permissions.database_access.length}

{user.permissions.database_access.map(role => )}
@@ -30,7 +31,7 @@ export default function Security({ user }) { {user.permissions.datasource_access &&

- Datasources {user.permissions.datasource_access.length} + {t('Datasources')} {user.permissions.datasource_access.length}

{user.permissions.datasource_access.map(role => )}
diff --git a/superset/assets/javascripts/profile/components/UserInfo.jsx b/superset/assets/javascripts/profile/components/UserInfo.jsx index 4f751ed0d2391..cf9bde717bb6e 100644 --- a/superset/assets/javascripts/profile/components/UserInfo.jsx +++ b/superset/assets/javascripts/profile/components/UserInfo.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import Gravatar from 'react-gravatar'; import moment from 'moment'; import { Panel } from 'react-bootstrap'; +import { t } from '../../locales'; const propTypes = { user: PropTypes.object.isRequired, @@ -14,7 +15,7 @@ const UserInfo = ({ user }) => ( email={user.email} width="100%" height="" - alt="Profile picture provided by Gravatar" + alt={t('Profile picture provided by Gravatar')} className="img-rounded" style={{ borderRadius: 15 }} /> @@ -29,7 +30,7 @@ const UserInfo = ({ user }) => (

- joined {moment(user.createdOn, 'YYYYMMDD').fromNow()} + {t('joined')} {moment(user.createdOn, 'YYYYMMDD').fromNow()}

{user.email} @@ -39,7 +40,7 @@ const UserInfo = ({ user }) => (

  - id:  + {t('id:')}  {user.userId}

diff --git a/superset/assets/javascripts/profile/index.jsx b/superset/assets/javascripts/profile/index.jsx index adf371e699768..e9ed59bd719b2 100644 --- a/superset/assets/javascripts/profile/index.jsx +++ b/superset/assets/javascripts/profile/index.jsx @@ -1,7 +1,7 @@ /* eslint no-unused-vars: 0 */ import React from 'react'; import ReactDOM from 'react-dom'; -import { Badge, Col, Label, Row, Tabs, Tab, Panel } from 'react-bootstrap'; + import App from './components/App'; import { appSetup } from '../common'; diff --git a/superset/assets/package.json b/superset/assets/package.json index 002d0e8e39ca3..729bb022a66f4 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -54,6 +54,8 @@ "datamaps": "^0.5.8", "datatables.net-bs": "^1.10.15", "immutable": "^3.8.1", + "jed": "^1.1.1", + "po2json": "^0.4.5", "jquery": "3.1.1", "lodash.throttle": "^4.1.1", "moment": "^2.14.1", @@ -85,6 +87,7 @@ "redux-localstorage": "^0.4.1", "redux-thunk": "^2.1.0", "shortid": "^2.2.6", + "sprintf-js": "^1.1.1", "supercluster": "https://github.com/georgeke/supercluster/tarball/ac3492737e7ce98e07af679623aad452373bbc40", "urijs": "^1.18.10", "viewport-mercator-project": "^2.1.0" diff --git a/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx b/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx index c08cc664197c9..35b6f8141108c 100644 --- a/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx +++ b/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx @@ -1,6 +1,6 @@ import React from 'react'; import Select from 'react-select'; -import { mount, shallow } from 'enzyme'; +import { shallow } from 'enzyme'; import { describe, it } from 'mocha'; import { expect } from 'chai'; import sinon from 'sinon'; @@ -11,10 +11,12 @@ describe('AsyncSelect', () => { const mockedProps = { dataEndpoint: '/slicemodelview/api/read', onChange: sinon.spy(), + placeholder: 'Select...', mutator: () => [ { value: 1, label: 'main' }, { value: 2, label: 'another' }, ], + valueRenderer: opt => opt.label, }; it('is valid element', () => { expect( @@ -49,34 +51,34 @@ describe('AsyncSelect', () => { server.restore(); }); it('should be off by default', () => { - const wrapper = mount( + const wrapper = shallow( , ); + wrapper.instance().fetchOptions(); const spy = sinon.spy(wrapper.instance(), 'onChange'); expect(spy.callCount).to.equal(0); }); it('should auto select first option', () => { - const wrapper = mount( + const wrapper = shallow( , ); const spy = sinon.spy(wrapper.instance(), 'onChange'); - + wrapper.instance().fetchOptions(); server.respond(); expect(spy.callCount).to.equal(1); expect(spy.calledWith(wrapper.instance().state.options[0])).to.equal(true); }); it('should not auto select when value prop is set', () => { - const wrapper = mount( + const wrapper = shallow( , ); const spy = sinon.spy(wrapper.instance(), 'onChange'); - + wrapper.instance().fetchOptions(); server.respond(); expect(spy.callCount).to.equal(0); expect(wrapper.find(Select)).to.have.length(1); - expect(wrapper.find('.Select-value-label').children().first().text()).to.equal('another'); }); }); }); diff --git a/superset/assets/spec/javascripts/profile/EditableTitle_spec.jsx b/superset/assets/spec/javascripts/profile/EditableTitle_spec.jsx index edce86a0245a9..4e63b267ee785 100644 --- a/superset/assets/spec/javascripts/profile/EditableTitle_spec.jsx +++ b/superset/assets/spec/javascripts/profile/EditableTitle_spec.jsx @@ -19,7 +19,7 @@ describe('EditableTitle', () => { }, }; const editableWrapper = shallow(); - const notEditableWrapper = shallow(); + const notEditableWrapper = shallow(); it('is valid', () => { expect( React.isValidElement(), diff --git a/superset/assets/spec/javascripts/sqllab/SaveQuery_spec.jsx b/superset/assets/spec/javascripts/sqllab/SaveQuery_spec.jsx index 3c8953fbc4abd..98c8758a8db26 100644 --- a/superset/assets/spec/javascripts/sqllab/SaveQuery_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/SaveQuery_spec.jsx @@ -1,9 +1,8 @@ import React from 'react'; import { Overlay, Popover, FormControl } from 'react-bootstrap'; -import { shallow, mount } from 'enzyme'; +import { shallow } from 'enzyme'; import { describe, it } from 'mocha'; import { expect } from 'chai'; - import SaveQuery from '../../../javascripts/SqlLab/components/SaveQuery'; describe('SavedQuery', () => { @@ -30,11 +29,11 @@ describe('SavedQuery', () => { expect(wrapper.find(Popover)).to.have.length(1); }); it('pops and hides', () => { - const wrapper = mount(); + const wrapper = shallow(); expect(wrapper.state().showSave).to.equal(false); - wrapper.find('.toggleSave').simulate('click'); + wrapper.find('.toggleSave').simulate('click', { target: { value: 'test' } }); expect(wrapper.state().showSave).to.equal(true); - wrapper.find('.toggleSave').simulate('click'); + wrapper.find('.toggleSave').simulate('click', { target: { value: 'test' } }); expect(wrapper.state().showSave).to.equal(false); }); it('has a cancel button', () => { diff --git a/superset/assets/spec/javascripts/sqllab/Timer_spec.jsx b/superset/assets/spec/javascripts/sqllab/Timer_spec.jsx index 21a8a4fb3d442..e9172a928c1c2 100644 --- a/superset/assets/spec/javascripts/sqllab/Timer_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/Timer_spec.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { describe, it, beforeEach } from 'mocha'; import { expect } from 'chai'; +import sinon from 'sinon'; import Timer from '../../../javascripts/components/Timer'; import { now } from '../../../javascripts/modules/dates'; @@ -9,16 +10,21 @@ import { now } from '../../../javascripts/modules/dates'; describe('Timer', () => { let wrapper; + let clock; const mockedProps = { - startTime: now(), endTime: null, isRunning: true, status: 'warning', }; beforeEach(() => { + clock = sinon.useFakeTimers(); + mockedProps.startTime = now() + 1; wrapper = mount(); }); + afterEach(() => { + clock.restore(); + }); it('is a valid element', () => { expect(React.isValidElement()).to.equal(true); @@ -26,9 +32,8 @@ describe('Timer', () => { it('componentWillMount starts timer after 30ms and sets state.clockStr', () => { expect(wrapper.state().clockStr).to.equal(''); - setTimeout(() => { - expect(wrapper.state().clockStr).not.equal(''); - }, 31); + clock.tick(31); + expect(wrapper.state().clockStr).not.equal(''); }); it('calls startTimer on mount', () => { diff --git a/superset/assets/spec/javascripts/sqllab/reducers_spec.js b/superset/assets/spec/javascripts/sqllab/reducers_spec.js index f777503e01308..a3a5dbf7b5771 100644 --- a/superset/assets/spec/javascripts/sqllab/reducers_spec.js +++ b/superset/assets/spec/javascripts/sqllab/reducers_spec.js @@ -1,4 +1,4 @@ -import { beforeEach, describe, it } from 'mocha'; +import { describe, it } from 'mocha'; import { expect } from 'chai'; import * as r from '../../../javascripts/SqlLab/reducers'; @@ -9,7 +9,9 @@ describe('sqlLabReducer', () => { describe('CLONE_QUERY_TO_NEW_TAB', () => { const testQuery = { sql: 'SELECT * FROM...', dbId: 1, id: 'flasj233' }; let newState = Object.assign({}, initialState, { queries: { [testQuery.id]: testQuery } }); - newState = r.sqlLabReducer(newState, actions.cloneQueryToNewTab(testQuery)); + beforeEach(() => { + newState = r.sqlLabReducer(newState, actions.cloneQueryToNewTab(testQuery)); + }); it('should have at most one more tab', () => { expect(newState.queryEditors).have.length(2); diff --git a/superset/assets/visualizations/EventFlow.jsx b/superset/assets/visualizations/EventFlow.jsx index 110f4a76482c6..83811a5f465f4 100644 --- a/superset/assets/visualizations/EventFlow.jsx +++ b/superset/assets/visualizations/EventFlow.jsx @@ -9,6 +9,7 @@ import { EVENT_NAME, ENTITY_ID, } from '@data-ui/event-flow'; +import { t } from '../javascripts/locales'; /* * This function takes the slice object and json payload as input and renders a @@ -52,7 +53,7 @@ function renderEventFlow(slice, json) { Component = ; } else { - Component =
Sorry, there appears to be no data
; + Component =
{t('Sorry, there appears to be no data')}
; } ReactDOM.render(Component, container); diff --git a/superset/assets/visualizations/filter_box.jsx b/superset/assets/visualizations/filter_box.jsx index a5de26a810811..1bcaa52fa579a 100644 --- a/superset/assets/visualizations/filter_box.jsx +++ b/superset/assets/visualizations/filter_box.jsx @@ -8,6 +8,7 @@ import { Button } from 'react-bootstrap'; import { TIME_CHOICES } from './constants'; import './filter_box.css'; +import { t } from '../javascripts/locales'; const propTypes = { origSelectedValues: PropTypes.object, @@ -102,7 +103,7 @@ class FilterBox extends React.Component {
{this.props.datasource.verbose_map[filter] || filter}
-
))} diff --git a/superset/assets/javascripts/dashboard/components/Header.jsx b/superset/assets/javascripts/dashboard/components/Header.jsx index 740b102f1df9d..a1ab0e8e551d2 100644 --- a/superset/assets/javascripts/dashboard/components/Header.jsx +++ b/superset/assets/javascripts/dashboard/components/Header.jsx @@ -30,6 +30,7 @@ class Header extends React.PureComponent { title={dashboard.dashboard_title} canEdit={dashboard.dash_save_perm} onSaveTitle={this.handleSaveTitle} + noPermitTooltip={'You don\'t have the rights to alter this dashboard.'} /> diff --git a/superset/assets/javascripts/dashboard/components/SliceCell.jsx b/superset/assets/javascripts/dashboard/components/SliceCell.jsx index 3cd1334dc0db6..2fbdff31ba030 100644 --- a/superset/assets/javascripts/dashboard/components/SliceCell.jsx +++ b/superset/assets/javascripts/dashboard/components/SliceCell.jsx @@ -4,22 +4,35 @@ import PropTypes from 'prop-types'; import { t } from '../../locales'; import { getExploreUrl } from '../../explore/exploreUtils'; +import EditableTitle from '../../components/EditableTitle'; const propTypes = { slice: PropTypes.object.isRequired, removeSlice: PropTypes.func.isRequired, + updateSliceName: PropTypes.func, expandedSlices: PropTypes.object, }; -function SliceCell({ expandedSlices, removeSlice, slice }) { +const SliceCell = ({ expandedSlices, removeSlice, slice, updateSliceName }) => { + const onSaveTitle = (newTitle) => { + if (updateSliceName) { + updateSliceName(slice.slice_id, newTitle); + } + }; + return (
-
-
-
- {slice.slice_name} +
+
+
+
-
+ ); -} +}; SliceCell.propTypes = propTypes; diff --git a/superset/assets/stylesheets/dashboard.css b/superset/assets/stylesheets/dashboard.css index ad84807aa14c4..fd47dec38d11c 100644 --- a/superset/assets/stylesheets/dashboard.css +++ b/superset/assets/stylesheets/dashboard.css @@ -16,7 +16,6 @@ div.widget .chart-controls { position: absolute; z-index: 100; right: 0; - left: 0; top: 5px; padding: 5px 5px; opacity: 0.75; @@ -117,6 +116,7 @@ div.widget .chart-controls { .chart-header .header { font-size: 16px; + margin: 0 -10px; } .ace_gutter { z-index: 0; diff --git a/superset/views/core.py b/superset/views/core.py index 65d61bab16b4f..386ec5a702c6a 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -1069,6 +1069,10 @@ def explore(self, datasource_type, datasource_id): # handle save or overwrite action = request.args.get('action') + + if action == 'overwrite' and not slice_overwrite_perm: + return json_error_response("You don't have the rights to alter this slice", status=400) + if action in ('saveas', 'overwrite'): return self.save_or_overwrite_slice( request.args, From 3949d39478f482993e4b11d51e7229a3784f5be1 Mon Sep 17 00:00:00 2001 From: Grace Guo Date: Mon, 25 Sep 2017 11:38:29 -0700 Subject: [PATCH 13/20] Allow user update slice title in visualize flow (#3466) * 1. after user make sql query and visualize, allow user click title to update slice title, and create a new slice at the same time. 2. don't save new title if it is empty. Will still show old title. * change saveSlice call response and update explore view --- .../javascripts/components/EditableTitle.jsx | 8 ++++++++ .../explore/actions/exploreActions.js | 5 +++++ .../explore/components/ChartContainer.jsx | 19 +++++++++++++------ .../explore/components/SaveModal.jsx | 3 ++- superset/assets/javascripts/explore/main.css | 3 +++ .../explore/reducers/exploreReducer.js | 9 +++++++++ .../profile/EditableTitle_spec.jsx | 7 +++++++ superset/views/core.py | 14 +++++++++++--- 8 files changed, 58 insertions(+), 10 deletions(-) diff --git a/superset/assets/javascripts/components/EditableTitle.jsx b/superset/assets/javascripts/components/EditableTitle.jsx index 9ea64a34912b9..31c4c53c96e03 100644 --- a/superset/assets/javascripts/components/EditableTitle.jsx +++ b/superset/assets/javascripts/components/EditableTitle.jsx @@ -53,6 +53,14 @@ class EditableTitle extends React.PureComponent { isEditing: false, }); + if (!this.state.title.length) { + this.setState({ + title: this.state.lastTitle, + }); + + return; + } + if (this.state.lastTitle !== this.state.title) { this.setState({ lastTitle: this.state.title, diff --git a/superset/assets/javascripts/explore/actions/exploreActions.js b/superset/assets/javascripts/explore/actions/exploreActions.js index 160828229786f..dbba7b7fbed73 100644 --- a/superset/assets/javascripts/explore/actions/exploreActions.js +++ b/superset/assets/javascripts/explore/actions/exploreActions.js @@ -150,3 +150,8 @@ export const RENDER_TRIGGERED = 'RENDER_TRIGGERED'; export function renderTriggered() { return { type: RENDER_TRIGGERED }; } + +export const CREATE_NEW_SLICE = 'CREATE_NEW_SLICE'; +export function createNewSlice(can_add, can_download, can_overwrite, slice, form_data) { + return { type: CREATE_NEW_SLICE, can_add, can_download, can_overwrite, slice, form_data }; +} diff --git a/superset/assets/javascripts/explore/components/ChartContainer.jsx b/superset/assets/javascripts/explore/components/ChartContainer.jsx index 2d0830d324c50..f3c660adf66bc 100644 --- a/superset/assets/javascripts/explore/components/ChartContainer.jsx +++ b/superset/assets/javascripts/explore/components/ChartContainer.jsx @@ -153,15 +153,22 @@ class ChartContainer extends React.PureComponent { this.props.actions.runQuery(this.props.formData, true, this.props.timeout); } - updateChartTitle(newTitle) { + updateChartTitleOrSaveSlice(newTitle) { + const isNewSlice = !this.props.slice; const params = { slice_name: newTitle, - action: 'overwrite', + action: isNewSlice ? 'saveas' : 'overwrite', }; const saveUrl = getExploreUrl(this.props.formData, 'base', false, null, params); this.props.actions.saveSlice(saveUrl) - .then(() => { - this.props.actions.updateChartTitle(newTitle); + .then((data) => { + if (isNewSlice) { + this.props.actions.createNewSlice( + data.can_add, data.can_download, data.can_overwrite, + data.slice, data.form_data); + } else { + this.props.actions.updateChartTitle(newTitle); + } }); } @@ -263,8 +270,8 @@ class ChartContainer extends React.PureComponent { > {this.props.slice && diff --git a/superset/assets/javascripts/explore/components/SaveModal.jsx b/superset/assets/javascripts/explore/components/SaveModal.jsx index ac76e3a5ad68e..9de835381b243 100644 --- a/superset/assets/javascripts/explore/components/SaveModal.jsx +++ b/superset/assets/javascripts/explore/components/SaveModal.jsx @@ -108,7 +108,7 @@ class SaveModal extends React.Component { this.props.actions.saveSlice(saveUrl) .then((data) => { // Go to new slice url or dashboard url - window.location = data; + window.location = data.slice.slice_url; }); this.props.onHide(); } @@ -185,6 +185,7 @@ class SaveModal extends React.Component { {t('Add slice to existing dashboard')}