From: Scott Baker Date: Tue, 20 May 2014 00:55:56 +0000 (-0700) Subject: dynamic home view with customization X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=2c3cb64a0a5f79e657afb456413e7fbc5c67903f;p=plstackapi.git dynamic home view with customization --- diff --git a/planetstack/core/admin.py b/planetstack/core/admin.py index 3b87dc8..f054e3f 100644 --- a/planetstack/core/admin.py +++ b/planetstack/core/admin.py @@ -874,6 +874,12 @@ class UserChangeForm(forms.ModelForm): # field does not have access to the initial value return self.initial["password"] +class UserDashboardViewInline(PlStackTabularInline): + model = UserDashboardView + extra = 0 + suit_classes = 'suit-tab suit-tab-dashboards' + fields = ['user', 'dashboardView', 'order'] + class UserAdmin(UserAdmin): class Meta: app_label = "core" @@ -888,7 +894,7 @@ class UserAdmin(UserAdmin): list_display = ('email', 'firstname', 'lastname', 'site', 'last_login') #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login') list_filter = ('site',) - inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline] + inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline] fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key'] fieldListContactInfo = ['firstname','lastname','phone','timezone'] @@ -896,6 +902,7 @@ class UserAdmin(UserAdmin): fieldsets = ( ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}), ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}), + #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}), #('Important dates', {'fields': ('last_login',)}), ) add_fieldsets = ( @@ -911,7 +918,12 @@ class UserAdmin(UserAdmin): user_readonly_fields = fieldListLoginDetails user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline] - suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges')) + suit_form_tabs =(('general','Login Details'), + ('contact','Contact Information'), + ('sliceprivileges','Slice Privileges'), + ('siteprivileges','Site Privileges'), + ('deploymentprivileges','Deployment Privileges'), + ('dashboards','Dashboard Views')) def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == 'site': @@ -956,7 +968,13 @@ class UserAdmin(UserAdmin): def queryset(self, request): return User.select_by_user(request.user) +class DashboardViewAdmin(PlanetStackBaseAdmin): + fieldsets = [('Dashboard View Details', + {'fields': ['name', 'url'], + 'classes': ['suit-tab suit-tab-general']}) + ] + suit_form_tabs =(('general','Dashboard View Details'),) class ServiceResourceROInline(ReadOnlyTabularInline): model = ServiceResource @@ -1378,4 +1396,5 @@ if True: #admin.site.register(SitePrivilege, SitePrivilegeAdmin) admin.site.register(Sliver, SliverAdmin) admin.site.register(Image, ImageAdmin) + admin.site.register(DashboardView, DashboardViewAdmin) diff --git a/planetstack/core/models/__init__.py b/planetstack/core/models/__init__.py index bf0015a..5dda2ed 100644 --- a/planetstack/core/models/__init__.py +++ b/planetstack/core/models/__init__.py @@ -8,7 +8,8 @@ from .tag import Tag from .role import Role #from .deployment import Deployment from .site import Site,Deployment, DeploymentRole, DeploymentPrivilege, SiteDeployments -from .user import User, UserDeployments +from .dashboard import DashboardView +from .user import User, UserDeployments, UserDashboardView from .serviceclass import ServiceClass from .slice import Slice, SliceDeployments from .site import SitePrivilege, SiteDeployments @@ -29,3 +30,4 @@ from .reservation import ReservedResource from .reservation import Reservation from .network import Network, NetworkParameterType, NetworkParameter, NetworkSliver, NetworkTemplate, Router, NetworkSlice, NetworkDeployments from .billing import Account, Invoice, Charge, UsableObject, Payment + diff --git a/planetstack/core/models/dashboard.py b/planetstack/core/models/dashboard.py new file mode 100644 index 0000000..aa79f84 --- /dev/null +++ b/planetstack/core/models/dashboard.py @@ -0,0 +1,11 @@ +import os +from django.db import models +from core.models import PlCoreBase +from django.contrib.contenttypes import generic + +class DashboardView(PlCoreBase): + name = models.CharField(max_length=200, unique=True, help_text="Name of the View") + url = models.CharField(max_length=1024, help_text="URL of Dashboard") + + def __unicode__(self): return u'%s' % (self.name) + diff --git a/planetstack/core/models/user.py b/planetstack/core/models/user.py index c4e06e0..6e7eef6 100644 --- a/planetstack/core/models/user.py +++ b/planetstack/core/models/user.py @@ -3,10 +3,11 @@ import datetime from collections import defaultdict from django.db import models from django.db.models import F, Q -from core.models import PlCoreBase,Site +from core.models import PlCoreBase,Site, DashboardView from core.models.deployment import Deployment from django.contrib.auth.models import AbstractBaseUser, BaseUserManager from timezones.fields import TimeZoneField +from operator import itemgetter, attrgetter # Create your models here. class UserManager(BaseUserManager): @@ -77,6 +78,8 @@ class User(AbstractBaseUser): timezone = TimeZoneField() + dashboards = models.ManyToManyField('DashboardView', through='UserDashboardView', blank=True) + objects = UserManager() USERNAME_FIELD = 'email' @@ -113,6 +116,20 @@ class User(AbstractBaseUser): def is_superuser(self): return False + def get_dashboards(self): + DEFAULT_DASHBOARDS=["Tenant"] + + dashboards = sorted(list(self.dashboardViews.all()), key=attrgetter('order')) + dashboards = [x.dashboardView for x in dashboards] + + if not dashboards: + for dashboardName in DEFAULT_DASHBOARDS: + dbv = DashboardView.objects.filter(name=dashboardName) + if dbv: + dashboards.append(dbv[0]) + + return dashboards + # def get_roles(self): # from core.models.site import SitePrivilege # from core.models.slice import SliceMembership @@ -163,4 +180,9 @@ class UserDeployments(PlCoreBase): else: users = Users.select_by_user(user) qs = Usereployments.objects.filter(user__in=slices) - return qs + return qs + +class UserDashboardView(PlCoreBase): + user = models.ForeignKey(User, related_name="dashboardViews") + dashboardView = models.ForeignKey(DashboardView, related_name="dashboardViews") + order = models.IntegerField(default=0) diff --git a/planetstack/core/plus/sites.py b/planetstack/core/plus/sites.py index b496481..66c5d00 100644 --- a/planetstack/core/plus/sites.py +++ b/planetstack/core/plus/sites.py @@ -12,17 +12,19 @@ class AdminMixin(object): def get_urls(self): """Add our dashboard view to the admin urlconf. Deleted the default index.""" from django.conf.urls import patterns, url - from views import DashboardView, DashboardWelcomeView, DashboardAjaxView, SimulatorView, DashboardSummaryAjaxView, DashboardAddOrRemoveSliverView, DashboardUserSiteView, DashboardAnalyticsAjaxView, TenantViewData,TenantCreateSlice, TenantAddOrRemoveSliverView, TenantPickSitesView, TenantDeleteSliceView,TenantUpdateSlice + from views import DashboardCustomize, DashboardDynamicView, DashboardWelcomeView, DashboardAjaxView, SimulatorView, DashboardSummaryAjaxView, DashboardAddOrRemoveSliverView, DashboardUserSiteView, DashboardAnalyticsAjaxView, TenantViewData,TenantCreateSlice, TenantAddOrRemoveSliverView, TenantPickSitesView, TenantDeleteSliceView,TenantUpdateSlice urls = super(AdminMixin, self).get_urls() del urls[0] custom_url = patterns('', - url(r'^$', self.admin_view(DashboardWelcomeView.as_view()), + url(r'^$', self.admin_view(DashboardDynamicView.as_view()), name="index"), url(r'^test/', self.admin_view(DashboardUserSiteView.as_view()), name="test"), - url(r'^dashboard/(?P\w+)/$', self.admin_view(DashboardView.as_view()), + url(r'^dashboard/(?P\w+)/$', self.admin_view(DashboardDynamicView.as_view()), name="dashboard"), + url(r'^customize/$', self.admin_view(DashboardCustomize.as_view()), + name="customize"), url(r'^hpcdashuserslices/', self.admin_view(DashboardUserSiteView.as_view()), name="hpcdashuserslices"), url(r'^hpcdashboard/', self.admin_view(DashboardAjaxView.as_view()), # DEPRECATED diff --git a/planetstack/core/plus/views.py b/planetstack/core/plus/views.py index 451ee6d..142911b 100644 --- a/planetstack/core/plus/views.py +++ b/planetstack/core/plus/views.py @@ -17,6 +17,7 @@ from django.http import HttpResponse, HttpResponseServerError from django.core import urlresolvers from django.contrib.gis.geoip import GeoIP from ipware.ip import get_ip +from operator import itemgetter, attrgetter import traceback import socket @@ -34,15 +35,10 @@ class DashboardWelcomeView(TemplateView): def get(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) - userDetails = getUserSliceInfo(request.user) - #context['site'] = userDetails['site'] - - context['userSliceInfo'] = userDetails['userSliceInfo'] - context['cdnData'] = userDetails['cdnData'] - context['cdnContentProviders'] = userDetails['cdnContentProviders'] + context = getDashboardContext(request.user, context) return self.render_to_response(context=context) -class DashboardView(TemplateView): +class DashboardDynamicView(TemplateView): head_template = r"""{% extends "admin/dashboard/dashboard_base.html" %} {% load admin_static %} {% block content %} @@ -50,25 +46,70 @@ class DashboardView(TemplateView): tail_template = r"{% endblock %}" - def get(self, request, name="hpc_historical", *args, **kwargs): + def get(self, request, name="root", *args, **kwargs): context = self.get_context_data(**kwargs) + context = getDashboardContext(request.user, context) + + if name=="root": + return self.multiDashboardView(request, context) + else: + return self.singleDashboardView(request, name, context) + def readDashboard(self, fn): + try: + template= open("/opt/planetstack/templates/admin/dashboard/%s.html" % fn, "r").read() + if (fn=="tenant"): + template = '
' + template + return template + except: + return "failed to open %s" % fn + + def multiDashboardView(self, request, context): head_template = self.head_template tail_template = self.tail_template - if (name=="tenant"): - # quick fix for tenant view - head_template = head_template + '
' + body = """ +
+ \n" - t = template.Template(head_template + open("/opt/planetstack/templates/admin/dashboard/%s.html" % name, "r").read() + self.tail_template) + for i,view in enumerate(dashboards): + url = view.url + body = body + '
\n' % i + if url.startswith("template:"): + fn = url[9:] + body = body + self.readDashboard(fn) + body = body + '
\n' + + body=body+"
\n" + + t = template.Template(head_template + body + self.tail_template) + + response_kwargs = {} + response_kwargs.setdefault('content_type', self.content_type) + return self.response_class( + request = request, + template = t, + context = context, + **response_kwargs) - userDetails = getUserSliceInfo(request.user) - #context['site'] = userDetails['site'] + def singleDashboardView(self, request, name, context): + head_template = self.head_template + tail_template = self.tail_template - context['userSliceInfo'] = userDetails['userSliceInfo'] - context['cdnData'] = userDetails['cdnData'] - context['cdnContentProviders'] = userDetails['cdnContentProviders'] + t = template.Template(head_template + self.readDashboard(fn) + self.tail_template) response_kwargs = {} response_kwargs.setdefault('content_type', self.content_type) @@ -78,18 +119,36 @@ class DashboardView(TemplateView): context = context, **response_kwargs) -def getUserSliceInfo(user, tableFormat = False): - userDetails = {} +def getDashboardContext(user, context={}, tableFormat = False): + context = {} userSliceData = getSliceInfo(user) if (tableFormat): -# pprint("******* GET USER SLICE INFO") - userDetails['userSliceInfo'] = userSliceTableFormatter(userSliceData) + context['userSliceInfo'] = userSliceTableFormatter(userSliceData) else: - userDetails['userSliceInfo'] = userSliceData - userDetails['cdnData'] = getCDNOperatorData(wait=False) - userDetails['cdnContentProviders'] = getCDNContentProviderData() - return userDetails + context['userSliceInfo'] = userSliceData + context['cdnData'] = getCDNOperatorData(wait=False) + context['cdnContentProviders'] = getCDNContentProviderData() + + (dashboards, unusedDashboards)= getDashboards(user) + unusedDashboards=[x for x in unusedDashboards if x!="Customize"] + context['dashboards'] = dashboards + context['unusedDashboards'] = unusedDashboards + + return context + +def getDashboards(user): + #dashboards = sorted(list(user.dashboardViews.all()), key=attrgetter('order')) + dashboards = user.get_dashboards() + + dashboard_names = [d.name for d in dashboards] + + unused_dashboard_names = [] + for dashboardView in DashboardView.objects.all(): + if not dashboardView.name in dashboard_names: + unused_dashboard_names.append(dashboardView.name) + + return (dashboard_names, unused_dashboard_names) class TenantCreateSlice(View): def post(self, request, *args, **kwargs): @@ -416,7 +475,7 @@ class SimulatorView(View): class DashboardUserSiteView(View): def get(self, request, **kwargs): - return HttpResponse(json.dumps(getUserSliceInfo(request.user, True)), mimetype='application/javascript') + return HttpResponse(json.dumps(getDashboardContext(request.user, tableFormat=True)), mimetype='application/javascript') class TenantViewData(View): def get(self, request, **kwargs): @@ -654,7 +713,7 @@ class DashboardAnalyticsAjaxView(View): if (name == "hpcSummary"): return HttpResponse(json.dumps(hpc_wizard.get_hpc_wizard().get_summary_for_view()), mimetype='application/javascript') elif (name == "hpcUserSite"): - return HttpResponse(json.dumps(getUserSliceInfo(request.user, True)), mimetype='application/javascript') + return HttpResponse(json.dumps(getDashboardContext(request.user, tableFormat=True)), mimetype='application/javascript') elif (name == "hpcMap"): return HttpResponse(json.dumps(getCDNOperatorData(True)), mimetype='application/javascript') elif (name == "bigquery"): @@ -663,3 +722,21 @@ class DashboardAnalyticsAjaxView(View): else: return HttpResponse(json.dumps("Unknown"), mimetype='application/javascript') +class DashboardCustomize(View): + def post(self, request, *args, **kwargs): + dashboards = request.POST.get("dashboards", None) + if not dashboards: + return HttpResponse("no data") + + dashboards = [x.strip() for x in dashboards.split(",")] + + dashboards = [DashboardView.objects.get(name=x) for x in dashboards] + + request.user.dashboardViews.all().delete() + + for i,dashboard in enumerate(dashboards): + udbv = UserDashboardView(user=request.user, dashboardView=dashboard, order=i) + udbv.save() + + return HttpResponse("updated") + diff --git a/planetstack/core/static/planetstack.css b/planetstack/core/static/planetstack.css index 39102be..234261c 100644 --- a/planetstack/core/static/planetstack.css +++ b/planetstack/core/static/planetstack.css @@ -1141,4 +1141,12 @@ display:none; #private-vol{ margin-right: 15% !important; -} \ No newline at end of file +} + +.customize_row { + display: table; +} +.customize_column { + display: table-cell; + padding: 10px; +} diff --git a/planetstack/templates/admin/dashboard/customize.html b/planetstack/templates/admin/dashboard/customize.html new file mode 100644 index 0000000..3f1d2c6 --- /dev/null +++ b/planetstack/templates/admin/dashboard/customize.html @@ -0,0 +1,85 @@ +
+
+
+
Available Dashboard Views
+ +
+
+
+
Add »


+
« Remove
+
+
+
Selected Dashboard Views
+ +
+
Save
+
+
+
+
Up


+
Down
+
+
+
+ + +