diff --git a/superset/assets/javascripts/profile/components/Favorites.jsx b/superset/assets/javascripts/profile/components/Favorites.jsx
index 7aa31f19fc67a..147d1b210f4ff 100644
--- a/superset/assets/javascripts/profile/components/Favorites.jsx
+++ b/superset/assets/javascripts/profile/components/Favorites.jsx
@@ -19,6 +19,7 @@ export default class Favorites extends React.PureComponent {
renderSliceTable() {
const mutator = (data) => data.map(slice => ({
slice: {slice.title},
+ creator: {slice.creator},
favorited: moment.utc(slice.dttm).fromNow(),
_favorited: slice.dttm,
}));
@@ -26,7 +27,7 @@ export default class Favorites extends React.PureComponent {
data.map(dash => ({
dashboard: {dash.title},
+ creator: {dash.creator},
favorited: moment.utc(dash.dttm).fromNow(),
}));
return (
@@ -44,7 +46,7 @@ export default class Favorites extends React.PureComponent {
mutator={mutator}
dataEndpoint={`/superset/fave_dashboards/${this.props.user.userId}/`}
noDataText="No favorite dashboards yet, go click on stars!"
- columns={['dashboard', 'favorited']}
+ columns={['dashboard', 'creator', 'favorited']}
sortable
/>
);
diff --git a/superset/assets/javascripts/profile/index.jsx b/superset/assets/javascripts/profile/index.jsx
index f32932b182b01..913dfca705051 100644
--- a/superset/assets/javascripts/profile/index.jsx
+++ b/superset/assets/javascripts/profile/index.jsx
@@ -15,7 +15,6 @@ const profileViewContainer = document.getElementById('app');
const bootstrap = JSON.parse(profileViewContainer.getAttribute('data-bootstrap'));
const user = bootstrap.user;
-
ReactDOM.render(
,
profileViewContainer
diff --git a/superset/models.py b/superset/models.py
index a5e3d3a572693..882634680a27b 100644
--- a/superset/models.py
+++ b/superset/models.py
@@ -149,13 +149,19 @@ def changed_by_fk(cls): # noqa
Integer, ForeignKey('ab_user.id'),
default=cls.get_user_id, onupdate=cls.get_user_id, nullable=True)
- @renders('created_on')
+ def _user_link(self, user):
+ if not user:
+ return ''
+ url = '/superset/profile/{}/'.format(user.username)
+ return '{}'.format(url, escape(user) or '')
+
+ @renders('created_by')
def creator(self): # noqa
- return '{}'.format(self.created_by or '')
+ return self._user_link(self.created_by)
@property
def changed_by_(self):
- return '{}'.format(self.changed_by or '')
+ return self._user_link(self.changed_by)
@renders('changed_on')
def changed_on_(self):
diff --git a/superset/views.py b/superset/views.py
index a375056a39277..0dcee47908a77 100755
--- a/superset/views.py
+++ b/superset/views.py
@@ -1796,7 +1796,7 @@ def recent_activity(self, user_id):
)
.filter(
sqla.and_(
- M.Log.action != 'queries',
+ ~M.Log.action.in_(('queries', 'shortner', 'sql_json')),
M.Log.user_id == user_id,
)
)
@@ -1845,13 +1845,21 @@ def fave_dashboards(self, user_id):
models.FavStar.dttm.desc()
)
)
- payload = [{
- 'id': o.Dashboard.id,
- 'dashboard': o.Dashboard.dashboard_link(),
- 'title': o.Dashboard.dashboard_title,
- 'url': o.Dashboard.url,
- 'dttm': o.dttm,
- } for o in qry.all()]
+ payload = []
+ for o in qry.all():
+ d = {
+ 'id': o.Dashboard.id,
+ 'dashboard': o.Dashboard.dashboard_link(),
+ 'title': o.Dashboard.dashboard_title,
+ 'url': o.Dashboard.url,
+ 'dttm': o.dttm,
+ }
+ if o.Dashboard.created_by:
+ user = o.Dashboard.created_by
+ d['creator'] = str(user)
+ d['creator_url'] = '/superset/profile/{}/'.format(
+ user.username)
+ payload.append(d)
return Response(
json.dumps(payload, default=utils.json_int_dttm_ser),
mimetype="application/json")
@@ -1934,12 +1942,20 @@ def fave_slices(self, user_id):
models.FavStar.dttm.desc()
)
)
- payload = [{
- 'id': o.Slice.id,
- 'title': o.Slice.slice_name,
- 'url': o.Slice.slice_url,
- 'dttm': o.dttm,
- } for o in qry.all()]
+ payload = []
+ for o in qry.all():
+ d = {
+ 'id': o.Slice.id,
+ 'title': o.Slice.slice_name,
+ 'url': o.Slice.slice_url,
+ 'dttm': o.dttm,
+ }
+ if o.Slice.created_by:
+ user = o.Slice.created_by
+ d['creator'] = str(user)
+ d['creator_url'] = '/superset/profile/{}/'.format(
+ user.username)
+ payload.append(d)
return Response(
json.dumps(payload, default=utils.json_int_dttm_ser),
mimetype="application/json")
@@ -2613,15 +2629,15 @@ def profile(self, username):
)
roles = {}
from collections import defaultdict
- permissions = defaultdict(list)
+ permissions = defaultdict(set)
for role in user.roles:
- perms = []
+ perms = set()
for perm in role.permissions:
- perms.append(
+ perms.add(
(perm.permission.name, perm.view_menu.name)
)
if perm.permission.name in ('datasource_access', 'database_access'):
- permissions[perm.permission.name].append(perm.view_menu.name)
+ permissions[perm.permission.name].add(perm.view_menu.name)
roles[role.name] = [
[perm.permission.name, perm.view_menu.name]
for perm in role.permissions
@@ -2643,7 +2659,8 @@ def profile(self, username):
'superset/profile.html',
title=user.username + "'s profile",
navbar_container=True,
- bootstrap_data=json.dumps(payload))
+ bootstrap_data=json.dumps(payload, default=utils.json_iso_dttm_ser)
+ )
@has_access
@expose("/sqllab")
diff --git a/tests/base_tests.py b/tests/base_tests.py
index 3a66c756de48d..36f3d8f3a9049 100644
--- a/tests/base_tests.py
+++ b/tests/base_tests.py
@@ -140,19 +140,23 @@ def get_druid_ds_by_name(self, name):
return db.session.query(models.DruidDatasource).filter_by(
datasource_name=name).first()
- def get_resp(self, url, data=None, follow_redirects=True):
+ def get_resp(
+ self, url, data=None, follow_redirects=True, raise_on_error=True):
"""Shortcut to get the parsed results while following redirects"""
if data:
resp = self.client.post(
url, data=data, follow_redirects=follow_redirects)
- return resp.data.decode('utf-8')
else:
resp = self.client.get(url, follow_redirects=follow_redirects)
- return resp.data.decode('utf-8')
+ if raise_on_error and resp.status_code > 400:
+ raise Exception(
+ "http request failed with code {}".format(resp.status_code))
+ return resp.data.decode('utf-8')
- def get_json_resp(self, url, data=None):
+ def get_json_resp(
+ self, url, data=None, follow_redirects=True, raise_on_error=True):
"""Shortcut to get the parsed results while following redirects"""
- resp = self.get_resp(url, data=data)
+ resp = self.get_resp(url, data, follow_redirects, raise_on_error)
return json.loads(resp)
def get_main_database(self, session):
@@ -200,6 +204,7 @@ def run_sql(self, sql, client_id, user_name=None):
dbid = self.get_main_database(db.session).id
resp = self.get_json_resp(
'/superset/sql_json/',
+ raise_on_error=False,
data=dict(database_id=dbid, sql=sql, select_as_create_as=False,
client_id=client_id),
)
diff --git a/tests/core_tests.py b/tests/core_tests.py
index d79dd67baaab6..2c2267a3bdb7a 100644
--- a/tests/core_tests.py
+++ b/tests/core_tests.py
@@ -478,6 +478,23 @@ def test_fetch_all_tables(self):
def test_user_profile(self):
self.login(username='admin')
+ slc = self.get_slice("Girls", db.session)
+
+ # Setting some faves
+ url = '/superset/favstar/Slice/{}/select/'.format(slc.id)
+ resp = self.get_json_resp(url)
+ self.assertEqual(resp['count'], 1)
+
+ dash = (
+ db.session
+ .query(models.Dashboard)
+ .filter_by(slug="births")
+ .first()
+ )
+ url = '/superset/favstar/Dashboard/{}/select/'.format(dash.id)
+ resp = self.get_json_resp(url)
+ self.assertEqual(resp['count'], 1)
+
userid = appbuilder.sm.find_user('admin').id
resp = self.get_resp('/superset/profile/admin/')
self.assertIn('"app"', resp)
diff --git a/tests/sqllab_tests.py b/tests/sqllab_tests.py
index cd8a93d806bc2..00c574dc741d4 100644
--- a/tests/sqllab_tests.py
+++ b/tests/sqllab_tests.py
@@ -118,8 +118,7 @@ def test_search_query_on_db_id(self):
self.run_some_queries()
self.login('admin')
# Test search queries on database Id
- resp = self.get_resp('/superset/search_queries?database_id=1')
- data = json.loads(resp)
+ data = self.get_json_resp('/superset/search_queries?database_id=1')
self.assertEquals(3, len(data))
db_ids = [data[k]['dbId'] for k in data]
self.assertEquals([1, 1, 1], db_ids)
@@ -164,8 +163,8 @@ def test_search_query_on_status(self):
def test_search_query_on_text(self):
self.run_some_queries()
self.login('admin')
- resp = self.get_resp('/superset/search_queries?search_text=permission')
- data = json.loads(resp)
+ url = '/superset/search_queries?search_text=permission'
+ data = self.get_json_resp(url)
self.assertEquals(1, len(data))
self.assertIn('permission', list(data.values())[0]['sql'])