diff --git a/dashed/assets/javascripts/dashboard.js b/dashed/assets/javascripts/dashboard.js index b5b7d0dcff..57dccfd6db 100644 --- a/dashed/assets/javascripts/dashboard.js +++ b/dashed/assets/javascripts/dashboard.js @@ -17,6 +17,7 @@ var Dashboard = function (dashboardData) { filters: {}, init: function () { this.initDashboardView(); + px.initFavStars(); var sliceObjects = [], dash = this; dashboard.slices.forEach(function (data) { diff --git a/dashed/assets/javascripts/explore.js b/dashed/assets/javascripts/explore.js index 7ea48bcf8e..2fc9154d16 100644 --- a/dashed/assets/javascripts/explore.js +++ b/dashed/assets/javascripts/explore.js @@ -117,6 +117,8 @@ function initExploreView() { $("#collapsed_fieldsets").val(collapsed_fieldsets.join("||")); } + px.initFavStars(); + $('legend').click(function () { toggle_fieldset($(this), true); }); diff --git a/dashed/assets/javascripts/modules/dashed.js b/dashed/assets/javascripts/modules/dashed.js index dd0e4af65a..3b2ab4f216 100644 --- a/dashed/assets/javascripts/modules/dashed.js +++ b/dashed/assets/javascripts/modules/dashed.js @@ -132,6 +132,46 @@ var px = (function () { }; } + function initFavStars() { + var baseUrl = '/dashed/favstar/'; + // Init star behavihor for favorite + function show() { + if ($(this).hasClass('selected')) { + $(this).html(''); + } else { + $(this).html(''); + } + } + $('.favstar') + .attr('title', 'Click to favorite/unfavorite') + .each(show) + .each(function () { + var url = baseUrl + $(this).attr("class_name"); + var star = this; + url += '/' + $(this).attr("obj_id") + '/'; + $.getJSON(url + 'count/', function (data) { + if (data.count > 0) { + $(star) + .addClass('selected') + .each(show); + } + }); + }) + .click(function () { + $(this).toggleClass('selected'); + var url = baseUrl + $(this).attr("class_name"); + url += '/' + $(this).attr("obj_id") + '/'; + if ($(this).hasClass('selected')) { + url += 'select/'; + } else { + url += 'unselect/'; + } + $.get(url); + $(this).each(show); + }) + .tooltip(); + } + var Slice = function (data, dashboard) { var timer; var token = $('#' + data.token); @@ -334,7 +374,8 @@ var px = (function () { formatDate: formatDate, timeFormatFactory: timeFormatFactory, color: color(), - getParam: getParam + getParam: getParam, + initFavStars: initFavStars }; })(); diff --git a/dashed/assets/stylesheets/dashed.css b/dashed/assets/stylesheets/dashed.css index 8f67c8bcc1..dd8d5cc578 100644 --- a/dashed/assets/stylesheets/dashed.css +++ b/dashed/assets/stylesheets/dashed.css @@ -26,6 +26,12 @@ input.form-control { margin-left: 365px; } +.favstar { + margin-right: 10px; + opacity: 0.5; + cursor: pointer; +} + .slice_description{ padding: 8px; margin: 5px; @@ -84,7 +90,10 @@ form div { .notbtn { cursor: default; + box-shadow: none; + border: 1px solid #ccc; } + hr { margin-top: 15px; margin-bottom: 15px; diff --git a/dashed/migrations/versions/a2d606a761d9_adding_favstar_model.py b/dashed/migrations/versions/a2d606a761d9_adding_favstar_model.py new file mode 100644 index 0000000000..16087cb82c --- /dev/null +++ b/dashed/migrations/versions/a2d606a761d9_adding_favstar_model.py @@ -0,0 +1,30 @@ +"""adding favstar model + +Revision ID: a2d606a761d9 +Revises: 430039611635 +Create Date: 2016-03-13 09:56:58.329512 + +""" + +# revision identifiers, used by Alembic. +revision = 'a2d606a761d9' +down_revision = '18e88e1cc004' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.create_table('favstar', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=True), + sa.Column('class_name', sa.String(length=50), nullable=True), + sa.Column('obj_id', sa.Integer(), nullable=True), + sa.Column('dttm', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['user_id'], ['ab_user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + + +def downgrade(): + op.drop_table('favstar') diff --git a/dashed/migrations/versions/d2424a248d63_.py b/dashed/migrations/versions/d2424a248d63_.py new file mode 100644 index 0000000000..e50d21f0be --- /dev/null +++ b/dashed/migrations/versions/d2424a248d63_.py @@ -0,0 +1,22 @@ +"""empty message + +Revision ID: d2424a248d63 +Revises: ('a2d606a761d9', '836c0bf75904') +Create Date: 2016-03-22 23:25:02.903273 + +""" + +# revision identifiers, used by Alembic. +revision = 'd2424a248d63' +down_revision = ('a2d606a761d9', '836c0bf75904') + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/dashed/models.py b/dashed/models.py index 358c167851..878b5f99f3 100644 --- a/dashed/models.py +++ b/dashed/models.py @@ -1204,3 +1204,13 @@ class DruidColumn(Model): if not m: session.add(metric) session.commit() + + +class FavStar(Model): + __tablename__ = 'favstar' + + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey('ab_user.id')) + class_name = Column(String(50)) + obj_id = Column(Integer) + dttm = Column(DateTime, default=func.now()) diff --git a/dashed/templates/dashed/dashboard.html b/dashed/templates/dashed/dashboard.html index 7eae55b0c8..73b1b4d331 100644 --- a/dashed/templates/dashed/dashboard.html +++ b/dashed/templates/dashed/dashboard.html @@ -43,6 +43,7 @@

+ {{ dashboard.dashboard_title }}

diff --git a/dashed/templates/dashed/explore.html b/dashed/templates/dashed/explore.html index 61436fd4c7..9dd7f8da13 100644 --- a/dashed/templates/dashed/explore.html +++ b/dashed/templates/dashed/explore.html @@ -42,7 +42,9 @@ {{ form.get_field("viz_type")(class_="select2") }} {% if slice %} - {{ slice.slice_name }} + + + {{ slice.slice_name }} {% if slice.description %} diff --git a/dashed/views.py b/dashed/views.py index 5c814362d3..42aff94078 100644 --- a/dashed/views.py +++ b/dashed/views.py @@ -6,7 +6,8 @@ import logging import re import traceback -from flask import request, redirect, flash, Response, render_template, Markup +from flask import ( + g, request, redirect, flash, Response, render_template, Markup) from flask.ext.appbuilder import ModelView, CompactCRUDMixin, BaseView, expose from flask.ext.appbuilder.actions import action from flask.ext.appbuilder.models.sqla.interface import SQLAInterface @@ -559,6 +560,30 @@ class Dashed(BaseView): status=500, mimetype="application/json") + @expose("/favstar////") + def favstar(self, class_name, obj_id, action): + session = db.session() + FavStar = models.FavStar + count = 0 + favs = session.query(FavStar).filter_by( + class_name=class_name, obj_id=obj_id, user_id=g.user.id).all() + if action == 'select': + if not favs: + session.add( + FavStar( + class_name=class_name, obj_id=obj_id, user_id=g.user.id, + dttm=datetime.now())) + count = 1 + elif action == 'unselect': + for fav in favs: + session.delete(fav) + else: + count = len(favs) + session.commit() + return Response( + json.dumps({'count': count}), + mimetype="application/json") + @has_access @expose("/dashboard//") def dashboard(self, dashboard_id):