Compare commits

...

11 Commits

Author SHA1 Message Date
Arik Fraimovich
b8aefd26b8 Merge pull request #392 from EverythingMe/feature/post_to_create_a_query
Support posting to /queries/new to create a new query.
2015-03-18 13:42:55 +02:00
Arik Fraimovich
85a762bcd2 Support posting to /queries/new to create a new query. 2015-03-18 13:28:23 +02:00
Arik Fraimovich
4f1b3d5beb Merge pull request #391 from EverythingMe/feature/api_key_auth
Fix: allow dots in column name
2015-03-16 15:08:00 +02:00
Arik Fraimovich
9218a7c437 Fix: allow dots in column name 2015-03-16 14:59:51 +02:00
Arik Fraimovich
71a3f066a5 Ignore gh-pages branch in CircleCI. 2015-03-16 09:03:52 +02:00
Arik Fraimovich
89436d779c Merge pull request #390 from fedex1/patch-1
Update bootstrap.sh
2015-03-16 05:37:33 +02:00
Ralph Yozzo
3631e938da Update bootstrap.sh
# modified by @fedex1 3/15/2015 seems to be the latest version at this point in time.
2015-03-15 23:27:45 -04:00
Arik Fraimovich
c0a9db68f0 Merge pull request #389 from EverythingMe/feature/api_key_auth
Fix: show date/time with respect to user's locale
2015-03-15 18:53:31 +02:00
Arik Fraimovich
bec9c9e14e Fix: show date/time in user's locale 2015-03-15 18:53:02 +02:00
Arik Fraimovich
47bbc25277 Merge pull request #388 from EverythingMe/feature/api_key_auth
Make it possible to set enabled query runners from env
2015-03-12 12:00:26 +02:00
Arik Fraimovich
f02c2588d2 Make it possible to set enabled query runners from env 2015-03-12 11:52:31 +02:00
10 changed files with 69 additions and 19 deletions

View File

@@ -26,3 +26,7 @@ deployment:
notify:
webhooks:
- url: https://webhooks.gitter.im/e/895d09c3165a0913ac2f
general:
branches:
ignore:
- gh-pages

View File

@@ -250,6 +250,7 @@
'__brkt': /[\(\)\[\]]/g,
'__dash': /-/g,
'__amp': /&/g,
'__dot': /\./g,
'__sl': /\//g,
'__fsl': /\\/g,
};

View File

@@ -79,14 +79,14 @@
} else if (columnType === 'date') {
columnDefinition.formatFunction = function (value) {
if (value) {
return value.format("DD/MM/YY");
return value.toDate().toLocaleDateString();
}
return value;
};
} else if (columnType === 'datetime') {
columnDefinition.formatFunction = function (value) {
if (value) {
return value.format("DD/MM/YY HH:mm");
return value.toDate().toLocaleString();
}
return value;
};

View File

@@ -135,6 +135,24 @@ def format_sql_query():
return sqlparse.format(query, reindent=True, keyword_case='upper')
@app.route('/queries/new', methods=['POST'])
@auth.required
def create_query_route():
query = request.form.get('query', None)
data_source_id = request.form.get('data_source_id', None)
if query is None or data_source_id is None:
abort(400)
query = models.Query.create(name="New Query",
query=query,
data_source=data_source_id,
user=current_user._get_current_object(),
ttl=-1)
return redirect('/queries/{}'.format(query.id), 303)
class BaseResource(Resource):
decorators = [auth.required]
@@ -326,8 +344,6 @@ class QueryListAPI(BaseResource):
query = models.Query(**query_def)
query.save()
query.create_default_visualizations()
return query.to_dict()
@require_permission('view_query')
@@ -350,6 +366,7 @@ class QueryAPI(BaseResource):
if 'data_source_id' in query_def:
query_def['data_source'] = query_def.pop('data_source_id')
# TODO: use #save() with #dirty_fields.
models.Query.update_instance(query_id, **query_def)
query = models.Query.get_by_id(query_id)

View File

@@ -60,6 +60,21 @@ class BaseModel(peewee.Model):
def get_by_id(cls, model_id):
return cls.get(cls.id == model_id)
def pre_save(self, created):
# Handler for pre_save operations. Overriding if needed.
pass
def post_save(self, created):
# Handler for post_save operations. Overriding if needed.
pass
def save(self, *args, **kwargs):
pk_value = self._get_pk_value()
created = kwargs.get('force_insert', False) or not bool(pk_value)
self.pre_save(created)
super(BaseModel, self).save(*args, **kwargs)
self.post_save(created)
class PermissionsCheckMixin(object):
def has_permission(self, permission):
@@ -306,12 +321,6 @@ class Query(BaseModel):
class Meta:
db_table = 'queries'
def create_default_visualizations(self):
table_visualization = Visualization(query=self, name="Table",
description='',
type="TABLE", options="{}")
table_visualization.save()
def to_dict(self, with_stats=False, with_visualizations=False, with_user=True):
d = {
'id': self.id,
@@ -396,6 +405,7 @@ class Query(BaseModel):
@classmethod
def recent(cls, user_id):
# TODO: instead of t2 here, we should define table_alias for Query table
return cls.select().where(Event.created_at > peewee.SQL("current_date - 7")).\
join(Event, on=(Query.id == peewee.SQL("t2.object_id::integer"))).\
where(Event.action << ('edit', 'execute', 'edit_name', 'edit_description', 'view_source')).\
@@ -414,10 +424,19 @@ class Query(BaseModel):
update = cls.update(**kwargs).where(cls.id == query_id)
return update.execute()
def save(self, *args, **kwargs):
def pre_save(self, created):
self.query_hash = utils.gen_query_hash(self.query)
self._set_api_key()
super(Query, self).save(*args, **kwargs)
def post_save(self, created):
if created:
self._create_default_visualizations()
def _create_default_visualizations(self):
table_visualization = Visualization(query=self, name="Table",
description='',
type="TABLE", options="{}")
table_visualization.save()
def _set_api_key(self):
if not self.api_key:

View File

@@ -67,14 +67,14 @@ GOOGLE_CLIENT_SECRET = os.environ.get("REDASH_GOOGLE_CLIENT_SECRET", "")
GOOGLE_OAUTH_ENABLED = GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET
STATIC_ASSETS_PATH = fix_assets_path(os.environ.get("REDASH_STATIC_ASSETS_PATH", "../rd_ui/app/"))
JOB_EXPIRY_TIME = int(os.environ.get("REDASH_JOB_EXPIRY_TIME", 3600*6))
JOB_EXPIRY_TIME = int(os.environ.get("REDASH_JOB_EXPIRY_TIME", 3600 * 6))
COOKIE_SECRET = os.environ.get("REDASH_COOKIE_SECRET", "c292a0a3aa32397cdb050e233733900f")
LOG_LEVEL = os.environ.get("REDASH_LOG_LEVEL", "INFO")
CLIENT_SIDE_METRICS = parse_boolean(os.environ.get("REDASH_CLIENT_SIDE_METRICS", "false"))
ANALYTICS = os.environ.get("REDASH_ANALYTICS", "")
# Query Runners
QUERY_RUNNERS = [
QUERY_RUNNERS = array_from_string(os.environ.get("REDASH_ENABLED_QUERY_RUNNERS", ",".join([
'redash.query_runner.big_query',
'redash.query_runner.graphite',
'redash.query_runner.mongodb',
@@ -82,7 +82,7 @@ QUERY_RUNNERS = [
'redash.query_runner.pg',
'redash.query_runner.script',
'redash.query_runner.url',
]
])))
# Features:
FEATURE_TABLES_PERMISSIONS = parse_boolean(os.environ.get("REDASH_FEATURE_TABLES_PERMISSIONS", "false"))

View File

@@ -98,7 +98,9 @@ if [ ! -f "/opt/redash/.env" ]; then
fi
# Install latest version
REDASH_VERSION=${REDASH_VERSION-0.4.0.b589}
# REDASH_VERSION=${REDASH_VERSION-0.4.0.b589}
# modified by @fedex1 3/15/2015 seems to be the latest version at this point in time.
REDASH_VERSION=${REDASH_VERSION-0.6.0.b722}
LATEST_URL="https://github.com/EverythingMe/redash/releases/download/v${REDASH_VERSION/.b/%2Bb}/redash.$REDASH_VERSION.tar.gz"
VERSION_DIR="/opt/redash/redash.$REDASH_VERSION"
REDASH_TARBALL=/tmp/redash.tar.gz

View File

@@ -294,7 +294,8 @@ class VisualizationAPITest(BaseTestCase):
rv = json_request(c.delete, '/api/visualizations/{0}'.format(visualization.id))
self.assertEquals(rv.status_code, 200)
self.assertEquals(models.Visualization.select().count(), 0)
# =1 because each query has a default table visualization.
self.assertEquals(models.Visualization.select().count(), 1)
def test_update_visualization(self):
visualization = visualization_factory.create()

View File

@@ -26,8 +26,10 @@ class ImportTest(BaseTestCase):
self.assertEqual(dashboard.widgets.count(),
reduce(lambda s, row: s + len(row), self.dashboard['widgets'], 0))
self.assertEqual(models.Visualization.select().count(), dashboard.widgets.count()-1)
self.assertEqual(models.Query.select().count(), dashboard.widgets.count()-2)
queries_count = models.Query.select().count()
self.assertEqual(models.Visualization.select().count(), dashboard.widgets.count()+queries_count-1)
self.assertEqual(queries_count, dashboard.widgets.count()-2)
def test_imports_updates_existing_models(self):
importer = import_export.Importer(data_source=data_source_factory.create())

View File

@@ -65,6 +65,10 @@ class QueryTest(BaseTestCase):
self.assertNotIn(q1, queries)
self.assertNotIn(q2, queries)
def test_save_creates_default_visualization(self):
q = query_factory.create()
self.assertEquals(q.visualizations.count(), 1)
class QueryArchiveTest(BaseTestCase):
def setUp(self):