diff --git a/superset/assets/javascripts/profile/components/CreatedContent.jsx b/superset/assets/javascripts/profile/components/CreatedContent.jsx index d779e19892178..96978756d3fa2 100644 --- a/superset/assets/javascripts/profile/components/CreatedContent.jsx +++ b/superset/assets/javascripts/profile/components/CreatedContent.jsx @@ -19,6 +19,7 @@ class CreatedContent extends React.PureComponent { renderSliceTable() { const mutator = (data) => data.map(slice => ({ slice: {slice.title}, + views: slice.views, favorited: moment.utc(slice.dttm).fromNow(), _favorited: slice.dttm, })); @@ -26,7 +27,7 @@ class CreatedContent extends React.PureComponent { data.map(dash => ({ dashboard: {dash.title}, + views: dash.views, favorited: moment.utc(dash.dttm).fromNow(), _favorited: dash.dttm, })); @@ -45,7 +47,7 @@ class CreatedContent extends React.PureComponent { mutator={mutator} dataEndpoint={`/superset/created_dashboards/${this.props.user.userId}/`} noDataText="No dashboards" - columns={['dashboard', 'favorited']} + columns={['dashboard', 'favorited', 'views']} sortable /> ); diff --git a/superset/migrations/versions/1b2c3f7c96f9_.py b/superset/migrations/versions/1b2c3f7c96f9_.py new file mode 100644 index 0000000000000..0e1a1379de0e4 --- /dev/null +++ b/superset/migrations/versions/1b2c3f7c96f9_.py @@ -0,0 +1,64 @@ +"""Add number of views as column to dashboards and slices + +Revision ID: 1b2c3f7c96f9 +Revises: 6414e83d82b7 +Create Date: 2016-12-15 16:32:31.909331 + +""" + +# revision identifiers, used by Alembic. +revision = '1b2c3f7c96f9' +down_revision = '6414e83d82b7' + +from alembic import op +import sqlalchemy as sa +from superset import db, models + +def upgrade(): + op.add_column('dashboards', sa.Column('views', sa.Integer, server_default='1', nullable=True)) + op.add_column('slices', sa.Column('views', sa.Integer, server_default='1', nullable=True)) + + if db.engine.name != 'postgresql': + Dash = models.Dashboard + Log = models.Log + qry = ( + db.session.query( + Dash, + sa.func.count(), + ) + .outerjoin(Log) + .filter( + sa.and_( + Log.dashboard_id == Dash.id, + ) + ) + .group_by(Dash) + ) + for dash_obj in qry.all(): + dash_obj[0].views = dash_obj[1] + db.session.commit() + + Slice = models.Slice + qry = ( + db.session.query( + Slice, + sa.func.count(), + ) + .outerjoin(Log) + .filter( + sa.and_( + Log.slice_id == Slice.id, + ) + ) + .group_by(Slice) + ) + for slice_obj in qry.all(): + slice_obj[0].views = slice_obj[1] + db.session.commit() + db.session.close() + + + +def downgrade(): + op.drop_column('dashboards', 'views') + op.drop_column('slices', 'views') diff --git a/superset/models.py b/superset/models.py index 84701a17db2b2..cef46a4c57a8b 100644 --- a/superset/models.py +++ b/superset/models.py @@ -232,6 +232,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin): cache_timeout = Column(Integer) perm = Column(String(1000)) owners = relationship("User", secondary=slice_user) + views = Column(Integer, default=1) export_fields = ('slice_name', 'datasource_type', 'datasource_name', 'viz_type', 'params', 'cache_timeout') @@ -437,9 +438,9 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin): slices = relationship( 'Slice', secondary=dashboard_slices, backref='dashboards') owners = relationship("User", secondary=dashboard_user) - export_fields = ('dashboard_title', 'position_json', 'json_metadata', 'description', 'css', 'slug') + views = Column(Integer, default=1) def __repr__(self): return self.dashboard_title @@ -2366,8 +2367,8 @@ class Log(Model): id = Column(Integer, primary_key=True) action = Column(String(512)) user_id = Column(Integer, ForeignKey('ab_user.id')) - dashboard_id = Column(Integer) - slice_id = Column(Integer) + dashboard_id = Column(Integer, ForeignKey('dashboards.id')) + slice_id = Column(Integer, ForeignKey('slices.id')) json = Column(Text) user = relationship('User', backref='logs', foreign_keys=[user_id]) dttm = Column(DateTime, default=datetime.utcnow) diff --git a/superset/views.py b/superset/views.py index fb8d6faa628a2..6cb206bfff7f2 100755 --- a/superset/views.py +++ b/superset/views.py @@ -1438,6 +1438,8 @@ def explore(self, datasource_type, datasource_id): if slice_id: slc = db.session.query(models.Slice).filter_by(id=slice_id).first() + slc.views = slc.views + 1 + db.session.commit() error_redirect = '/slicemodelview/list/' datasource_class = SourceRegistry.sources[datasource_type] @@ -1955,10 +1957,7 @@ def created_dashboards(self, user_id): Dash, ) .filter( - sqla.or_( - Dash.created_by_fk == user_id, - Dash.changed_by_fk == user_id, - ) + Dash.created_by_fk == user_id, ) .order_by( Dash.changed_on.desc() @@ -1970,6 +1969,7 @@ def created_dashboards(self, user_id): 'title': o.dashboard_title, 'url': o.url, 'dttm': o.changed_on, + 'views': o.views, } for o in qry.all()] return Response( json.dumps(payload, default=utils.json_int_dttm_ser), @@ -1984,10 +1984,7 @@ def created_slices(self, user_id): qry = ( db.session.query(Slice) .filter( - sqla.or_( - Slice.created_by_fk == user_id, - Slice.changed_by_fk == user_id, - ) + Slice.created_by_fk == user_id, ) .order_by(Slice.changed_on.desc()) ) @@ -1996,6 +1993,7 @@ def created_slices(self, user_id): 'title': o.slice_name, 'url': o.slice_url, 'dttm': o.changed_on, + 'views': o.views, } for o in qry.all()] return Response( json.dumps(payload, default=utils.json_int_dttm_ser), @@ -2132,6 +2130,8 @@ def dashboard(self, dashboard_id): qry = qry.filter_by(slug=dashboard_id) dash = qry.one() + dash.views = dash.views + 1 + db.session.commit() datasources = {slc.datasource for slc in dash.slices} for datasource in datasources: if not self.datasource_access(datasource):