Source code for smo.web.view

from django.http import JsonResponse
from django.shortcuts import render_to_response, RequestContext
from SmoWeb.settings import JINJA_TEMPLATE_IMPORTS, db
import json
import traceback
import logging
#from smo.data.hdf import HDFInterface
from SmoWebBase.tasks import celeryCompute
from celery.result import AsyncResult
import h5py
import numpy as np
logger = logging.getLogger('django.request.smo.view')

from bson.objectid import ObjectId

[docs]class Action(object): """ Abstract base class for all action types """ def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): result = self.func(*args, **kwargs) return result
[docs]class PostAction(Action): """ General POST action """
class action(object): @staticmethod def post(**kwargs): def postActionDecorator(func): action = PostAction(func) action.label = "" if (func.__doc__ is not None): action.label = func.__doc__ return action return postActionDecorator
[docs]class ModularPageViewMeta(type): """ Metaclass facilitation the creation of modular page views """ def __new__(cls, name, bases, attrs): # Label if ('label' not in attrs): attrs['label'] = name if ('showInMenu' not in attrs): attrs['showInMenu'] = True if ('controllerName' not in attrs): attrs['controllerName'] = name + 'Controller' # Containers to collect actions, library and module names postActions = {} requiredJSLibraries = set() requiredGoogleModules = set() new_class = (super(ModularPageViewMeta, cls) .__new__(cls, name, bases, attrs)) for c in reversed(new_class.__mro__): for key, value in c.__dict__.iteritems(): if (isinstance(value, PostAction)): postActions[key] = value elif (key == 'requireJS'): requiredJSLibraries.update(c.requireJS) elif (key == 'requireGoogle'): requiredGoogleModules.update(c.requireGoogle) if (len(requiredGoogleModules) > 0): requiredJSLibraries.update(['GoogleAPI']) if (new_class.__doc__ is None): new_class.__doc__ = "" new_class.__doc__ += '\n' + ''.join( [' * :attr:`{0}`: {1}\n'.format(name, action.label.strip()) for name, action in postActions.iteritems()] ) new_class.postActions = postActions new_class.requiredJSLibraries = requiredJSLibraries new_class.requiredGoogleModules = requiredGoogleModules return new_class
[docs]class ModularPageView(object): """ Abstract base class for creating pages consisting of modules. There are different kind of modules: * NumericalModel model-views * Restructured text views * HTML module views :param: :class:`smo.web.router.ViewRouter` router: a router instance with which the view is registered Class attributes: * :attr:`label`: label for the page view class (default is the page view class name) * :attr:`controllerName`: name of the AngularJS contoller for the page view (default is the page view class name + 'Controller') * :attr:`showInMenu`: used to specify if the page is to show in the navigation bar menus * :attr:`modules`: modules making up the page view * :attr:`injectVariables`: list of names of AngularJS dependencies required for the page view * :attr:`jsLibraries`: registry of common Java Script libraries used in the applicaions * :attr:`googleModules`: registry of common Google modules used in the applicaions * :attr:`requireJS`: list of JS libraries required by the page view * :attr:`requireGoogle`: list of Google modules required by the page view * :attr:`template`: HTML template file Defined POST actions:""" __metaclass__ = ModularPageViewMeta jsLibraries = { 'dygraph': '/static/dygraph/dygraph-combined.js', 'dygraphExport': 'http://cavorite.com/labs/js/dygraphs-export/dygraph-extra.js', 'MathJax': "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML", 'GoogleAPI': 'https://www.google.com/jsapi' } googleModules = { 'visualization':{'version': '1.0', 'packages': ["corechart", "table", "controls"]} } requireJS = ['MathJax'] requireGoogle = [] template = 'SmoWebBase/ModelViewTemplate.jinja' def __init__(self, router): self.router = router
[docs] def view(self, request): """ Entry function for processing an HTTP request """ if request.method == 'GET': logger.debug('GET ' + request.path) return self.get(request) elif request.method == 'POST': logger.debug('POST ' + request.path) return self.post(request) else: raise ValueError('Only GET and POST requests can be served')
[docs] def get(self, request): """ Function handling HTTP GET request """ parameters = request.GET modelView = None self.recordIdDict = {} if (hasattr(self, 'modules') and len(self.modules) > 0): self.activeModule = None else: raise ValueError('This page defines no modules') if ('model' in parameters): modelName = parameters['model'] # find the active module for module in self.modules: if (module.__name__ == modelName): self.activeModule = module break if (self.activeModule is None): raise ValueError("Unknown module {0}".format(modelName)) else: if ('view' in parameters): # find the active view viewName = parameters['view'] for block in self.activeModule.modelBlocks: if (block.name == viewName): modelView = block break if (modelView is None): raise ValueError("Unknown view {0} in module {1}".format(viewName, modelName)) if ('id' in parameters): recordId = parameters['id'] self.recordIdDict[modelView] = recordId else: self.activeModule = self.modules[0] context = {"pageView": self} context.update(JINJA_TEMPLATE_IMPORTS) context[self.router.name] = self.router.appNamespace return render_to_response(self.template, context, context_instance=RequestContext(request))
[docs] def post(self, request): """ Function handling HTTP POST request """ postData = json.loads(request.body) action = postData['action'] data = postData['data'] # Get the action parameters parameters = {} if ('parameters' in data): parameters = data['parameters'] # Get the calling model view model = None if ("modelName" in data): modelName = data['modelName'] for module in self.modules: if (module.__name__ == modelName): model = module if (model is None): raise ValueError("Unknown model {0}".format(modelName)) modelView = None if (model is not None and "viewName" in data): viewName = data['viewName'] for block in model.modelBlocks: if (block.name == viewName): modelView = block if (modelView is None): raise ValueError("Unknown model view {0}.{1}".format(modelName, viewName)) logger.debug('Action: ' + action) logger.debug('Parameters: ' + json.dumps(parameters)) return self.execPostAction(action, model, modelView, parameters)
[docs] def execPostAction(self, actionName, model, view, parameters): """ Executes a specific POST action registered with this view """ response = {} if (actionName in self.postActions.keys()): try: response['data'] = self.postActions[actionName](self, model, view, parameters) response['errStatus'] = False except Exception, e: response['errStatus'] = True response['error'] = str(e) response['stackTrace'] = traceback.format_exc() else: response['errStatus'] = True response['error'] = 'No POST action with name {0}'.format(actionName) return JsonResponse(response)
@action.post() def load(self, model, view, parameters): """ Action for loading model data """ instance = model() if 'viewRecordId' in parameters: viewRecordId = parameters['viewRecordId'] coll = db.savedViewData record = coll.find_one({'_id': ObjectId(viewRecordId)}) if (record is not None): instance.fieldValuesFromJson(record['values']) else: raise ValueError("Unknown view data record with id: {0}".format(viewRecordId)) return instance.modelView2Json(view) @action.post() def save(self, model, view, parameters): """ Action for saving data in the DB """ coll = db.savedViewData recordId = coll.insert({'values' : parameters}) return {'model': model.__name__, 'view': view.name, 'id' : str(recordId)} @action.post() def compute(self, model, view, parameters): """ Predefined compute action """ instance = model() instance.fieldValuesFromJson(parameters) instance.compute() return instance.modelView2Json(view) @action.post() def loadEg(self, model, view, parameters): instance = model() getattr(instance, parameters)() return instance.modelView2Json(view) @action.post() def loadHdfValues(self, model, view, parameters): # List of dict of storage field names and data to be returned to the client resultList = [] for field in parameters: # Looping over received data about storage fields fieldDict = {} h5File = h5py.File(field['hdfFile'], 'r') datasetPath = field['hdfGroup'] + '/' + field['dataset'] if field['datasetColumns'] is None: fieldDict[field['name']] = h5File[datasetPath][...].tolist() else: fieldDict[field['name']] = np.array(h5File[datasetPath][tuple(field['datasetColumns'])]).transpose().tolist() h5File.close() resultList.append(fieldDict) return resultList @action.post() def startCompute(self, model, view, parameters): job = celeryCompute.delay(model.__name__, view.name, parameters) if (job.failed()): raise job.result return {'jobID': job.id, 'fractionOutput': model.progressOptions['fractionOutput'], 'suffix': model.progressOptions['suffix'], 'state': job.state } @action.post() def checkProgress(self, model, view, parameters): job = AsyncResult(parameters['jobID']) state = job.state if (state == 'FAILURE'): raise job.result responseDict = {'state': job.state} if (state == 'SUCCESS'): responseDict.update(job.result) elif (state == 'PROGRESS'): responseDict.update({'current': job.info['current'], 'total': job.info['total']}) return responseDict @action.post() def abort(self, model, view, parameters): if (model.async == True): job = AsyncResult(parameters['jobID']) job.revoke(terminate=True) return {'state': job.state} else: return {} @classmethod
[docs] def asView(cls): """ Can be used if a function-like object is needed instead of a class instance. """ def view(request): instance = cls() return instance.view(request) return view
if __name__ == '__main__': pass