From 94fce5df0cba142f961f780ba20884a03b9829a5 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Fri, 11 Apr 2014 23:08:31 +0530 Subject: [PATCH 01/96] Start restructuring the SILPA Code was previously started as a proof of concept and was then directly extended and deployed. Extending it more will be a problem so lets start reorganizing the code. Signed-off-by: Vasudev Kamath --- core/__init__.py | 2 -- core/jsonrpchandler.py => silpa/api/jsonrpc.py | 0 silpa.py => silpa/frontend/main.py | 0 loadconfig.py => silpa/loadconfig.py | 0 {core => silpa}/modulehelper.py | 0 webbridge.py => silpa/webbridge.py | 0 6 files changed, 2 deletions(-) delete mode 100755 core/__init__.py rename core/jsonrpchandler.py => silpa/api/jsonrpc.py (100%) mode change 100755 => 100644 rename silpa.py => silpa/frontend/main.py (100%) rename loadconfig.py => silpa/loadconfig.py (100%) rename {core => silpa}/modulehelper.py (100%) mode change 100755 => 100644 rename webbridge.py => silpa/webbridge.py (100%) diff --git a/core/__init__.py b/core/__init__.py deleted file mode 100755 index 4118946..0000000 --- a/core/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from jsonrpchandler import * -from modulehelper import * diff --git a/core/jsonrpchandler.py b/silpa/api/jsonrpc.py old mode 100755 new mode 100644 similarity index 100% rename from core/jsonrpchandler.py rename to silpa/api/jsonrpc.py diff --git a/silpa.py b/silpa/frontend/main.py similarity index 100% rename from silpa.py rename to silpa/frontend/main.py diff --git a/loadconfig.py b/silpa/loadconfig.py similarity index 100% rename from loadconfig.py rename to silpa/loadconfig.py diff --git a/core/modulehelper.py b/silpa/modulehelper.py old mode 100755 new mode 100644 similarity index 100% rename from core/modulehelper.py rename to silpa/modulehelper.py diff --git a/webbridge.py b/silpa/webbridge.py similarity index 100% rename from webbridge.py rename to silpa/webbridge.py From 3bc5ad2b617863b938992671540d8eb8c5b3f366 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Fri, 11 Apr 2014 23:09:38 +0530 Subject: [PATCH 02/96] JSONRPC refactoring We were using JSON-RPC 1.0 previously, new code starts using JSON-RPC 2.0 instead, code is being written from scratch. Signed-off-by: Vasudev Kamath --- silpa/api/__init__.py | 14 ++++ silpa/api/jsonrpc.py | 157 ++++++++++++++++-------------------------- 2 files changed, 74 insertions(+), 97 deletions(-) create mode 100644 silpa/api/__init__.py diff --git a/silpa/api/__init__.py b/silpa/api/__init__.py new file mode 100644 index 0000000..399c7fd --- /dev/null +++ b/silpa/api/__init__.py @@ -0,0 +1,14 @@ +from flask import jsonify +from functools import wraps + + +def route(bp, *args, **kwargs): + def decorator(f): + @bp.route(*args, **kwargs) + @wraps(f) + def wrapper(*args, **kwargs): + sc = 200 + result = f(*args, **kwargs) + return jsonify(result), sc + return f + return decorator diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index 10ee409..e16aa9c 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -1,102 +1,65 @@ -from json import loads, dumps -from modulehelper import modules, modulenames, MODULES, enabled_modules, \ - load_modules - - -class JSONRPCHandlerException(Exception): - pass - - -class JSONRequestNotTranslatable(JSONRPCHandlerException): - pass - - -class BadServiceRequest(JSONRPCHandlerException): - pass - - -class MethodNotFoundException(JSONRPCHandlerException): - def __init__(self, name): - self.methodname = name - +from collections import namedtuple +from flask import Blueprint, request +from . import route +import json + +JsonRpcError = namedtuple('JsonRpcError', ['code', 'message', 'data']) +JsonRpcRequest = namedtuple('JsonRpcRequest', + ['jsonrpc', 'method', 'params', 'id']) +JsonRpcErrorResponse = namedtuple('JsonRpcErrorResponse', + ['jsonrpc', 'error', 'id']) +JsonRpcResultResponse = namedtuple('JsonRpcResultResponse', + ['jsonrpc', 'result', 'id']) + +_PARSE_ERRORS = -32700 +_INVALID_REQUEST = -32600 +_METHOD_NOT_FOUND = -32601 +_INVALID_PARAMS = -32602 +_INTERNAL_ERROR = -32603 + + +bp = Blueprint('JSONRPC', __name__, url_prefix='/JSONRPC') + + +@route(bp, '/JSONRPC', methods=['POST']) +def handle_jsonrpc_call(): + if request.data is not None: + rpc_object = JsonRpc(request.data) + + if rpc_object.error_response is not None: + # There was a error, translate and return the dictionary + # object for client + return dict(zip(rpc_object.error_response._fields)) + else: + # there was no problem constructing request lets process + # the call + pass -class JSONRPCHandler(object): - def __init__(self): - ''' - This should be only once called. Atleast my assumption - ''' - load_modules() +class JsonRpc(object): + __slots__ = ['request', 'response', 'error_response'] - def translate_request(self, data): + def __init__(self, data): + self.error_response = None try: - req = loads(data) - except: - raise JSONRequestNotTranslatable - return req - - def translate_result(self, result, error, id_): - if error != None: - error = {"name": error.__class__.__name__, "message": error} - result = None - - try: - data = dumps({"result": result, "id": id_, "error": error}) - except: - error = {"name": "JSONEncodeException", \ - "message": "Result object is not serializable"} - data = dumps({"result": None, "id": id_, "error": error}) - - return data - - def call(self, method, args): - _args = None - for arg in args: - if arg != '': - if _args == None: - _args = [] - _args.append(arg) - - if _args == None: - # No arguments - return method() + self.request = JsonRpcRequest(**json.loads(data)) + except Exception as e: + # Unable to parse json + error = JsonRpcError(code=_PARSE_ERRORS, message=e.message, + data="") + self.errors_response = JsonRpcErrorResponse(jsonrpc="2.0", + error=error, id='') else: - return method(*_args) - - def handle_request(self, json): - err = None - meth = None - id_ = '' - result = None - args = None - - try: - req = self.translate_request(json) - except JSONRequestNotTranslatable, e: - err = e - req = {'id': id_} - - if err == None: - try: - id_ = req['id'] - meth = req['method'] - try: - args = req['params'] - except: - pass - except: - err = BadServiceRequest(json) - - module_instance = None - if err == None: - try: - module_instance = MODULES.get(meth.split('.')[0]) - except: - err = MethodNotFoundException(meth.split('.')[-1]) - - method = None - if err == None: - result = self.call(getattr(module_instance, \ - meth.split('.')[-1]), args) - - return self.translate_result(result, err, id_) + # successfully parsed now verify request + if self.request.jsonrpc != "2.0" or len(self.request.method) == 0 \ + or len(self.request.params) == 0 or len(self.id) == 0: + # not valid request + error = JsonRpcError(code=_INVALID_REQUEST, + message="Not a valid JSON-RPC request", + data='') + self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", + error=error, id='') + + def __call__(self): + # process request here + pass From ddd9a80864980ba46f946e1d4f045c88b120b6a7 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Fri, 11 Apr 2014 23:10:19 +0530 Subject: [PATCH 03/96] old jsonrpc code for reference, will be deleted once refactoring is done. --- silpa/api/jsonrpc_old.py | 102 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 silpa/api/jsonrpc_old.py diff --git a/silpa/api/jsonrpc_old.py b/silpa/api/jsonrpc_old.py new file mode 100644 index 0000000..10ee409 --- /dev/null +++ b/silpa/api/jsonrpc_old.py @@ -0,0 +1,102 @@ +from json import loads, dumps +from modulehelper import modules, modulenames, MODULES, enabled_modules, \ + load_modules + + +class JSONRPCHandlerException(Exception): + pass + + +class JSONRequestNotTranslatable(JSONRPCHandlerException): + pass + + +class BadServiceRequest(JSONRPCHandlerException): + pass + + +class MethodNotFoundException(JSONRPCHandlerException): + def __init__(self, name): + self.methodname = name + + +class JSONRPCHandler(object): + + def __init__(self): + ''' + This should be only once called. Atleast my assumption + ''' + load_modules() + + def translate_request(self, data): + try: + req = loads(data) + except: + raise JSONRequestNotTranslatable + return req + + def translate_result(self, result, error, id_): + if error != None: + error = {"name": error.__class__.__name__, "message": error} + result = None + + try: + data = dumps({"result": result, "id": id_, "error": error}) + except: + error = {"name": "JSONEncodeException", \ + "message": "Result object is not serializable"} + data = dumps({"result": None, "id": id_, "error": error}) + + return data + + def call(self, method, args): + _args = None + for arg in args: + if arg != '': + if _args == None: + _args = [] + _args.append(arg) + + if _args == None: + # No arguments + return method() + else: + return method(*_args) + + def handle_request(self, json): + err = None + meth = None + id_ = '' + result = None + args = None + + try: + req = self.translate_request(json) + except JSONRequestNotTranslatable, e: + err = e + req = {'id': id_} + + if err == None: + try: + id_ = req['id'] + meth = req['method'] + try: + args = req['params'] + except: + pass + except: + err = BadServiceRequest(json) + + module_instance = None + if err == None: + try: + module_instance = MODULES.get(meth.split('.')[0]) + except: + err = MethodNotFoundException(meth.split('.')[-1]) + + method = None + if err == None: + result = self.call(getattr(module_instance, \ + meth.split('.')[-1]), args) + + return self.translate_result(result, err, id_) From 7ee3c0d7d5c3217614c28f44e463cd5aa91d1f03 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 11:29:17 +0530 Subject: [PATCH 04/96] Move config file into silpa application directory Signed-off-by: Vasudev Kamath --- silpa.conf => silpa/silpa.conf | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename silpa.conf => silpa/silpa.conf (100%) diff --git a/silpa.conf b/silpa/silpa.conf similarity index 100% rename from silpa.conf rename to silpa/silpa.conf From 80c00aa1f31fe740577158ac2668870388711d61 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 11:29:51 +0530 Subject: [PATCH 05/96] improve modulehelper module Use importlib module to import modules, these modules will be accessible to applications from sys.modules so drop local caching of modules. Signed-off-by: Vasudev Kamath --- silpa/modulehelper.py | 55 +++++++++---------------------------------- 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/silpa/modulehelper.py b/silpa/modulehelper.py index 7f6513f..6871204 100644 --- a/silpa/modulehelper.py +++ b/silpa/modulehelper.py @@ -4,52 +4,19 @@ initialized multiple times ''' -_all_ = ['MODULES', 'modules', 'modulenames', 'enabled_modules', \ - 'load_modules', 'BASEURL'] +from loadconfig import config +from flask import globals -import loadconfig -import sys +import importlib - -MODULES = {} -BASEURL = loadconfig.get('baseurl') - -modules = loadconfig.get('modules') -modulenames = loadconfig.get('modules_display') - -enabled_modules = [modulenames[x] for x in modules.keys() \ - if modules[x] == "yes"] +_modules = [module for module, need in config.items("modules") + if need == "yes"] def load_modules(): - ''' - Load the modules enabled in the configuration file - by user. This function initializes global variable - MODULES which is a dictionary having module name as - key and module itself as value. Which can be used later - to process requests coming for the modules. - ''' - - # Already initialized the modules then don't do it again - if len(MODULES) != 0: - return - - for key in modules.keys(): - if modules.get(key) == 'yes': - mod = None - try: - mod = sys.modules[key] - if not type(mod).__name__ == 'module': - raise KeyError - except KeyError: - try: - mod = __import__(key, globals(), locals(), []) - except ImportError: - # Since we can't use logger from flask as its not yet - # activated we will write it to sys.stderr this should - # go to webserver error.log - print >> sys.stderr, "Failed to import module {0}"\ - .format(key) - pass - if mod: - MODULES[key] = mod.getInstance() + app = globals.current_app + for module in _modules: + try: + importlib.import_module(module) + except ImportError: + app.logger.error("Failed to import {module}".format(module=module)) From f6eafb16b22293c79c18ca81f24f91a973a25ddb Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 11:30:51 +0530 Subject: [PATCH 06/96] Better config parsing Derive from ConfigParser.ConfigParser our config parsing class, and create a config variable which reads config file when module is first imported. --- silpa/loadconfig.py | 63 ++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/silpa/loadconfig.py b/silpa/loadconfig.py index 3aa8d26..0fc2ada 100644 --- a/silpa/loadconfig.py +++ b/silpa/loadconfig.py @@ -1,36 +1,51 @@ -from ConfigParser import RawConfigParser -import os - -_all_ = ['get'] +__all__ = ['IncompleteConfigError', 'config'] +import ConfigParser +import os -class _SilpaConfig: - def __init__(self): - _config = RawConfigParser() - _config.read(os.path.join(os.path.dirname(__file__), 'silpa.conf')) +class IncompleteConfigError(Exception): + def __init__(self, section, option): + self.section = section + self.option = option - self.site_name = _config.get('main', 'site') - self.baseurl = _config.get('main', 'baseurl') + def __str__(self): + if self.option is not None: + return ">> Missing option {option} in section {section}" + \ + "of config file".format(option=self.option, + section=self.section) + else: + return ">> Missiong section {section} in config file".format( + section=self.section) - self.log_level = _config.get('logging', 'log_level') - folder = _config.get('logging', 'log_folder') - self.log_folder = folder if folder != "." else os.getcwd() +class _Config(ConfigParser.ConfigParser): + _instance = None - name = _config.get('logging', 'log_name') - self.log_name = name if name else 'silpa.log' + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(_Config, cls).__new__(cls, *args, **kwargs) + return cls._instance - self.modules = {} - for module, status in _config.items('modules'): - self.modules[module] = status if status else "no" + def __init__(self, location="silpa.conf"): + ConfigParser.ConfigParser.__init__(self) + self.read(location) + self.verify - self.modules_display = {} - for module, name in _config.items('module_display'): - self.modules_display[module] = name + def verify(self): + self._verify_item("main", "site") + self._verify_item("main", "baseurl") + self._verify_item("logging", "log_level") + self._verify_item("logging", "log_folder") + self._verify_item("logging", "log_name") + if not self.has_section("modules"): + raise IncompleteConfigError("modules", None) -_config = _SilpaConfig() + if not self.has_section("modules_display"): + raise IncompleteConfigError("modules_display") + def _verify_item(self, section, option): + if not self.has_option(section, option): + raise IncompleteConfigError(section, option) -def get(key): - return _config.__dict__[key] +config = _Config(os.path.join(os.path.dirname(__file__), "silpa.conf")) From 8648c352c05e55fc4b18a76e6d1ae74b948b4118 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 13:29:13 +0530 Subject: [PATCH 07/96] Renamed modulehelper to moduleloader Now new name reflects what module is actually doing Signed-off-by: Vasudev Kamath --- silpa/modulehelper.py | 22 ---------------------- silpa/moduleloader.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 22 deletions(-) delete mode 100644 silpa/modulehelper.py create mode 100644 silpa/moduleloader.py diff --git a/silpa/modulehelper.py b/silpa/modulehelper.py deleted file mode 100644 index 6871204..0000000 --- a/silpa/modulehelper.py +++ /dev/null @@ -1,22 +0,0 @@ -''' - Purpose of this file is to hold variables which is used - across the application so that these variables don't get - initialized multiple times -''' - -from loadconfig import config -from flask import globals - -import importlib - -_modules = [module for module, need in config.items("modules") - if need == "yes"] - - -def load_modules(): - app = globals.current_app - for module in _modules: - try: - importlib.import_module(module) - except ImportError: - app.logger.error("Failed to import {module}".format(module=module)) diff --git a/silpa/moduleloader.py b/silpa/moduleloader.py new file mode 100644 index 0000000..1d8db9a --- /dev/null +++ b/silpa/moduleloader.py @@ -0,0 +1,16 @@ +from loadconfig import config +from flask import current_app + +import importlib + +_modules = [module for module, need in config.items("modules") + if need == "yes"] + + +def load_modules(): + for module in _modules: + try: + importlib.import_module(module) + except ImportError: + current_app.logger.error( + "Failed to import {module}".format(module=module)) From f0b4f6717d72433040f8ada75fb8404bad6edb3f Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 13:31:11 +0530 Subject: [PATCH 08/96] Add method invoking logic to JSON-RPC Logic for invoking requested method on a module is added, if module doesn't have a method by the requested name a METHOD_NOT_FOUND error is returned to client as per JSON-RPC 2.0 specifications. If there is error in execution of call, INTERNAL_ERROR is returned to client as per JSON-RPC spec and message is set appropriately to reflect what happened. Signed-off-by: Vasudev Kamath --- silpa/api/jsonrpc.py | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index e16aa9c..a9b493f 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -2,6 +2,7 @@ from flask import Blueprint, request from . import route import json +import sys JsonRpcError = namedtuple('JsonRpcError', ['code', 'message', 'data']) JsonRpcRequest = namedtuple('JsonRpcRequest', @@ -29,11 +30,21 @@ def handle_jsonrpc_call(): if rpc_object.error_response is not None: # There was a error, translate and return the dictionary # object for client - return dict(zip(rpc_object.error_response._fields)) + return dict(zip(rpc_object.error_response._fields, + rpc_object.error_response)) else: # there was no problem constructing request lets process # the call - pass + try: + rpc_object() + except Exception as e: + # Possible errors in execution of method + error = JsonRpcError(code=_INTERNAL_ERROR, message=e.message, + data=dict(zip(rpc_object.request._fields, + rpc_object.request))) + return dict(jsonrpc="2.0", + error=dict(zip(error._fields, error), + id=rpc_object.request.id)) class JsonRpc(object): @@ -62,4 +73,27 @@ def __init__(self, data): def __call__(self): # process request here - pass + module, method = self.request.method.split('.') + + if module not in sys.modules: + # Module is not yet loaded? handle this + pass + else: + # module is present in sys + mod = sys.modules[module] + if not hasattr(mod, method) and \ + type(getattr(mod, method).__name__ == 'function'): + # method not found + error = JsonRpcError(code=_METHOD_NOT_FOUND, + message="requested method not found", + data="Requested method {}".format( + self.request.method)) + self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", + error=error, + id=self.request.id) + else: + # Method present so lets call it to get result + result = getattr(module, method)(*self.request.params) + self.response = JsonRpcResultResponse(jsonrpc="2.0", + result=result, + id=self.request.id) From 0f32cc35b4094af6b22af52c2916e3979123f070 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 20:04:15 +0530 Subject: [PATCH 09/96] Wrie app factory and fix http return code introduce create_app function as factory function, and on error in jsonrpc call return 400 error code instead of 200. Signed-off-by: Vasudev Kamath --- silpa/api/__init__.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/silpa/api/__init__.py b/silpa/api/__init__.py index 399c7fd..2cc2b7d 100644 --- a/silpa/api/__init__.py +++ b/silpa/api/__init__.py @@ -1,5 +1,8 @@ from flask import jsonify from functools import wraps +from .. import factory + +import os def route(bp, *args, **kwargs): @@ -7,8 +10,18 @@ def decorator(f): @bp.route(*args, **kwargs) @wraps(f) def wrapper(*args, **kwargs): - sc = 200 result = f(*args, **kwargs) + sc = 200 if 'error' not in result else 400 return jsonify(result), sc return f return decorator + + +def create_app(settings_override=None): + app = factory.create_app(__name__, os.path.dirname(__file__), + settings_override) + app.errorhandler(404)(on_404) + + +def on_404(e): + return jsonify(dict(error='Not Found')), 404 From 462db0845e0b189860d6f910f59555180670c3ed Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 20:05:32 +0530 Subject: [PATCH 10/96] settings file for silpa Signed-off-by: Vasudev Kamath --- silpa/settings.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 silpa/settings.py diff --git a/silpa/settings.py b/silpa/settings.py new file mode 100644 index 0000000..078f2ca --- /dev/null +++ b/silpa/settings.py @@ -0,0 +1,5 @@ +# Do not edit this file use your own settings.py for testing +# and for production create a file called settings.cfg +DEBUG = False +TESTING = False +JSON_AS_ASCII = False From 6dc5b3b2f11e88663b9ccf387d09e5234e737290 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 20:05:45 +0530 Subject: [PATCH 11/96] Factory for SILPA app creation app factory for SILPA creates app and registers its blueprints and sets all settings for the app before returning it. Signed-off-by: Vasudev Kamath --- silpa/factory.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 silpa/factory.py diff --git a/silpa/factory.py b/silpa/factory.py new file mode 100644 index 0000000..0c05427 --- /dev/null +++ b/silpa/factory.py @@ -0,0 +1,26 @@ +from flask import Flask, Blueprint + +import pkgutil +import importlib + + +def register_blueprints(app, package_name, package_path): + rv = [] + for _, name, _ in pkgutil.iter_modules(package_path): + m = importlib.import_module('{package}.{module}'.format( + package=package_name, module=name)) + for item in dir(m): + item = getattr(m, item) + if isinstance(item, Blueprint): + app.register_blueprint(item) + rv.append(item) + return rv + + +def create_app(package_name, package_path, settings_override=None): + app = Flask(package_name, instance_relative_config=True) + app.config.from_object("silpa.settings") + app.config.from_pyfile('settings.cfg', silent=True) + app.config.from_object(settings_override) + + register_blueprints(app, package_name, package_path) From f3e04e7585c22d11e4e60b88c5f6f5cd18382205 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 20:22:09 +0530 Subject: [PATCH 12/96] Convert SILPA application into package --- silpa/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 silpa/__init__.py diff --git a/silpa/__init__.py b/silpa/__init__.py new file mode 100644 index 0000000..eab6a15 --- /dev/null +++ b/silpa/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +''' + SILPA + ~~~~~ + + SILPA application package +''' From 8484053b305e3edc80b2b7c04d8c334d16a43110 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 20:22:20 +0530 Subject: [PATCH 13/96] In app factory itself load modules needed by application --- silpa/api/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/silpa/api/__init__.py b/silpa/api/__init__.py index 2cc2b7d..9fc2dcb 100644 --- a/silpa/api/__init__.py +++ b/silpa/api/__init__.py @@ -1,6 +1,7 @@ from flask import jsonify from functools import wraps from .. import factory +from .. import moduleloader import os @@ -22,6 +23,9 @@ def create_app(settings_override=None): settings_override) app.errorhandler(404)(on_404) + # Load enabled modules from the config + moduleloader.load_modules() + def on_404(e): return jsonify(dict(error='Not Found')), 404 From 9f3402df32076a81cc23fccd78071e1bdb48defe Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 12 Apr 2014 20:23:59 +0530 Subject: [PATCH 14/96] Move templates and static folders into frontend module Signed-off-by: Vasudev Kamath --- .../frontend/static}/css/bootstrap.min.css | 0 .../frontend/static}/css/jquery.ime.css | 0 {static => silpa/frontend/static}/css/main.css | 0 {static => silpa/frontend/static}/doc/apis.html | 0 {static => silpa/frontend/static}/doc/contact.html | 0 .../frontend/static}/doc/contribute.html | 0 {static => silpa/frontend/static}/doc/credits.html | 0 {static => silpa/frontend/static}/doc/faq.html | 0 {static => silpa/frontend/static}/doc/index.html | 0 {static => silpa/frontend/static}/doc/install.html | 0 {static => silpa/frontend/static}/doc/json.html | 0 {static => silpa/frontend/static}/doc/license.html | 0 {static => silpa/frontend/static}/doc/privacy.html | 0 {static => silpa/frontend/static}/doc/source.html | 0 {static => silpa/frontend/static}/doc/todo.html | 0 .../frontend/static}/images/ime-active.png | Bin .../frontend/static}/images/ime-active.svg | 0 .../frontend/static}/images/ime-inactive.png | Bin .../frontend/static}/images/ime-inactive.svg | 0 .../frontend/static}/images/netdotnet.png | Bin {static => silpa/frontend/static}/images/tick.png | Bin {static => silpa/frontend/static}/images/tick.svg | 0 .../frontend/static}/js/bootstrap.min.js | 0 {static => silpa/frontend/static}/js/jquery.ime.js | 0 {static => silpa/frontend/static}/js/jquery.js | 0 {static => silpa/frontend/static}/output/.gitignore | 0 {static => silpa/frontend/static}/rules/README | 0 .../frontend/static}/rules/am/am-transliteration.js | 0 .../frontend/static}/rules/ar/ar-kbd.js | 0 .../frontend/static}/rules/as/as-avro.js | 0 .../frontend/static}/rules/as/as-bornona.js | 0 .../frontend/static}/rules/as/as-inscript.js | 0 .../frontend/static}/rules/as/as-inscript2.js | 0 .../frontend/static}/rules/as/as-phonetic.js | 0 .../frontend/static}/rules/as/as-transliteration.js | 0 .../frontend/static}/rules/be/be-kbd.js | 0 .../frontend/static}/rules/be/be-latin.js | 0 .../frontend/static}/rules/be/be-transliteration.js | 0 .../frontend/static}/rules/ber/ber-tfng.js | 0 .../frontend/static}/rules/bn/bn-avro.js | 0 .../frontend/static}/rules/bn/bn-inscript.js | 0 .../frontend/static}/rules/bn/bn-inscript2.js | 0 .../frontend/static}/rules/bn/bn-nkb.js | 0 .../frontend/static}/rules/bn/bn-probhat.js | 0 .../frontend/static}/rules/brx/brx-inscript.js | 0 .../frontend/static}/rules/brx/brx-inscript2.js | 0 .../frontend/static}/rules/cyrl/cyrl-palochka.js | 0 .../frontend/static}/rules/da/da-normforms.js | 0 {static => silpa/frontend/static}/rules/de/de.js | 0 .../frontend/static}/rules/doi/doi-inscript2.js | 0 .../frontend/static}/rules/el/el-kbd.js | 0 .../frontend/static}/rules/eo/eo-h-f.js | 0 {static => silpa/frontend/static}/rules/eo/eo-h.js | 0 .../frontend/static}/rules/eo/eo-plena.js | 0 {static => silpa/frontend/static}/rules/eo/eo-q.js | 0 .../frontend/static}/rules/eo/eo-transliteration.js | 0 {static => silpa/frontend/static}/rules/eo/eo-vi.js | 0 {static => silpa/frontend/static}/rules/eo/eo-x.js | 0 .../frontend/static}/rules/fi/fi-transliteration.js | 0 .../frontend/static}/rules/fo/fo-normforms.js | 0 .../frontend/static}/rules/fonipa/ipa-sil.js | 0 .../frontend/static}/rules/gu/gu-inscript.js | 0 .../frontend/static}/rules/gu/gu-inscript2.js | 0 .../frontend/static}/rules/gu/gu-phonetic.js | 0 .../frontend/static}/rules/gu/gu-transliteration.js | 0 .../frontend/static}/rules/he/he-kbd.js | 0 .../static}/rules/he/he-standard-2012-extonly.js | 0 .../frontend/static}/rules/he/he-standard-2012.js | 0 .../frontend/static}/rules/hi/hi-bolnagri.js | 0 .../frontend/static}/rules/hi/hi-inscript.js | 0 .../frontend/static}/rules/hi/hi-inscript2.js | 0 .../frontend/static}/rules/hi/hi-phonetic.js | 0 .../frontend/static}/rules/hi/hi-transliteration.js | 0 .../frontend/static}/rules/hr/hr-kbd.js | 0 .../frontend/static}/rules/hy/hy-kbd.js | 0 .../frontend/static}/rules/is/is-normforms.js | 0 .../frontend/static}/rules/jv/jv-transliteration.js | 0 .../frontend/static}/rules/ka/ka-kbd.js | 0 .../frontend/static}/rules/ka/ka-transliteration.js | 0 .../frontend/static}/rules/kk/kk-arabic.js | 0 .../frontend/static}/rules/kk/kk-kbd.js | 0 .../frontend/static}/rules/kn/kn-inscript.js | 0 .../frontend/static}/rules/kn/kn-inscript2.js | 0 .../frontend/static}/rules/kn/kn-kgp.js | 0 .../frontend/static}/rules/kn/kn-transliteration.js | 0 .../frontend/static}/rules/kok/kok-inscript2.js | 0 .../frontend/static}/rules/ks/ks-inscript.js | 0 .../frontend/static}/rules/ks/ks-kbd.js | 0 .../frontend/static}/rules/lo/lo-kbd.js | 0 .../frontend/static}/rules/mai/mai-inscript.js | 0 .../frontend/static}/rules/mai/mai-inscript2.js | 0 {static => silpa/frontend/static}/rules/mh/mh.js | 0 .../frontend/static}/rules/ml/ml-inscript.js | 0 .../frontend/static}/rules/ml/ml-inscript2.js | 0 .../frontend/static}/rules/ml/ml-transliteration.js | 0 .../frontend/static}/rules/mn/mn-cyrl.js | 0 .../frontend/static}/rules/mni/mni-inscript2.js | 0 .../frontend/static}/rules/mr/mr-inscript.js | 0 .../frontend/static}/rules/mr/mr-inscript2.js | 0 .../frontend/static}/rules/mr/mr-phonetic.js | 0 .../frontend/static}/rules/mr/mr-transliteration.js | 0 .../frontend/static}/rules/my/my-kbd.js | 0 .../frontend/static}/rules/my/my-xkb.js | 0 .../frontend/static}/rules/ne/ne-inscript.js | 0 .../frontend/static}/rules/ne/ne-inscript2.js | 0 .../frontend/static}/rules/ne/ne-rom.js | 0 .../frontend/static}/rules/ne/ne-trad.js | 0 .../frontend/static}/rules/ne/ne-transliteration.js | 0 .../frontend/static}/rules/no/no-normforms.js | 0 .../frontend/static}/rules/no/no-tildeforms.js | 0 .../frontend/static}/rules/or/or-inscript.js | 0 .../frontend/static}/rules/or/or-inscript2.js | 0 .../frontend/static}/rules/or/or-lekhani.js | 0 .../frontend/static}/rules/or/or-phonetic.js | 0 .../frontend/static}/rules/or/or-transliteration.js | 0 .../frontend/static}/rules/pa/pa-inscript.js | 0 .../frontend/static}/rules/pa/pa-inscript2.js | 0 .../frontend/static}/rules/pa/pa-jhelum.js | 0 .../frontend/static}/rules/pa/pa-phonetic.js | 0 .../frontend/static}/rules/pa/pa-transliteration.js | 0 .../frontend/static}/rules/ru/ru-jcuken.js | 0 .../frontend/static}/rules/ru/ru-kbd.js | 0 .../frontend/static}/rules/ru/ru-phonetic.js | 0 .../frontend/static}/rules/ru/ru-yawerty.js | 0 .../frontend/static}/rules/sa/sa-inscript.js | 0 .../frontend/static}/rules/sa/sa-inscript2.js | 0 .../frontend/static}/rules/sa/sa-transliteration.js | 0 .../static}/rules/sah/sah-transliteration.js | 0 .../frontend/static}/rules/sat/sat-inscript2.js | 0 .../frontend/static}/rules/sd/sd-inscript2.js | 0 .../frontend/static}/rules/se/se-normforms.js | 0 .../frontend/static}/rules/si/si-singlish.js | 0 .../frontend/static}/rules/si/si-wijesekara.js | 0 .../frontend/static}/rules/sk/sk-kbd.js | 0 .../frontend/static}/rules/sr/sr-kbd.js | 0 .../frontend/static}/rules/sv/sv-normforms.js | 0 {static => silpa/frontend/static}/rules/ta/ta-99.js | 0 .../frontend/static}/rules/ta/ta-bamini.js | 0 .../frontend/static}/rules/ta/ta-inscript.js | 0 .../frontend/static}/rules/ta/ta-inscript2.js | 0 .../frontend/static}/rules/ta/ta-transliteration.js | 0 .../frontend/static}/rules/te/te-inscript.js | 0 .../frontend/static}/rules/te/te-inscript2.js | 0 .../frontend/static}/rules/te/te-transliteration.js | 0 .../frontend/static}/rules/th/th-kedmanee.js | 0 .../frontend/static}/rules/th/th-pattachote.js | 0 .../frontend/static}/rules/ug/ug-kbd.js | 0 .../frontend/static}/rules/uk/uk-kbd.js | 0 .../frontend/static}/rules/ur/ur-phonetic.js | 0 .../frontend/static}/rules/ur/ur-transliteration.js | 0 .../frontend/static}/rules/uz/uz-kbd.js | 0 .../frontend/templates}/contact.html | 0 .../frontend/templates}/credits.html | 0 {templates => silpa/frontend/templates}/index.html | 0 .../frontend/templates}/license.html | 0 {templates => silpa/frontend/templates}/silpa.html | 0 156 files changed, 0 insertions(+), 0 deletions(-) rename {static => silpa/frontend/static}/css/bootstrap.min.css (100%) rename {static => silpa/frontend/static}/css/jquery.ime.css (100%) rename {static => silpa/frontend/static}/css/main.css (100%) rename {static => silpa/frontend/static}/doc/apis.html (100%) rename {static => silpa/frontend/static}/doc/contact.html (100%) rename {static => silpa/frontend/static}/doc/contribute.html (100%) rename {static => silpa/frontend/static}/doc/credits.html (100%) rename {static => silpa/frontend/static}/doc/faq.html (100%) rename {static => silpa/frontend/static}/doc/index.html (100%) rename {static => silpa/frontend/static}/doc/install.html (100%) rename {static => silpa/frontend/static}/doc/json.html (100%) rename {static => silpa/frontend/static}/doc/license.html (100%) rename {static => silpa/frontend/static}/doc/privacy.html (100%) rename {static => silpa/frontend/static}/doc/source.html (100%) rename {static => silpa/frontend/static}/doc/todo.html (100%) rename {static => silpa/frontend/static}/images/ime-active.png (100%) rename {static => silpa/frontend/static}/images/ime-active.svg (100%) rename {static => silpa/frontend/static}/images/ime-inactive.png (100%) rename {static => silpa/frontend/static}/images/ime-inactive.svg (100%) rename {static => silpa/frontend/static}/images/netdotnet.png (100%) rename {static => silpa/frontend/static}/images/tick.png (100%) rename {static => silpa/frontend/static}/images/tick.svg (100%) rename {static => silpa/frontend/static}/js/bootstrap.min.js (100%) rename {static => silpa/frontend/static}/js/jquery.ime.js (100%) rename {static => silpa/frontend/static}/js/jquery.js (100%) rename {static => silpa/frontend/static}/output/.gitignore (100%) rename {static => silpa/frontend/static}/rules/README (100%) rename {static => silpa/frontend/static}/rules/am/am-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/ar/ar-kbd.js (100%) rename {static => silpa/frontend/static}/rules/as/as-avro.js (100%) rename {static => silpa/frontend/static}/rules/as/as-bornona.js (100%) rename {static => silpa/frontend/static}/rules/as/as-inscript.js (100%) rename {static => silpa/frontend/static}/rules/as/as-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/as/as-phonetic.js (100%) rename {static => silpa/frontend/static}/rules/as/as-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/be/be-kbd.js (100%) rename {static => silpa/frontend/static}/rules/be/be-latin.js (100%) rename {static => silpa/frontend/static}/rules/be/be-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/ber/ber-tfng.js (100%) rename {static => silpa/frontend/static}/rules/bn/bn-avro.js (100%) rename {static => silpa/frontend/static}/rules/bn/bn-inscript.js (100%) rename {static => silpa/frontend/static}/rules/bn/bn-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/bn/bn-nkb.js (100%) rename {static => silpa/frontend/static}/rules/bn/bn-probhat.js (100%) rename {static => silpa/frontend/static}/rules/brx/brx-inscript.js (100%) rename {static => silpa/frontend/static}/rules/brx/brx-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/cyrl/cyrl-palochka.js (100%) rename {static => silpa/frontend/static}/rules/da/da-normforms.js (100%) rename {static => silpa/frontend/static}/rules/de/de.js (100%) rename {static => silpa/frontend/static}/rules/doi/doi-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/el/el-kbd.js (100%) rename {static => silpa/frontend/static}/rules/eo/eo-h-f.js (100%) rename {static => silpa/frontend/static}/rules/eo/eo-h.js (100%) rename {static => silpa/frontend/static}/rules/eo/eo-plena.js (100%) rename {static => silpa/frontend/static}/rules/eo/eo-q.js (100%) rename {static => silpa/frontend/static}/rules/eo/eo-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/eo/eo-vi.js (100%) rename {static => silpa/frontend/static}/rules/eo/eo-x.js (100%) rename {static => silpa/frontend/static}/rules/fi/fi-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/fo/fo-normforms.js (100%) rename {static => silpa/frontend/static}/rules/fonipa/ipa-sil.js (100%) rename {static => silpa/frontend/static}/rules/gu/gu-inscript.js (100%) rename {static => silpa/frontend/static}/rules/gu/gu-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/gu/gu-phonetic.js (100%) rename {static => silpa/frontend/static}/rules/gu/gu-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/he/he-kbd.js (100%) rename {static => silpa/frontend/static}/rules/he/he-standard-2012-extonly.js (100%) rename {static => silpa/frontend/static}/rules/he/he-standard-2012.js (100%) rename {static => silpa/frontend/static}/rules/hi/hi-bolnagri.js (100%) rename {static => silpa/frontend/static}/rules/hi/hi-inscript.js (100%) rename {static => silpa/frontend/static}/rules/hi/hi-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/hi/hi-phonetic.js (100%) rename {static => silpa/frontend/static}/rules/hi/hi-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/hr/hr-kbd.js (100%) rename {static => silpa/frontend/static}/rules/hy/hy-kbd.js (100%) rename {static => silpa/frontend/static}/rules/is/is-normforms.js (100%) rename {static => silpa/frontend/static}/rules/jv/jv-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/ka/ka-kbd.js (100%) rename {static => silpa/frontend/static}/rules/ka/ka-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/kk/kk-arabic.js (100%) rename {static => silpa/frontend/static}/rules/kk/kk-kbd.js (100%) rename {static => silpa/frontend/static}/rules/kn/kn-inscript.js (100%) rename {static => silpa/frontend/static}/rules/kn/kn-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/kn/kn-kgp.js (100%) rename {static => silpa/frontend/static}/rules/kn/kn-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/kok/kok-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/ks/ks-inscript.js (100%) rename {static => silpa/frontend/static}/rules/ks/ks-kbd.js (100%) rename {static => silpa/frontend/static}/rules/lo/lo-kbd.js (100%) rename {static => silpa/frontend/static}/rules/mai/mai-inscript.js (100%) rename {static => silpa/frontend/static}/rules/mai/mai-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/mh/mh.js (100%) rename {static => silpa/frontend/static}/rules/ml/ml-inscript.js (100%) rename {static => silpa/frontend/static}/rules/ml/ml-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/ml/ml-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/mn/mn-cyrl.js (100%) rename {static => silpa/frontend/static}/rules/mni/mni-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/mr/mr-inscript.js (100%) rename {static => silpa/frontend/static}/rules/mr/mr-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/mr/mr-phonetic.js (100%) rename {static => silpa/frontend/static}/rules/mr/mr-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/my/my-kbd.js (100%) rename {static => silpa/frontend/static}/rules/my/my-xkb.js (100%) rename {static => silpa/frontend/static}/rules/ne/ne-inscript.js (100%) rename {static => silpa/frontend/static}/rules/ne/ne-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/ne/ne-rom.js (100%) rename {static => silpa/frontend/static}/rules/ne/ne-trad.js (100%) rename {static => silpa/frontend/static}/rules/ne/ne-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/no/no-normforms.js (100%) rename {static => silpa/frontend/static}/rules/no/no-tildeforms.js (100%) rename {static => silpa/frontend/static}/rules/or/or-inscript.js (100%) rename {static => silpa/frontend/static}/rules/or/or-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/or/or-lekhani.js (100%) rename {static => silpa/frontend/static}/rules/or/or-phonetic.js (100%) rename {static => silpa/frontend/static}/rules/or/or-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/pa/pa-inscript.js (100%) rename {static => silpa/frontend/static}/rules/pa/pa-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/pa/pa-jhelum.js (100%) rename {static => silpa/frontend/static}/rules/pa/pa-phonetic.js (100%) rename {static => silpa/frontend/static}/rules/pa/pa-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/ru/ru-jcuken.js (100%) rename {static => silpa/frontend/static}/rules/ru/ru-kbd.js (100%) rename {static => silpa/frontend/static}/rules/ru/ru-phonetic.js (100%) rename {static => silpa/frontend/static}/rules/ru/ru-yawerty.js (100%) rename {static => silpa/frontend/static}/rules/sa/sa-inscript.js (100%) rename {static => silpa/frontend/static}/rules/sa/sa-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/sa/sa-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/sah/sah-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/sat/sat-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/sd/sd-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/se/se-normforms.js (100%) rename {static => silpa/frontend/static}/rules/si/si-singlish.js (100%) rename {static => silpa/frontend/static}/rules/si/si-wijesekara.js (100%) rename {static => silpa/frontend/static}/rules/sk/sk-kbd.js (100%) rename {static => silpa/frontend/static}/rules/sr/sr-kbd.js (100%) rename {static => silpa/frontend/static}/rules/sv/sv-normforms.js (100%) rename {static => silpa/frontend/static}/rules/ta/ta-99.js (100%) rename {static => silpa/frontend/static}/rules/ta/ta-bamini.js (100%) rename {static => silpa/frontend/static}/rules/ta/ta-inscript.js (100%) rename {static => silpa/frontend/static}/rules/ta/ta-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/ta/ta-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/te/te-inscript.js (100%) rename {static => silpa/frontend/static}/rules/te/te-inscript2.js (100%) rename {static => silpa/frontend/static}/rules/te/te-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/th/th-kedmanee.js (100%) rename {static => silpa/frontend/static}/rules/th/th-pattachote.js (100%) rename {static => silpa/frontend/static}/rules/ug/ug-kbd.js (100%) rename {static => silpa/frontend/static}/rules/uk/uk-kbd.js (100%) rename {static => silpa/frontend/static}/rules/ur/ur-phonetic.js (100%) rename {static => silpa/frontend/static}/rules/ur/ur-transliteration.js (100%) rename {static => silpa/frontend/static}/rules/uz/uz-kbd.js (100%) rename {templates => silpa/frontend/templates}/contact.html (100%) rename {templates => silpa/frontend/templates}/credits.html (100%) rename {templates => silpa/frontend/templates}/index.html (100%) rename {templates => silpa/frontend/templates}/license.html (100%) rename {templates => silpa/frontend/templates}/silpa.html (100%) diff --git a/static/css/bootstrap.min.css b/silpa/frontend/static/css/bootstrap.min.css similarity index 100% rename from static/css/bootstrap.min.css rename to silpa/frontend/static/css/bootstrap.min.css diff --git a/static/css/jquery.ime.css b/silpa/frontend/static/css/jquery.ime.css similarity index 100% rename from static/css/jquery.ime.css rename to silpa/frontend/static/css/jquery.ime.css diff --git a/static/css/main.css b/silpa/frontend/static/css/main.css similarity index 100% rename from static/css/main.css rename to silpa/frontend/static/css/main.css diff --git a/static/doc/apis.html b/silpa/frontend/static/doc/apis.html similarity index 100% rename from static/doc/apis.html rename to silpa/frontend/static/doc/apis.html diff --git a/static/doc/contact.html b/silpa/frontend/static/doc/contact.html similarity index 100% rename from static/doc/contact.html rename to silpa/frontend/static/doc/contact.html diff --git a/static/doc/contribute.html b/silpa/frontend/static/doc/contribute.html similarity index 100% rename from static/doc/contribute.html rename to silpa/frontend/static/doc/contribute.html diff --git a/static/doc/credits.html b/silpa/frontend/static/doc/credits.html similarity index 100% rename from static/doc/credits.html rename to silpa/frontend/static/doc/credits.html diff --git a/static/doc/faq.html b/silpa/frontend/static/doc/faq.html similarity index 100% rename from static/doc/faq.html rename to silpa/frontend/static/doc/faq.html diff --git a/static/doc/index.html b/silpa/frontend/static/doc/index.html similarity index 100% rename from static/doc/index.html rename to silpa/frontend/static/doc/index.html diff --git a/static/doc/install.html b/silpa/frontend/static/doc/install.html similarity index 100% rename from static/doc/install.html rename to silpa/frontend/static/doc/install.html diff --git a/static/doc/json.html b/silpa/frontend/static/doc/json.html similarity index 100% rename from static/doc/json.html rename to silpa/frontend/static/doc/json.html diff --git a/static/doc/license.html b/silpa/frontend/static/doc/license.html similarity index 100% rename from static/doc/license.html rename to silpa/frontend/static/doc/license.html diff --git a/static/doc/privacy.html b/silpa/frontend/static/doc/privacy.html similarity index 100% rename from static/doc/privacy.html rename to silpa/frontend/static/doc/privacy.html diff --git a/static/doc/source.html b/silpa/frontend/static/doc/source.html similarity index 100% rename from static/doc/source.html rename to silpa/frontend/static/doc/source.html diff --git a/static/doc/todo.html b/silpa/frontend/static/doc/todo.html similarity index 100% rename from static/doc/todo.html rename to silpa/frontend/static/doc/todo.html diff --git a/static/images/ime-active.png b/silpa/frontend/static/images/ime-active.png similarity index 100% rename from static/images/ime-active.png rename to silpa/frontend/static/images/ime-active.png diff --git a/static/images/ime-active.svg b/silpa/frontend/static/images/ime-active.svg similarity index 100% rename from static/images/ime-active.svg rename to silpa/frontend/static/images/ime-active.svg diff --git a/static/images/ime-inactive.png b/silpa/frontend/static/images/ime-inactive.png similarity index 100% rename from static/images/ime-inactive.png rename to silpa/frontend/static/images/ime-inactive.png diff --git a/static/images/ime-inactive.svg b/silpa/frontend/static/images/ime-inactive.svg similarity index 100% rename from static/images/ime-inactive.svg rename to silpa/frontend/static/images/ime-inactive.svg diff --git a/static/images/netdotnet.png b/silpa/frontend/static/images/netdotnet.png similarity index 100% rename from static/images/netdotnet.png rename to silpa/frontend/static/images/netdotnet.png diff --git a/static/images/tick.png b/silpa/frontend/static/images/tick.png similarity index 100% rename from static/images/tick.png rename to silpa/frontend/static/images/tick.png diff --git a/static/images/tick.svg b/silpa/frontend/static/images/tick.svg similarity index 100% rename from static/images/tick.svg rename to silpa/frontend/static/images/tick.svg diff --git a/static/js/bootstrap.min.js b/silpa/frontend/static/js/bootstrap.min.js similarity index 100% rename from static/js/bootstrap.min.js rename to silpa/frontend/static/js/bootstrap.min.js diff --git a/static/js/jquery.ime.js b/silpa/frontend/static/js/jquery.ime.js similarity index 100% rename from static/js/jquery.ime.js rename to silpa/frontend/static/js/jquery.ime.js diff --git a/static/js/jquery.js b/silpa/frontend/static/js/jquery.js similarity index 100% rename from static/js/jquery.js rename to silpa/frontend/static/js/jquery.js diff --git a/static/output/.gitignore b/silpa/frontend/static/output/.gitignore similarity index 100% rename from static/output/.gitignore rename to silpa/frontend/static/output/.gitignore diff --git a/static/rules/README b/silpa/frontend/static/rules/README similarity index 100% rename from static/rules/README rename to silpa/frontend/static/rules/README diff --git a/static/rules/am/am-transliteration.js b/silpa/frontend/static/rules/am/am-transliteration.js similarity index 100% rename from static/rules/am/am-transliteration.js rename to silpa/frontend/static/rules/am/am-transliteration.js diff --git a/static/rules/ar/ar-kbd.js b/silpa/frontend/static/rules/ar/ar-kbd.js similarity index 100% rename from static/rules/ar/ar-kbd.js rename to silpa/frontend/static/rules/ar/ar-kbd.js diff --git a/static/rules/as/as-avro.js b/silpa/frontend/static/rules/as/as-avro.js similarity index 100% rename from static/rules/as/as-avro.js rename to silpa/frontend/static/rules/as/as-avro.js diff --git a/static/rules/as/as-bornona.js b/silpa/frontend/static/rules/as/as-bornona.js similarity index 100% rename from static/rules/as/as-bornona.js rename to silpa/frontend/static/rules/as/as-bornona.js diff --git a/static/rules/as/as-inscript.js b/silpa/frontend/static/rules/as/as-inscript.js similarity index 100% rename from static/rules/as/as-inscript.js rename to silpa/frontend/static/rules/as/as-inscript.js diff --git a/static/rules/as/as-inscript2.js b/silpa/frontend/static/rules/as/as-inscript2.js similarity index 100% rename from static/rules/as/as-inscript2.js rename to silpa/frontend/static/rules/as/as-inscript2.js diff --git a/static/rules/as/as-phonetic.js b/silpa/frontend/static/rules/as/as-phonetic.js similarity index 100% rename from static/rules/as/as-phonetic.js rename to silpa/frontend/static/rules/as/as-phonetic.js diff --git a/static/rules/as/as-transliteration.js b/silpa/frontend/static/rules/as/as-transliteration.js similarity index 100% rename from static/rules/as/as-transliteration.js rename to silpa/frontend/static/rules/as/as-transliteration.js diff --git a/static/rules/be/be-kbd.js b/silpa/frontend/static/rules/be/be-kbd.js similarity index 100% rename from static/rules/be/be-kbd.js rename to silpa/frontend/static/rules/be/be-kbd.js diff --git a/static/rules/be/be-latin.js b/silpa/frontend/static/rules/be/be-latin.js similarity index 100% rename from static/rules/be/be-latin.js rename to silpa/frontend/static/rules/be/be-latin.js diff --git a/static/rules/be/be-transliteration.js b/silpa/frontend/static/rules/be/be-transliteration.js similarity index 100% rename from static/rules/be/be-transliteration.js rename to silpa/frontend/static/rules/be/be-transliteration.js diff --git a/static/rules/ber/ber-tfng.js b/silpa/frontend/static/rules/ber/ber-tfng.js similarity index 100% rename from static/rules/ber/ber-tfng.js rename to silpa/frontend/static/rules/ber/ber-tfng.js diff --git a/static/rules/bn/bn-avro.js b/silpa/frontend/static/rules/bn/bn-avro.js similarity index 100% rename from static/rules/bn/bn-avro.js rename to silpa/frontend/static/rules/bn/bn-avro.js diff --git a/static/rules/bn/bn-inscript.js b/silpa/frontend/static/rules/bn/bn-inscript.js similarity index 100% rename from static/rules/bn/bn-inscript.js rename to silpa/frontend/static/rules/bn/bn-inscript.js diff --git a/static/rules/bn/bn-inscript2.js b/silpa/frontend/static/rules/bn/bn-inscript2.js similarity index 100% rename from static/rules/bn/bn-inscript2.js rename to silpa/frontend/static/rules/bn/bn-inscript2.js diff --git a/static/rules/bn/bn-nkb.js b/silpa/frontend/static/rules/bn/bn-nkb.js similarity index 100% rename from static/rules/bn/bn-nkb.js rename to silpa/frontend/static/rules/bn/bn-nkb.js diff --git a/static/rules/bn/bn-probhat.js b/silpa/frontend/static/rules/bn/bn-probhat.js similarity index 100% rename from static/rules/bn/bn-probhat.js rename to silpa/frontend/static/rules/bn/bn-probhat.js diff --git a/static/rules/brx/brx-inscript.js b/silpa/frontend/static/rules/brx/brx-inscript.js similarity index 100% rename from static/rules/brx/brx-inscript.js rename to silpa/frontend/static/rules/brx/brx-inscript.js diff --git a/static/rules/brx/brx-inscript2.js b/silpa/frontend/static/rules/brx/brx-inscript2.js similarity index 100% rename from static/rules/brx/brx-inscript2.js rename to silpa/frontend/static/rules/brx/brx-inscript2.js diff --git a/static/rules/cyrl/cyrl-palochka.js b/silpa/frontend/static/rules/cyrl/cyrl-palochka.js similarity index 100% rename from static/rules/cyrl/cyrl-palochka.js rename to silpa/frontend/static/rules/cyrl/cyrl-palochka.js diff --git a/static/rules/da/da-normforms.js b/silpa/frontend/static/rules/da/da-normforms.js similarity index 100% rename from static/rules/da/da-normforms.js rename to silpa/frontend/static/rules/da/da-normforms.js diff --git a/static/rules/de/de.js b/silpa/frontend/static/rules/de/de.js similarity index 100% rename from static/rules/de/de.js rename to silpa/frontend/static/rules/de/de.js diff --git a/static/rules/doi/doi-inscript2.js b/silpa/frontend/static/rules/doi/doi-inscript2.js similarity index 100% rename from static/rules/doi/doi-inscript2.js rename to silpa/frontend/static/rules/doi/doi-inscript2.js diff --git a/static/rules/el/el-kbd.js b/silpa/frontend/static/rules/el/el-kbd.js similarity index 100% rename from static/rules/el/el-kbd.js rename to silpa/frontend/static/rules/el/el-kbd.js diff --git a/static/rules/eo/eo-h-f.js b/silpa/frontend/static/rules/eo/eo-h-f.js similarity index 100% rename from static/rules/eo/eo-h-f.js rename to silpa/frontend/static/rules/eo/eo-h-f.js diff --git a/static/rules/eo/eo-h.js b/silpa/frontend/static/rules/eo/eo-h.js similarity index 100% rename from static/rules/eo/eo-h.js rename to silpa/frontend/static/rules/eo/eo-h.js diff --git a/static/rules/eo/eo-plena.js b/silpa/frontend/static/rules/eo/eo-plena.js similarity index 100% rename from static/rules/eo/eo-plena.js rename to silpa/frontend/static/rules/eo/eo-plena.js diff --git a/static/rules/eo/eo-q.js b/silpa/frontend/static/rules/eo/eo-q.js similarity index 100% rename from static/rules/eo/eo-q.js rename to silpa/frontend/static/rules/eo/eo-q.js diff --git a/static/rules/eo/eo-transliteration.js b/silpa/frontend/static/rules/eo/eo-transliteration.js similarity index 100% rename from static/rules/eo/eo-transliteration.js rename to silpa/frontend/static/rules/eo/eo-transliteration.js diff --git a/static/rules/eo/eo-vi.js b/silpa/frontend/static/rules/eo/eo-vi.js similarity index 100% rename from static/rules/eo/eo-vi.js rename to silpa/frontend/static/rules/eo/eo-vi.js diff --git a/static/rules/eo/eo-x.js b/silpa/frontend/static/rules/eo/eo-x.js similarity index 100% rename from static/rules/eo/eo-x.js rename to silpa/frontend/static/rules/eo/eo-x.js diff --git a/static/rules/fi/fi-transliteration.js b/silpa/frontend/static/rules/fi/fi-transliteration.js similarity index 100% rename from static/rules/fi/fi-transliteration.js rename to silpa/frontend/static/rules/fi/fi-transliteration.js diff --git a/static/rules/fo/fo-normforms.js b/silpa/frontend/static/rules/fo/fo-normforms.js similarity index 100% rename from static/rules/fo/fo-normforms.js rename to silpa/frontend/static/rules/fo/fo-normforms.js diff --git a/static/rules/fonipa/ipa-sil.js b/silpa/frontend/static/rules/fonipa/ipa-sil.js similarity index 100% rename from static/rules/fonipa/ipa-sil.js rename to silpa/frontend/static/rules/fonipa/ipa-sil.js diff --git a/static/rules/gu/gu-inscript.js b/silpa/frontend/static/rules/gu/gu-inscript.js similarity index 100% rename from static/rules/gu/gu-inscript.js rename to silpa/frontend/static/rules/gu/gu-inscript.js diff --git a/static/rules/gu/gu-inscript2.js b/silpa/frontend/static/rules/gu/gu-inscript2.js similarity index 100% rename from static/rules/gu/gu-inscript2.js rename to silpa/frontend/static/rules/gu/gu-inscript2.js diff --git a/static/rules/gu/gu-phonetic.js b/silpa/frontend/static/rules/gu/gu-phonetic.js similarity index 100% rename from static/rules/gu/gu-phonetic.js rename to silpa/frontend/static/rules/gu/gu-phonetic.js diff --git a/static/rules/gu/gu-transliteration.js b/silpa/frontend/static/rules/gu/gu-transliteration.js similarity index 100% rename from static/rules/gu/gu-transliteration.js rename to silpa/frontend/static/rules/gu/gu-transliteration.js diff --git a/static/rules/he/he-kbd.js b/silpa/frontend/static/rules/he/he-kbd.js similarity index 100% rename from static/rules/he/he-kbd.js rename to silpa/frontend/static/rules/he/he-kbd.js diff --git a/static/rules/he/he-standard-2012-extonly.js b/silpa/frontend/static/rules/he/he-standard-2012-extonly.js similarity index 100% rename from static/rules/he/he-standard-2012-extonly.js rename to silpa/frontend/static/rules/he/he-standard-2012-extonly.js diff --git a/static/rules/he/he-standard-2012.js b/silpa/frontend/static/rules/he/he-standard-2012.js similarity index 100% rename from static/rules/he/he-standard-2012.js rename to silpa/frontend/static/rules/he/he-standard-2012.js diff --git a/static/rules/hi/hi-bolnagri.js b/silpa/frontend/static/rules/hi/hi-bolnagri.js similarity index 100% rename from static/rules/hi/hi-bolnagri.js rename to silpa/frontend/static/rules/hi/hi-bolnagri.js diff --git a/static/rules/hi/hi-inscript.js b/silpa/frontend/static/rules/hi/hi-inscript.js similarity index 100% rename from static/rules/hi/hi-inscript.js rename to silpa/frontend/static/rules/hi/hi-inscript.js diff --git a/static/rules/hi/hi-inscript2.js b/silpa/frontend/static/rules/hi/hi-inscript2.js similarity index 100% rename from static/rules/hi/hi-inscript2.js rename to silpa/frontend/static/rules/hi/hi-inscript2.js diff --git a/static/rules/hi/hi-phonetic.js b/silpa/frontend/static/rules/hi/hi-phonetic.js similarity index 100% rename from static/rules/hi/hi-phonetic.js rename to silpa/frontend/static/rules/hi/hi-phonetic.js diff --git a/static/rules/hi/hi-transliteration.js b/silpa/frontend/static/rules/hi/hi-transliteration.js similarity index 100% rename from static/rules/hi/hi-transliteration.js rename to silpa/frontend/static/rules/hi/hi-transliteration.js diff --git a/static/rules/hr/hr-kbd.js b/silpa/frontend/static/rules/hr/hr-kbd.js similarity index 100% rename from static/rules/hr/hr-kbd.js rename to silpa/frontend/static/rules/hr/hr-kbd.js diff --git a/static/rules/hy/hy-kbd.js b/silpa/frontend/static/rules/hy/hy-kbd.js similarity index 100% rename from static/rules/hy/hy-kbd.js rename to silpa/frontend/static/rules/hy/hy-kbd.js diff --git a/static/rules/is/is-normforms.js b/silpa/frontend/static/rules/is/is-normforms.js similarity index 100% rename from static/rules/is/is-normforms.js rename to silpa/frontend/static/rules/is/is-normforms.js diff --git a/static/rules/jv/jv-transliteration.js b/silpa/frontend/static/rules/jv/jv-transliteration.js similarity index 100% rename from static/rules/jv/jv-transliteration.js rename to silpa/frontend/static/rules/jv/jv-transliteration.js diff --git a/static/rules/ka/ka-kbd.js b/silpa/frontend/static/rules/ka/ka-kbd.js similarity index 100% rename from static/rules/ka/ka-kbd.js rename to silpa/frontend/static/rules/ka/ka-kbd.js diff --git a/static/rules/ka/ka-transliteration.js b/silpa/frontend/static/rules/ka/ka-transliteration.js similarity index 100% rename from static/rules/ka/ka-transliteration.js rename to silpa/frontend/static/rules/ka/ka-transliteration.js diff --git a/static/rules/kk/kk-arabic.js b/silpa/frontend/static/rules/kk/kk-arabic.js similarity index 100% rename from static/rules/kk/kk-arabic.js rename to silpa/frontend/static/rules/kk/kk-arabic.js diff --git a/static/rules/kk/kk-kbd.js b/silpa/frontend/static/rules/kk/kk-kbd.js similarity index 100% rename from static/rules/kk/kk-kbd.js rename to silpa/frontend/static/rules/kk/kk-kbd.js diff --git a/static/rules/kn/kn-inscript.js b/silpa/frontend/static/rules/kn/kn-inscript.js similarity index 100% rename from static/rules/kn/kn-inscript.js rename to silpa/frontend/static/rules/kn/kn-inscript.js diff --git a/static/rules/kn/kn-inscript2.js b/silpa/frontend/static/rules/kn/kn-inscript2.js similarity index 100% rename from static/rules/kn/kn-inscript2.js rename to silpa/frontend/static/rules/kn/kn-inscript2.js diff --git a/static/rules/kn/kn-kgp.js b/silpa/frontend/static/rules/kn/kn-kgp.js similarity index 100% rename from static/rules/kn/kn-kgp.js rename to silpa/frontend/static/rules/kn/kn-kgp.js diff --git a/static/rules/kn/kn-transliteration.js b/silpa/frontend/static/rules/kn/kn-transliteration.js similarity index 100% rename from static/rules/kn/kn-transliteration.js rename to silpa/frontend/static/rules/kn/kn-transliteration.js diff --git a/static/rules/kok/kok-inscript2.js b/silpa/frontend/static/rules/kok/kok-inscript2.js similarity index 100% rename from static/rules/kok/kok-inscript2.js rename to silpa/frontend/static/rules/kok/kok-inscript2.js diff --git a/static/rules/ks/ks-inscript.js b/silpa/frontend/static/rules/ks/ks-inscript.js similarity index 100% rename from static/rules/ks/ks-inscript.js rename to silpa/frontend/static/rules/ks/ks-inscript.js diff --git a/static/rules/ks/ks-kbd.js b/silpa/frontend/static/rules/ks/ks-kbd.js similarity index 100% rename from static/rules/ks/ks-kbd.js rename to silpa/frontend/static/rules/ks/ks-kbd.js diff --git a/static/rules/lo/lo-kbd.js b/silpa/frontend/static/rules/lo/lo-kbd.js similarity index 100% rename from static/rules/lo/lo-kbd.js rename to silpa/frontend/static/rules/lo/lo-kbd.js diff --git a/static/rules/mai/mai-inscript.js b/silpa/frontend/static/rules/mai/mai-inscript.js similarity index 100% rename from static/rules/mai/mai-inscript.js rename to silpa/frontend/static/rules/mai/mai-inscript.js diff --git a/static/rules/mai/mai-inscript2.js b/silpa/frontend/static/rules/mai/mai-inscript2.js similarity index 100% rename from static/rules/mai/mai-inscript2.js rename to silpa/frontend/static/rules/mai/mai-inscript2.js diff --git a/static/rules/mh/mh.js b/silpa/frontend/static/rules/mh/mh.js similarity index 100% rename from static/rules/mh/mh.js rename to silpa/frontend/static/rules/mh/mh.js diff --git a/static/rules/ml/ml-inscript.js b/silpa/frontend/static/rules/ml/ml-inscript.js similarity index 100% rename from static/rules/ml/ml-inscript.js rename to silpa/frontend/static/rules/ml/ml-inscript.js diff --git a/static/rules/ml/ml-inscript2.js b/silpa/frontend/static/rules/ml/ml-inscript2.js similarity index 100% rename from static/rules/ml/ml-inscript2.js rename to silpa/frontend/static/rules/ml/ml-inscript2.js diff --git a/static/rules/ml/ml-transliteration.js b/silpa/frontend/static/rules/ml/ml-transliteration.js similarity index 100% rename from static/rules/ml/ml-transliteration.js rename to silpa/frontend/static/rules/ml/ml-transliteration.js diff --git a/static/rules/mn/mn-cyrl.js b/silpa/frontend/static/rules/mn/mn-cyrl.js similarity index 100% rename from static/rules/mn/mn-cyrl.js rename to silpa/frontend/static/rules/mn/mn-cyrl.js diff --git a/static/rules/mni/mni-inscript2.js b/silpa/frontend/static/rules/mni/mni-inscript2.js similarity index 100% rename from static/rules/mni/mni-inscript2.js rename to silpa/frontend/static/rules/mni/mni-inscript2.js diff --git a/static/rules/mr/mr-inscript.js b/silpa/frontend/static/rules/mr/mr-inscript.js similarity index 100% rename from static/rules/mr/mr-inscript.js rename to silpa/frontend/static/rules/mr/mr-inscript.js diff --git a/static/rules/mr/mr-inscript2.js b/silpa/frontend/static/rules/mr/mr-inscript2.js similarity index 100% rename from static/rules/mr/mr-inscript2.js rename to silpa/frontend/static/rules/mr/mr-inscript2.js diff --git a/static/rules/mr/mr-phonetic.js b/silpa/frontend/static/rules/mr/mr-phonetic.js similarity index 100% rename from static/rules/mr/mr-phonetic.js rename to silpa/frontend/static/rules/mr/mr-phonetic.js diff --git a/static/rules/mr/mr-transliteration.js b/silpa/frontend/static/rules/mr/mr-transliteration.js similarity index 100% rename from static/rules/mr/mr-transliteration.js rename to silpa/frontend/static/rules/mr/mr-transliteration.js diff --git a/static/rules/my/my-kbd.js b/silpa/frontend/static/rules/my/my-kbd.js similarity index 100% rename from static/rules/my/my-kbd.js rename to silpa/frontend/static/rules/my/my-kbd.js diff --git a/static/rules/my/my-xkb.js b/silpa/frontend/static/rules/my/my-xkb.js similarity index 100% rename from static/rules/my/my-xkb.js rename to silpa/frontend/static/rules/my/my-xkb.js diff --git a/static/rules/ne/ne-inscript.js b/silpa/frontend/static/rules/ne/ne-inscript.js similarity index 100% rename from static/rules/ne/ne-inscript.js rename to silpa/frontend/static/rules/ne/ne-inscript.js diff --git a/static/rules/ne/ne-inscript2.js b/silpa/frontend/static/rules/ne/ne-inscript2.js similarity index 100% rename from static/rules/ne/ne-inscript2.js rename to silpa/frontend/static/rules/ne/ne-inscript2.js diff --git a/static/rules/ne/ne-rom.js b/silpa/frontend/static/rules/ne/ne-rom.js similarity index 100% rename from static/rules/ne/ne-rom.js rename to silpa/frontend/static/rules/ne/ne-rom.js diff --git a/static/rules/ne/ne-trad.js b/silpa/frontend/static/rules/ne/ne-trad.js similarity index 100% rename from static/rules/ne/ne-trad.js rename to silpa/frontend/static/rules/ne/ne-trad.js diff --git a/static/rules/ne/ne-transliteration.js b/silpa/frontend/static/rules/ne/ne-transliteration.js similarity index 100% rename from static/rules/ne/ne-transliteration.js rename to silpa/frontend/static/rules/ne/ne-transliteration.js diff --git a/static/rules/no/no-normforms.js b/silpa/frontend/static/rules/no/no-normforms.js similarity index 100% rename from static/rules/no/no-normforms.js rename to silpa/frontend/static/rules/no/no-normforms.js diff --git a/static/rules/no/no-tildeforms.js b/silpa/frontend/static/rules/no/no-tildeforms.js similarity index 100% rename from static/rules/no/no-tildeforms.js rename to silpa/frontend/static/rules/no/no-tildeforms.js diff --git a/static/rules/or/or-inscript.js b/silpa/frontend/static/rules/or/or-inscript.js similarity index 100% rename from static/rules/or/or-inscript.js rename to silpa/frontend/static/rules/or/or-inscript.js diff --git a/static/rules/or/or-inscript2.js b/silpa/frontend/static/rules/or/or-inscript2.js similarity index 100% rename from static/rules/or/or-inscript2.js rename to silpa/frontend/static/rules/or/or-inscript2.js diff --git a/static/rules/or/or-lekhani.js b/silpa/frontend/static/rules/or/or-lekhani.js similarity index 100% rename from static/rules/or/or-lekhani.js rename to silpa/frontend/static/rules/or/or-lekhani.js diff --git a/static/rules/or/or-phonetic.js b/silpa/frontend/static/rules/or/or-phonetic.js similarity index 100% rename from static/rules/or/or-phonetic.js rename to silpa/frontend/static/rules/or/or-phonetic.js diff --git a/static/rules/or/or-transliteration.js b/silpa/frontend/static/rules/or/or-transliteration.js similarity index 100% rename from static/rules/or/or-transliteration.js rename to silpa/frontend/static/rules/or/or-transliteration.js diff --git a/static/rules/pa/pa-inscript.js b/silpa/frontend/static/rules/pa/pa-inscript.js similarity index 100% rename from static/rules/pa/pa-inscript.js rename to silpa/frontend/static/rules/pa/pa-inscript.js diff --git a/static/rules/pa/pa-inscript2.js b/silpa/frontend/static/rules/pa/pa-inscript2.js similarity index 100% rename from static/rules/pa/pa-inscript2.js rename to silpa/frontend/static/rules/pa/pa-inscript2.js diff --git a/static/rules/pa/pa-jhelum.js b/silpa/frontend/static/rules/pa/pa-jhelum.js similarity index 100% rename from static/rules/pa/pa-jhelum.js rename to silpa/frontend/static/rules/pa/pa-jhelum.js diff --git a/static/rules/pa/pa-phonetic.js b/silpa/frontend/static/rules/pa/pa-phonetic.js similarity index 100% rename from static/rules/pa/pa-phonetic.js rename to silpa/frontend/static/rules/pa/pa-phonetic.js diff --git a/static/rules/pa/pa-transliteration.js b/silpa/frontend/static/rules/pa/pa-transliteration.js similarity index 100% rename from static/rules/pa/pa-transliteration.js rename to silpa/frontend/static/rules/pa/pa-transliteration.js diff --git a/static/rules/ru/ru-jcuken.js b/silpa/frontend/static/rules/ru/ru-jcuken.js similarity index 100% rename from static/rules/ru/ru-jcuken.js rename to silpa/frontend/static/rules/ru/ru-jcuken.js diff --git a/static/rules/ru/ru-kbd.js b/silpa/frontend/static/rules/ru/ru-kbd.js similarity index 100% rename from static/rules/ru/ru-kbd.js rename to silpa/frontend/static/rules/ru/ru-kbd.js diff --git a/static/rules/ru/ru-phonetic.js b/silpa/frontend/static/rules/ru/ru-phonetic.js similarity index 100% rename from static/rules/ru/ru-phonetic.js rename to silpa/frontend/static/rules/ru/ru-phonetic.js diff --git a/static/rules/ru/ru-yawerty.js b/silpa/frontend/static/rules/ru/ru-yawerty.js similarity index 100% rename from static/rules/ru/ru-yawerty.js rename to silpa/frontend/static/rules/ru/ru-yawerty.js diff --git a/static/rules/sa/sa-inscript.js b/silpa/frontend/static/rules/sa/sa-inscript.js similarity index 100% rename from static/rules/sa/sa-inscript.js rename to silpa/frontend/static/rules/sa/sa-inscript.js diff --git a/static/rules/sa/sa-inscript2.js b/silpa/frontend/static/rules/sa/sa-inscript2.js similarity index 100% rename from static/rules/sa/sa-inscript2.js rename to silpa/frontend/static/rules/sa/sa-inscript2.js diff --git a/static/rules/sa/sa-transliteration.js b/silpa/frontend/static/rules/sa/sa-transliteration.js similarity index 100% rename from static/rules/sa/sa-transliteration.js rename to silpa/frontend/static/rules/sa/sa-transliteration.js diff --git a/static/rules/sah/sah-transliteration.js b/silpa/frontend/static/rules/sah/sah-transliteration.js similarity index 100% rename from static/rules/sah/sah-transliteration.js rename to silpa/frontend/static/rules/sah/sah-transliteration.js diff --git a/static/rules/sat/sat-inscript2.js b/silpa/frontend/static/rules/sat/sat-inscript2.js similarity index 100% rename from static/rules/sat/sat-inscript2.js rename to silpa/frontend/static/rules/sat/sat-inscript2.js diff --git a/static/rules/sd/sd-inscript2.js b/silpa/frontend/static/rules/sd/sd-inscript2.js similarity index 100% rename from static/rules/sd/sd-inscript2.js rename to silpa/frontend/static/rules/sd/sd-inscript2.js diff --git a/static/rules/se/se-normforms.js b/silpa/frontend/static/rules/se/se-normforms.js similarity index 100% rename from static/rules/se/se-normforms.js rename to silpa/frontend/static/rules/se/se-normforms.js diff --git a/static/rules/si/si-singlish.js b/silpa/frontend/static/rules/si/si-singlish.js similarity index 100% rename from static/rules/si/si-singlish.js rename to silpa/frontend/static/rules/si/si-singlish.js diff --git a/static/rules/si/si-wijesekara.js b/silpa/frontend/static/rules/si/si-wijesekara.js similarity index 100% rename from static/rules/si/si-wijesekara.js rename to silpa/frontend/static/rules/si/si-wijesekara.js diff --git a/static/rules/sk/sk-kbd.js b/silpa/frontend/static/rules/sk/sk-kbd.js similarity index 100% rename from static/rules/sk/sk-kbd.js rename to silpa/frontend/static/rules/sk/sk-kbd.js diff --git a/static/rules/sr/sr-kbd.js b/silpa/frontend/static/rules/sr/sr-kbd.js similarity index 100% rename from static/rules/sr/sr-kbd.js rename to silpa/frontend/static/rules/sr/sr-kbd.js diff --git a/static/rules/sv/sv-normforms.js b/silpa/frontend/static/rules/sv/sv-normforms.js similarity index 100% rename from static/rules/sv/sv-normforms.js rename to silpa/frontend/static/rules/sv/sv-normforms.js diff --git a/static/rules/ta/ta-99.js b/silpa/frontend/static/rules/ta/ta-99.js similarity index 100% rename from static/rules/ta/ta-99.js rename to silpa/frontend/static/rules/ta/ta-99.js diff --git a/static/rules/ta/ta-bamini.js b/silpa/frontend/static/rules/ta/ta-bamini.js similarity index 100% rename from static/rules/ta/ta-bamini.js rename to silpa/frontend/static/rules/ta/ta-bamini.js diff --git a/static/rules/ta/ta-inscript.js b/silpa/frontend/static/rules/ta/ta-inscript.js similarity index 100% rename from static/rules/ta/ta-inscript.js rename to silpa/frontend/static/rules/ta/ta-inscript.js diff --git a/static/rules/ta/ta-inscript2.js b/silpa/frontend/static/rules/ta/ta-inscript2.js similarity index 100% rename from static/rules/ta/ta-inscript2.js rename to silpa/frontend/static/rules/ta/ta-inscript2.js diff --git a/static/rules/ta/ta-transliteration.js b/silpa/frontend/static/rules/ta/ta-transliteration.js similarity index 100% rename from static/rules/ta/ta-transliteration.js rename to silpa/frontend/static/rules/ta/ta-transliteration.js diff --git a/static/rules/te/te-inscript.js b/silpa/frontend/static/rules/te/te-inscript.js similarity index 100% rename from static/rules/te/te-inscript.js rename to silpa/frontend/static/rules/te/te-inscript.js diff --git a/static/rules/te/te-inscript2.js b/silpa/frontend/static/rules/te/te-inscript2.js similarity index 100% rename from static/rules/te/te-inscript2.js rename to silpa/frontend/static/rules/te/te-inscript2.js diff --git a/static/rules/te/te-transliteration.js b/silpa/frontend/static/rules/te/te-transliteration.js similarity index 100% rename from static/rules/te/te-transliteration.js rename to silpa/frontend/static/rules/te/te-transliteration.js diff --git a/static/rules/th/th-kedmanee.js b/silpa/frontend/static/rules/th/th-kedmanee.js similarity index 100% rename from static/rules/th/th-kedmanee.js rename to silpa/frontend/static/rules/th/th-kedmanee.js diff --git a/static/rules/th/th-pattachote.js b/silpa/frontend/static/rules/th/th-pattachote.js similarity index 100% rename from static/rules/th/th-pattachote.js rename to silpa/frontend/static/rules/th/th-pattachote.js diff --git a/static/rules/ug/ug-kbd.js b/silpa/frontend/static/rules/ug/ug-kbd.js similarity index 100% rename from static/rules/ug/ug-kbd.js rename to silpa/frontend/static/rules/ug/ug-kbd.js diff --git a/static/rules/uk/uk-kbd.js b/silpa/frontend/static/rules/uk/uk-kbd.js similarity index 100% rename from static/rules/uk/uk-kbd.js rename to silpa/frontend/static/rules/uk/uk-kbd.js diff --git a/static/rules/ur/ur-phonetic.js b/silpa/frontend/static/rules/ur/ur-phonetic.js similarity index 100% rename from static/rules/ur/ur-phonetic.js rename to silpa/frontend/static/rules/ur/ur-phonetic.js diff --git a/static/rules/ur/ur-transliteration.js b/silpa/frontend/static/rules/ur/ur-transliteration.js similarity index 100% rename from static/rules/ur/ur-transliteration.js rename to silpa/frontend/static/rules/ur/ur-transliteration.js diff --git a/static/rules/uz/uz-kbd.js b/silpa/frontend/static/rules/uz/uz-kbd.js similarity index 100% rename from static/rules/uz/uz-kbd.js rename to silpa/frontend/static/rules/uz/uz-kbd.js diff --git a/templates/contact.html b/silpa/frontend/templates/contact.html similarity index 100% rename from templates/contact.html rename to silpa/frontend/templates/contact.html diff --git a/templates/credits.html b/silpa/frontend/templates/credits.html similarity index 100% rename from templates/credits.html rename to silpa/frontend/templates/credits.html diff --git a/templates/index.html b/silpa/frontend/templates/index.html similarity index 100% rename from templates/index.html rename to silpa/frontend/templates/index.html diff --git a/templates/license.html b/silpa/frontend/templates/license.html similarity index 100% rename from templates/license.html rename to silpa/frontend/templates/license.html diff --git a/templates/silpa.html b/silpa/frontend/templates/silpa.html similarity index 100% rename from templates/silpa.html rename to silpa/frontend/templates/silpa.html From b4ba1a095216169e92e279680f853de69b405090 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 13 Apr 2014 07:59:30 +0530 Subject: [PATCH 15/96] Configure logging on app before returning it We also need same logging across multiple app, configure logging before returning the app from factory. Signed-off-by: Vasudev Kamath --- silpa/factory.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/silpa/factory.py b/silpa/factory.py index 0c05427..20efe25 100644 --- a/silpa/factory.py +++ b/silpa/factory.py @@ -1,7 +1,12 @@ -from flask import Flask, Blueprint - import pkgutil import importlib +import logging +import os + +from flask import Flask, Blueprint +from loadconfig import config +from logging import Formatter +from logging.handlers import TimedRotatingFileHandler def register_blueprints(app, package_name, package_path): @@ -17,6 +22,33 @@ def register_blueprints(app, package_name, package_path): return rv +def configure_logging(app): + log_level = config.get('logging', 'log_level') + log_folder = config.get('logging', 'log_folder') + log_name = config.get('logging', 'log_name') + + handler = TimedRotatingFileHandler(os.path.join(log_folder, log_name), + when='D', interval=7, backupCount=4) + + level = None + + if log_level == 'debug': + level = logging.DEBUG + elif log_level == 'info': + level = logging.INFO + elif log_level == 'error': + level = logging.ERROR + elif log_level == 'warn': + level = logging.WARNING + + handler.setLevel(level) + handler.setFormatter(Formatter('%(asctime)s %(levelname)s' + + ' %(messagename)7s - [in %(funcName)s' + + ' at %(pathname)s %(lineno)d]')) + app.logger.setLevel(level) + app.logger.addHandler(handler) + + def create_app(package_name, package_path, settings_override=None): app = Flask(package_name, instance_relative_config=True) app.config.from_object("silpa.settings") @@ -24,3 +56,4 @@ def create_app(package_name, package_path, settings_override=None): app.config.from_object(settings_override) register_blueprints(app, package_name, package_path) + configure_logging(app) From 7d9c5a4aa5f375ce8f73c0b56ef9a87f2246e057 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 13 Apr 2014 08:11:48 +0530 Subject: [PATCH 16/96] add return statement to return app from the factory --- silpa/api/__init__.py | 1 + silpa/factory.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/silpa/api/__init__.py b/silpa/api/__init__.py index 9fc2dcb..e163398 100644 --- a/silpa/api/__init__.py +++ b/silpa/api/__init__.py @@ -25,6 +25,7 @@ def create_app(settings_override=None): # Load enabled modules from the config moduleloader.load_modules() + return app def on_404(e): diff --git a/silpa/factory.py b/silpa/factory.py index 20efe25..7936920 100644 --- a/silpa/factory.py +++ b/silpa/factory.py @@ -57,3 +57,5 @@ def create_app(package_name, package_path, settings_override=None): register_blueprints(app, package_name, package_path) configure_logging(app) + + return app From b810abab24ab1546e55fe873f30e913ae57d574c Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 13 Apr 2014 08:12:05 +0530 Subject: [PATCH 17/96] Can't use current_app here, instead print to sys.stderr --- silpa/moduleloader.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/silpa/moduleloader.py b/silpa/moduleloader.py index 1d8db9a..c6c7853 100644 --- a/silpa/moduleloader.py +++ b/silpa/moduleloader.py @@ -1,7 +1,8 @@ +from __future__ import print_function from loadconfig import config -from flask import current_app import importlib +import sys _modules = [module for module, need in config.items("modules") if need == "yes"] @@ -12,5 +13,5 @@ def load_modules(): try: importlib.import_module(module) except ImportError: - current_app.logger.error( - "Failed to import {module}".format(module=module)) + print("Failed to import {module}".format(module=module), + file=sys.stderr) From 4a7514a5f1542bc270a3d31c14a444f581e40648 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 13 Apr 2014 08:14:38 +0530 Subject: [PATCH 18/96] Remove old jsonrpc reference file --- silpa/api/jsonrpc_old.py | 102 --------------------------------------- 1 file changed, 102 deletions(-) delete mode 100644 silpa/api/jsonrpc_old.py diff --git a/silpa/api/jsonrpc_old.py b/silpa/api/jsonrpc_old.py deleted file mode 100644 index 10ee409..0000000 --- a/silpa/api/jsonrpc_old.py +++ /dev/null @@ -1,102 +0,0 @@ -from json import loads, dumps -from modulehelper import modules, modulenames, MODULES, enabled_modules, \ - load_modules - - -class JSONRPCHandlerException(Exception): - pass - - -class JSONRequestNotTranslatable(JSONRPCHandlerException): - pass - - -class BadServiceRequest(JSONRPCHandlerException): - pass - - -class MethodNotFoundException(JSONRPCHandlerException): - def __init__(self, name): - self.methodname = name - - -class JSONRPCHandler(object): - - def __init__(self): - ''' - This should be only once called. Atleast my assumption - ''' - load_modules() - - def translate_request(self, data): - try: - req = loads(data) - except: - raise JSONRequestNotTranslatable - return req - - def translate_result(self, result, error, id_): - if error != None: - error = {"name": error.__class__.__name__, "message": error} - result = None - - try: - data = dumps({"result": result, "id": id_, "error": error}) - except: - error = {"name": "JSONEncodeException", \ - "message": "Result object is not serializable"} - data = dumps({"result": None, "id": id_, "error": error}) - - return data - - def call(self, method, args): - _args = None - for arg in args: - if arg != '': - if _args == None: - _args = [] - _args.append(arg) - - if _args == None: - # No arguments - return method() - else: - return method(*_args) - - def handle_request(self, json): - err = None - meth = None - id_ = '' - result = None - args = None - - try: - req = self.translate_request(json) - except JSONRequestNotTranslatable, e: - err = e - req = {'id': id_} - - if err == None: - try: - id_ = req['id'] - meth = req['method'] - try: - args = req['params'] - except: - pass - except: - err = BadServiceRequest(json) - - module_instance = None - if err == None: - try: - module_instance = MODULES.get(meth.split('.')[0]) - except: - err = MethodNotFoundException(meth.split('.')[-1]) - - method = None - if err == None: - result = self.call(getattr(module_instance, \ - meth.split('.')[-1]), args) - - return self.translate_result(result, err, id_) From 8b3e177e266867c0d7351ff308ec1e62c09e055c Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 13 Apr 2014 08:16:18 +0530 Subject: [PATCH 19/96] for package path use __path__ pep8 throws an error but this is run time variable, need to find to make flake8 ignore this error. --- silpa/api/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silpa/api/__init__.py b/silpa/api/__init__.py index e163398..e7daace 100644 --- a/silpa/api/__init__.py +++ b/silpa/api/__init__.py @@ -19,7 +19,7 @@ def wrapper(*args, **kwargs): def create_app(settings_override=None): - app = factory.create_app(__name__, os.path.dirname(__file__), + app = factory.create_app(__name__, __path__, settings_override) app.errorhandler(404)(on_404) From 5c2b2b81efeb7f84d0932cd53434ddda91d3279f Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 13 Apr 2014 08:16:57 +0530 Subject: [PATCH 20/96] Mount JSON-RPC at /api url_prefix Signed-off-by: Vasudev Kamath --- silpa/api/jsonrpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index a9b493f..cb833d6 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -19,7 +19,7 @@ _INTERNAL_ERROR = -32603 -bp = Blueprint('JSONRPC', __name__, url_prefix='/JSONRPC') +bp = Blueprint('JSONRPC', __name__, url_prefix='/api') @route(bp, '/JSONRPC', methods=['POST']) From 1534b8c4ae6ccb60f6b56fc0452c654b4f78d7c4 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 13 Apr 2014 17:18:04 +0530 Subject: [PATCH 21/96] New front end logic implemented Using blueprints redesigned the page handling logic (frontend) of SILPA to be much more cleaner and neater, dropped old files Signed-off-by: Vasudev Kamath --- silpa/frontend/__init__.py | 37 ++++++++++++ silpa/frontend/main.py | 112 ------------------------------------- silpa/frontend/pages.py | 52 +++++++++++++++++ silpa/webbridge.py | 65 --------------------- 4 files changed, 89 insertions(+), 177 deletions(-) create mode 100644 silpa/frontend/__init__.py delete mode 100644 silpa/frontend/main.py create mode 100644 silpa/frontend/pages.py delete mode 100644 silpa/webbridge.py diff --git a/silpa/frontend/__init__.py b/silpa/frontend/__init__.py new file mode 100644 index 0000000..d5591f1 --- /dev/null +++ b/silpa/frontend/__init__.py @@ -0,0 +1,37 @@ +from .. import factory +from functools import wraps +from jinja2 import PackageLoader, ChoiceLoader +from ..loadconfig import config + + +def create_app(settings_override=None): + app = factory.create_app(__name__,__path__, + settings_override) + load_module_templates(app) + return app + + +def load_module_templates(app): + modules = [module for module, need in config.items('modules') + if need == 'yes'] + templates = [app.jinja_loader] + for module in modules: + templates.append(PackageLoader(module)) + app.jinja_loader = ChoiceLoader(templates) + + +def route(bp, *args, **kwargs): + def decorator(f): + # FIXME: when we drop python2 support use unpack feature from + # python3 + baseurl, module_display = args[0], args[1] + for module in module_display: + bp.add_url_rule(baseurl + module, view_func=f) + + @wraps(f) + def wrapper(*args, **kwargs): + print(args) + print(kwargs) + return f(*args, **kwargs) + return f + return decorator diff --git a/silpa/frontend/main.py b/silpa/frontend/main.py deleted file mode 100644 index a249f1a..0000000 --- a/silpa/frontend/main.py +++ /dev/null @@ -1,112 +0,0 @@ -from flask import Flask -from logging import handlers, Formatter -from webbridge import WebBridge -from core.modulehelper import enabled_modules, BASEURL, modules -from jinja2 import PackageLoader, ChoiceLoader -import loadconfig -import logging -import os - - -def register_url(): - ''' - Function all form of URL which will be handled by SILPA service - - This function actually make this flask front end of SILPA independent - of any modules. It doesn't know what modules are present nor doesn't - know how to handle modules all the request will be promptly handed over - to WebBridge - ''' - # / or /baseurl for index page - baseurl = '/' if BASEURL == '/' else BASEURL - app.logger.debug("Registering the URL:{0}".format(baseurl)) - app.add_url_rule(baseurl, view_func=WebBridge.as_view(baseurl)) - # License page - app.logger.debug("Registering the URL:{0}".format(baseurl+"License")) - app.add_url_rule(baseurl+"License", view_func=WebBridge.as_view("license")) - # Credits Page - app.logger.debug("Registering the URL:{0}".format(baseurl+"Credits")) - app.add_url_rule(baseurl+"Credits", view_func=WebBridge.as_view("credits")) - # Contacts Page - app.logger.debug("Registering the URL:{0}".format(baseurl+"Contact")) - app.add_url_rule(baseurl+"Contact", view_func=WebBridge.as_view("contact")) - # Register all enabled modules - # baseurl/modulenames['module'] - for module in enabled_modules: - module_url = baseurl + "/" + module if not baseurl == "/" \ - else baseurl + module - app.logger.debug("Registering the URL:{0}".format(module_url)) - app.add_url_rule(module_url, view_func=WebBridge.as_view(module_url)) - - # JSONRPC url - jsonrpc_url = (baseurl + "/JSONRPC" if not baseurl == "/" - else baseurl + "JSONRPC") - app.logger.debug("Registering the URL:{0}".format(baseurl)) - app.add_url_rule(jsonrpc_url, view_func=WebBridge.as_view(jsonrpc_url)) - - -def add_templates(): - templates = [app.jinja_loader] - for key in modules.keys(): - if modules.get(key) == 'yes': - templates.append(PackageLoader(key)) - app.jinja_loader = ChoiceLoader(templates) - - -def configure_logging(): - ''' - This function configures logging for the SILPA applications using Flask's - internal logger. - - For now log file will be rotated 7 days once and 4 backups will be kept. - This can't be modified using configuration file as of now. - - Default logging level will be ERROR and can be modified from - configuration file. Log folder and file name can also be configured using - configuration file but make sure the path you give is writable for - Webserver user, otherwise this will lead to an error. - ''' - log_level = loadconfig.get('log_level') - log_folder = loadconfig.get('log_folder') - log_name = loadconfig.get('log_name') - filename = os.path.join(log_folder, log_name) - - handler = handlers.TimedRotatingFileHandler(filename, when='D', - interval=7, backupCount=4) - - level = logging.ERROR - - if log_level == "debug": - level = logging.DEBUG - elif log_level == "info": - level = logging.INFO - elif log_level == "warn": - level = logging.WARNING - elif log_level == "error": - level = logging.ERROR - - handler.setLevel(level) - handler.setFormatter(Formatter('%(asctime)s %(levelname)s: %(message)s \ - [in %(pathname)s %(lineno)d]')) - - app.logger.setLevel(level) - app.logger.addHandler(handler) - - -DEBUG = False - -# Basics -app = Flask(__name__) -app.config.from_object(__name__) - -# Logging -configure_logging() - -# Register URL's -register_url() - -# adds templates from imported modules -add_templates() - -if __name__ == '__main__': - app.run('0.0.0.0') diff --git a/silpa/frontend/pages.py b/silpa/frontend/pages.py new file mode 100644 index 0000000..f7b7977 --- /dev/null +++ b/silpa/frontend/pages.py @@ -0,0 +1,52 @@ +from flask import current_app, request, Blueprint, render_template, abort +from ..loadconfig import config +from . import route + +_BASE_URL = config.get('main', 'baseurl') +_modules = [module for module, need in config.items('modules') + if need == 'yes'] +_modulename_to_display = sorted((display_name + for module, display_name in + config.items('module_display') + if module in _modules)) +_display_module_map = {display_name: module for module, display_name in + config.items('module_display') + if module in _modules} + +bp = Blueprint('frontend', __name__) + + +@bp.route(_BASE_URL) +@bp.route(_BASE_URL + 'index.html') +@bp.route(_BASE_URL + 'License') +@bp.route(_BASE_URL + 'Credits') +@bp.route(_BASE_URL + 'Contact') +def serve_pages(): + if request.path == _BASE_URL: + return render_template('index.html', title='SILPA', + main_page=_BASE_URL, + modules=_modulename_to_display) + elif request.path == _BASE_URL + "License": + return render_template('license.html', title='SILPA License', + main_page=_BASE_URL, + modules=_modulename_to_display) + elif request.path == _BASE_URL + "Credits": + return render_template('credits.html', title='Credits', + main_page=_BASE_URL, + modules=_modulename_to_display) + elif request.path == _BASE_URL + "Contact": + return render_template('contact.html', title='Contact SILPA Team', + main_page=_BASE_URL, + modules=_modulename_to_display) + + +@route(bp, _BASE_URL, _modulename_to_display) +def serve_module_page(): + request_mod = request.path.split('/')[-1] + if request_mod in _display_module_map: + return render_template(_display_module_map[request_mod] + '.html', + title=request_mod, main_page=_BASE_URL, + modules=_modulename_to_display) + else: + # Did we encounter something which is not registered by us? + return abort(404) diff --git a/silpa/webbridge.py b/silpa/webbridge.py deleted file mode 100644 index 54b35c7..0000000 --- a/silpa/webbridge.py +++ /dev/null @@ -1,65 +0,0 @@ -''' - WebBridge is a bridging class between the flask front end of SILPA with - the module related information like rendering appropriate templates and - answering the JSONRPC requests and RESTful requests. - - This class is derived from MethodView class from flask.views -''' - -from flask.views import MethodView -from flask import render_template, request -from core.modulehelper import modulenames, enabled_modules, BASEURL -from core.jsonrpchandler import JSONRPCHandler -from flask import globals - -handler = JSONRPCHandler() - - -class WebBridge(MethodView): - def __init__(self): - self.app = globals.current_app - - def get(self): - ''' - Method from MethodView class which handles all HTTP GET requests - depending on request path render required template. - - If the query string is not None then its a RESTful request respond - with a proper response format - ''' - self.app.logger.debug('REQUEST PATH {0} and Base URL {1}' \ - .format(request.path, BASEURL)) - if request.path == BASEURL: - # request is for document root - return render_template('index.html', title="SILPA", \ - main_page=BASEURL, modules=enabled_modules) - elif request.path == BASEURL+ "License": - return render_template('license.html', title="License", - main_page=BASEURL, modules=enabled_modules) - elif request.path == BASEURL+ "Credits": - return render_template('credits.html', title="Credits", \ - main_page=BASEURL, modules=enabled_modules) - elif request.path == BASEURL+ "Contact": - return render_template('contact.html', title="Contact", \ - main_page=BASEURL, modules=enabled_modules) - - elif len(request.args) == 0: - # This is not query so lets serve the page - pathcomponent = request.path.split('/')[-1] - for module, name in modulenames.items(): - if pathcomponent == name: - return render_template(module + '.html', title=name, \ - main_page=BASEURL, modules=enabled_modules) - - def post(self): - ''' - Method from MethodView class which handles all HTTP POST requests - - In our case only POST requests will be for JSONRPC. - This is how all page work so we are retaining this else RESTful - will be more modern way of doing stuff than RPC - ''' - if request.path.split('/')[-1] == "JSONRPC": - if request.data != None: - result = handler.handle_request(request.data) - return result From 0812874169c50d55ebf495fa2cf9b950512e9a9b Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 13 Apr 2014 22:50:00 +0530 Subject: [PATCH 22/96] Fix mistakes in code * silpa/api/jsonrpc.py: instance variable name is error_response typoed it as errors_response. * silpa/factory.py: %(message) is formatter field for message, typoe it as %(messagename) Signed-off-by: Vasudev Kamath --- silpa/api/jsonrpc.py | 2 +- silpa/factory.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index cb833d6..65c5a5f 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -58,7 +58,7 @@ def __init__(self, data): # Unable to parse json error = JsonRpcError(code=_PARSE_ERRORS, message=e.message, data="") - self.errors_response = JsonRpcErrorResponse(jsonrpc="2.0", + self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", error=error, id='') else: # successfully parsed now verify request diff --git a/silpa/factory.py b/silpa/factory.py index 7936920..f67b132 100644 --- a/silpa/factory.py +++ b/silpa/factory.py @@ -43,7 +43,7 @@ def configure_logging(app): handler.setLevel(level) handler.setFormatter(Formatter('%(asctime)s %(levelname)s' + - ' %(messagename)7s - [in %(funcName)s' + + ' %(message)7s - [in %(funcName)s' + ' at %(pathname)s %(lineno)d]')) app.logger.setLevel(level) app.logger.addHandler(handler) From 2bc80975e6e1c6e0227971a71c77023cd16b81f8 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 22:48:16 +0530 Subject: [PATCH 23/96] Lets bring in the tests! Unit test for SILPA framework initiated, need to add more test cases here. Signed-off-by: Vasudev Kamath --- tests/__init__.py | 23 ++++++++++++++++++ tests/api/__init__.py | 11 +++++++++ tests/api/jsonrpc_tests.py | 44 ++++++++++++++++++++++++++++++++++ tests/settings.py | 10 ++++++++ tests/utils.py | 48 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/api/__init__.py create mode 100644 tests/api/jsonrpc_tests.py create mode 100644 tests/settings.py create mode 100644 tests/utils.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..85e6898 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,23 @@ + +from unittest import TestCase +from .utils import SILPATestCaseMixin + + +class SILPATestCase(TestCase): + pass + + +class SILPAAppTestCase(SILPATestCaseMixin, SILPATestCase): + def _create_app(self): + raise NotImplementedError + + def setUp(self): + super(SILPAAppTestCase, self).setUp() + self.app = self._create_app() + self.client = self.app.test_client() + self.app_context = self.app.app_context() + self.app_context.push() + + def tearDown(self): + super(SILPAAppTestCase, self).tearDown() + self.app_context.pop() diff --git a/tests/api/__init__.py b/tests/api/__init__.py new file mode 100644 index 0000000..fb2e1d2 --- /dev/null +++ b/tests/api/__init__.py @@ -0,0 +1,11 @@ +from silpa.api import create_app +from .. import SILPAAppTestCase, settings + + +class SILPAApiTestCase(SILPAAppTestCase): + + def _create_app(self): + return create_app(settings) + + def setUp(self): + super(SILPAApiTestCase, self).setUp() diff --git a/tests/api/jsonrpc_tests.py b/tests/api/jsonrpc_tests.py new file mode 100644 index 0000000..52a467c --- /dev/null +++ b/tests/api/jsonrpc_tests.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +""" + tests.api.jsonrpc_tests + ~~~~~~~~~~~~~~~~~~~~~~~ + + json-rpc api test module +""" + +from . import SILPAApiTestCase +from silpa.api import jsonrpc +import json +import random + + +class JsonRpcApiTestCase(SILPAApiTestCase): + + def assertJsonRpcMethodNotFound(self, response): + response_dict = json.loads(self.assertBadJson(response).data) + self.assertIn('error', response_dict) + error_obj = jsonrpc.JsonRpcError(**response_dict['error']) + self.assertEquals(error_obj.code, jsonrpc.METHOD_NOT_FOUND) + + def assertJsonRpcInvalidRequest(self, response): + response_dict = json.loads(self.assertBadJson(response).data) + self.assertIn('error', response_dict) + error_obj = jsonrpc.JsonRpcErrorResponse(**response_dict['error']) + self.assertEquals(error_obj.code, jsonrpc.INVALID_REQUEST) + + def assertJsonRpcParseErrors(self, response): + response_dict = json.loads(self.assertBadJson(response).data) + self.assertIn('error', response_dict) + error_obj = jsonrpc.JsonRpcErrorResponse(**response_dict['error']) + self.assertEquals(error_obj.code, jsonrpc.PARSE_ERRORS) + + def assertJsonRpcResult(self, response): + response_dict = json.loads(self.assertJsonOk(response).data) + self.assertIn('result', response_dict) + + def test_methodnot_found(self): + data = dict(jsonrpc='2.0', + method='transliteration.transliterate', + params=['Hello World!', 'kn_IN'], + id=random.randint(1, 1000)) + self.assertJsonRpcMethodNotFound(self.jpost('/api/JSONRPC', data=data)) diff --git a/tests/settings.py b/tests/settings.py new file mode 100644 index 0000000..b8ef5fd --- /dev/null +++ b/tests/settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" + tests.settings + ~~~~~~~~~~~~~~ + + tests settings module +""" + +DEBUG = False +TESTING = True diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..1fd0b5d --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,48 @@ +import json + + +class SILPATestCaseMixin(object): + def _json_data(self, kwargs): + if 'data' in kwargs: + kwargs['data'] = json.dumps(kwargs['data']) + if 'content_type' not in kwargs: + kwargs['content_type'] = 'application/json' + return kwargs + + def _request(self, method, *args, **kwargs): + kwargs.setdefault('content_type', 'text/html') + kwargs.setdefault('follow_redirects', True) + return method(*args, **kwargs) + + def get(self, *args, **kwargs): + return self._request(*args, **kwargs) + + def post(self, *args, **kwargs): + return self._request(*args, **kwargs) + + def jpost(self, *args, **kwargs): + return self._request(self.client.post, *args, + **self._json_data(kwargs)) + + def assertStatusCode(self, response, status_code): + self.assertEquals(status_code, response.status_code) + return response + + def assertOk(self, response): + return self.assertStatusCode(response, 200) + + def assertBadRequest(self, response): + return self.assertStatusCode(response, 400) + + def assertContentType(self, response, content_type): + self.assertEquals(content_type, response.headers['Content-Type']) + return response + + def assertJson(self, response): + return self.assertContentType(response, 'application/json') + + def assertOkJson(self, response): + return self.assertOk(self.assertJson(response)) + + def assertBadJson(self, response): + return self.assertBadRequest(self.assertJson(response)) From 716a25c48766964d06b18824987456b66bb9dc83 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 22:49:27 +0530 Subject: [PATCH 24/96] Fixes after adding test cases * make the error codes public so it can be accessed from outside the module, like test cases * error namedtuple needs to be translated into dict before returning the result, else error will be just a list!. * Fix the logic for method calling Signed-off-by: Vasudev Kamath --- silpa/api/jsonrpc.py | 80 ++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index 65c5a5f..3bb7c11 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -1,3 +1,4 @@ +from __future__ import print_function from collections import namedtuple from flask import Blueprint, request from . import route @@ -12,11 +13,11 @@ JsonRpcResultResponse = namedtuple('JsonRpcResultResponse', ['jsonrpc', 'result', 'id']) -_PARSE_ERRORS = -32700 -_INVALID_REQUEST = -32600 -_METHOD_NOT_FOUND = -32601 -_INVALID_PARAMS = -32602 -_INTERNAL_ERROR = -32603 +PARSE_ERRORS = -32700 +INVALID_REQUEST = -32600 +METHOD_NOT_FOUND = -32601 +INVALID_PARAMS = -32602 +INTERNAL_ERROR = -32603 bp = Blueprint('JSONRPC', __name__, url_prefix='/api') @@ -39,16 +40,24 @@ def handle_jsonrpc_call(): rpc_object() except Exception as e: # Possible errors in execution of method - error = JsonRpcError(code=_INTERNAL_ERROR, message=e.message, + error = JsonRpcError(code=INTERNAL_ERROR, message=e.message, data=dict(zip(rpc_object.request._fields, rpc_object.request))) return dict(jsonrpc="2.0", - error=dict(zip(error._fields, error), - id=rpc_object.request.id)) + error=dict(zip(error._fields, error)), + id=rpc_object.request.id) + else: + if rpc_object.error_response is None: + # success! + return dict(zip(rpc_object.response._fields, + rpc_object.response)) + else: + return dict(zip(rpc_object.error_response._fields, + rpc_object.error_response)) class JsonRpc(object): - __slots__ = ['request', 'response', 'error_response'] + __slots__ = ['request', 'response', 'error_response', 'instance_type'] def __init__(self, data): self.error_response = None @@ -56,16 +65,20 @@ def __init__(self, data): self.request = JsonRpcRequest(**json.loads(data)) except Exception as e: # Unable to parse json - error = JsonRpcError(code=_PARSE_ERRORS, message=e.message, + error = JsonRpcError(code=PARSE_ERRORS, message=e.message, data="") self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", - error=error, id='') + error=dict(zip( + error._fields, + error)), + id='') else: # successfully parsed now verify request if self.request.jsonrpc != "2.0" or len(self.request.method) == 0 \ - or len(self.request.params) == 0 or len(self.id) == 0: + or len(self.request.params) == 0 or \ + len(str(self.request.id)) == 0: # not valid request - error = JsonRpcError(code=_INVALID_REQUEST, + error = JsonRpcError(code=INVALID_REQUEST, message="Not a valid JSON-RPC request", data='') self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", @@ -73,27 +86,36 @@ def __init__(self, data): def __call__(self): # process request here + print("call function") module, method = self.request.method.split('.') - if module not in sys.modules: # Module is not yet loaded? handle this + print("module not loaded") pass else: # module is present in sys + print("module loaded") mod = sys.modules[module] - if not hasattr(mod, method) and \ - type(getattr(mod, method).__name__ == 'function'): - # method not found - error = JsonRpcError(code=_METHOD_NOT_FOUND, - message="requested method not found", - data="Requested method {}".format( - self.request.method)) - self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", - error=error, - id=self.request.id) + if hasattr(mod, 'getInstance'): + print("method has getInstance") + instance = getattr(mod, 'getInstance')() + if not hasattr(instance, method): + result = getattr(instance, method)(*self.request.params) + print(result) + self.response = JsonRpcResultResponse(jsonrpc="2.0", + result=result, + id=self.request.id) + else: + # method not found + error = JsonRpcError(code=METHOD_NOT_FOUND, + message="requested method not found", + data="Requested method {}".format( + self.request.method)) + error_dict = dict(zip(error._fields, error)) + self.error_response = JsonRpcErrorResponse( + jsonrpc="2.0", + error=error_dict, + id=self.request.id) else: - # Method present so lets call it to get result - result = getattr(module, method)(*self.request.params) - self.response = JsonRpcResultResponse(jsonrpc="2.0", - result=result, - id=self.request.id) + # module doesn't provide an interface to us? + pass From 9273fea77edc677c1bf6ed7db156bf048970c358 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 22:50:33 +0530 Subject: [PATCH 25/96] Add exception message in import failure message --- silpa/moduleloader.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/silpa/moduleloader.py b/silpa/moduleloader.py index c6c7853..4c3a877 100644 --- a/silpa/moduleloader.py +++ b/silpa/moduleloader.py @@ -12,6 +12,7 @@ def load_modules(): for module in _modules: try: importlib.import_module(module) - except ImportError: - print("Failed to import {module}".format(module=module), + except ImportError as e: + print("Failed to import {module}: {message}". + format(module=module, message=e.message), file=sys.stderr) From 3bd5d29badec697aca92b1ece5baaf82b4e86552 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 22:50:52 +0530 Subject: [PATCH 26/96] Script render is hard to install in virtualenv lets disable it Signed-off-by: Vasudev Kamath --- silpa/silpa.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silpa/silpa.conf b/silpa/silpa.conf index af65e6d..b5d7c49 100644 --- a/silpa/silpa.conf +++ b/silpa/silpa.conf @@ -37,7 +37,7 @@ shingling = yes textsimilarity = yes indicstemmer = yes katapayadi = yes -scriptrender = yes +#scriptrender = yes [module_display] # Section gives names to be displayed on HTML and From 4cc8a5e544ff94572e8e2d1b56e72be982b923be Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 23:13:25 +0530 Subject: [PATCH 27/96] Add test for parse errors of request * silpa/api/jsonrpc.py: Remove unwanted print statement and fix the error dict wrapping in error response. * tests/api/jsonrpc_tests.py: Tests for parse errors Signed-off-by: Vasudev Kamath --- silpa/api/jsonrpc.py | 9 +++------ tests/api/jsonrpc_tests.py | 13 +++++++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index 3bb7c11..41a3e6f 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -81,27 +81,24 @@ def __init__(self, data): error = JsonRpcError(code=INVALID_REQUEST, message="Not a valid JSON-RPC request", data='') + error_dict = dict(zip(error._fields, error)) self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", - error=error, id='') + error=error_dict, + id='') def __call__(self): # process request here - print("call function") module, method = self.request.method.split('.') if module not in sys.modules: # Module is not yet loaded? handle this - print("module not loaded") pass else: # module is present in sys - print("module loaded") mod = sys.modules[module] if hasattr(mod, 'getInstance'): - print("method has getInstance") instance = getattr(mod, 'getInstance')() if not hasattr(instance, method): result = getattr(instance, method)(*self.request.params) - print(result) self.response = JsonRpcResultResponse(jsonrpc="2.0", result=result, id=self.request.id) diff --git a/tests/api/jsonrpc_tests.py b/tests/api/jsonrpc_tests.py index 52a467c..8444a3e 100644 --- a/tests/api/jsonrpc_tests.py +++ b/tests/api/jsonrpc_tests.py @@ -23,13 +23,13 @@ def assertJsonRpcMethodNotFound(self, response): def assertJsonRpcInvalidRequest(self, response): response_dict = json.loads(self.assertBadJson(response).data) self.assertIn('error', response_dict) - error_obj = jsonrpc.JsonRpcErrorResponse(**response_dict['error']) + error_obj = jsonrpc.JsonRpcError(**response_dict['error']) self.assertEquals(error_obj.code, jsonrpc.INVALID_REQUEST) def assertJsonRpcParseErrors(self, response): response_dict = json.loads(self.assertBadJson(response).data) self.assertIn('error', response_dict) - error_obj = jsonrpc.JsonRpcErrorResponse(**response_dict['error']) + error_obj = jsonrpc.JsonRpcError(**response_dict['error']) self.assertEquals(error_obj.code, jsonrpc.PARSE_ERRORS) def assertJsonRpcResult(self, response): @@ -42,3 +42,12 @@ def test_methodnot_found(self): params=['Hello World!', 'kn_IN'], id=random.randint(1, 1000)) self.assertJsonRpcMethodNotFound(self.jpost('/api/JSONRPC', data=data)) + + def test_request_parseerror(self): + # notice missing jsonrpc key? this is invalid request but our + # code will trigger parse error as we can't convert this to + # JsonRpcRequest object + data = dict(method='transliteration.transliterate', + params=['Hello World!', 'kn_IN'], + id=random.randint(1, 1000)) + self.assertJsonRpcParseErrors(self.jpost('/api/JSONRPC', data=data)) From aa5fe5e0395fe0b46538bf7ea22b6220482b5f52 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 23:20:52 +0530 Subject: [PATCH 28/96] Intial travis integration and toxing --- .travis.yml | 19 +++++++++++++++++++ Makefile | 13 +++++++++++++ tox.ini | 11 +++++++++++ 3 files changed, 43 insertions(+) create mode 100644 .travis.yml create mode 100644 Makefile create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3d92db4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: python +python: + - "2.7" + - "3.3" +install: + - pip install -r requirements.txt + - pip install -r requirements-modules.txt + - pip install coveralls +script: make travis +after_success: coveralls +notifications: + email: + - silpa-discuss@nongnu.org + irc: + channels: + - "irc.freenode.net#silpa" + on_success: change + on_failure: change + use_notice: true \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..25c5dcf --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +travis: + nosetests -s --with-coverage --cover-package=silpa + flake8 silpa tests + +clean: + find . -name "*.pyc" -exec rm -vf {} \; + find -name __pycache__ -deete + +tox: + tox + +flake: + flake8 silpa tests diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..3eb4787 --- /dev/null +++ b/tox.ini @@ -0,0 +1,11 @@ +[tox] +envlist = py27, py33, flake8 +[testenv] +commands = nosetests +deps = + nose + +[testenv:flake8] +deps = + flake8 +commands = flake8 silpa tests \ No newline at end of file From 1534a58c3d219eab253e9b73b45328d5ae315b3b Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 23:28:45 +0530 Subject: [PATCH 29/96] scriptrender has installation problem leave it for now --- requirements-modules.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-modules.txt b/requirements-modules.txt index f7f0df5..a044b77 100644 --- a/requirements-modules.txt +++ b/requirements-modules.txt @@ -5,7 +5,7 @@ -e git+https://github.com/Project-SILPA/Soundex.git#egg=soundex -e git+https://github.com/Project-SILPA/silpa-common.git#egg=silpa_common -e git+https://github.com/Project-SILPA/shingling.git#egg=shingling --e git+https://github.com/Project-SILPA/scriptrender.git#egg=scriptrender +#-e git+https://github.com/Project-SILPA/scriptrender.git#egg=scriptrender -e git+https://github.com/Project-SILPA/payyans.git#egg=payyans -e git+https://github.com/Project-SILPA/normalizer.git#egg=normalizer -e git+https://github.com/Project-SILPA/inexactsearch.git#egg=inexactsearch From 999fea4a2d53560bf20b0412274ad2ee40b24e69 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 23:32:10 +0530 Subject: [PATCH 30/96] flake8 is needed for testing install it in travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3d92db4..77a2c66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ install: - pip install -r requirements.txt - pip install -r requirements-modules.txt - pip install coveralls + - pip install flake8 script: make travis after_success: coveralls notifications: From 54e0863a79beedb216aec8e7606ae9d15cb112aa Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 19 Apr 2014 23:45:31 +0530 Subject: [PATCH 31/96] flake8 fixes --- silpa/api/__init__.py | 2 -- silpa/frontend/__init__.py | 2 +- silpa/frontend/pages.py | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/silpa/api/__init__.py b/silpa/api/__init__.py index e7daace..50facbb 100644 --- a/silpa/api/__init__.py +++ b/silpa/api/__init__.py @@ -3,8 +3,6 @@ from .. import factory from .. import moduleloader -import os - def route(bp, *args, **kwargs): def decorator(f): diff --git a/silpa/frontend/__init__.py b/silpa/frontend/__init__.py index d5591f1..54ecd42 100644 --- a/silpa/frontend/__init__.py +++ b/silpa/frontend/__init__.py @@ -5,7 +5,7 @@ def create_app(settings_override=None): - app = factory.create_app(__name__,__path__, + app = factory.create_app(__name__, __path__, settings_override) load_module_templates(app) return app diff --git a/silpa/frontend/pages.py b/silpa/frontend/pages.py index f7b7977..0306923 100644 --- a/silpa/frontend/pages.py +++ b/silpa/frontend/pages.py @@ -1,4 +1,4 @@ -from flask import current_app, request, Blueprint, render_template, abort +from flask import request, Blueprint, render_template, abort from ..loadconfig import config from . import route From 242ffbd5ef0136f2303c4497962186bc58cd7100 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Fri, 25 Apr 2014 15:06:40 +0530 Subject: [PATCH 32/96] Use configparser library for both python2 and 3 ConfigParser of python2 is replaced by configparser in python3, and for backward compatibility purpose configparser is provided via pypi. Use this to provide compatibility for both python2 and python3 --- silpa/loadconfig.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/silpa/loadconfig.py b/silpa/loadconfig.py index 0fc2ada..63d7ab6 100644 --- a/silpa/loadconfig.py +++ b/silpa/loadconfig.py @@ -1,6 +1,6 @@ __all__ = ['IncompleteConfigError', 'config'] -import ConfigParser +import configparser import os class IncompleteConfigError(Exception): @@ -18,16 +18,10 @@ def __str__(self): section=self.section) -class _Config(ConfigParser.ConfigParser): - _instance = None - - def __new__(cls, *args, **kwargs): - if not cls._instance: - cls._instance = super(_Config, cls).__new__(cls, *args, **kwargs) - return cls._instance +class _Config(configparser.ConfigParser): def __init__(self, location="silpa.conf"): - ConfigParser.ConfigParser.__init__(self) + configparser.ConfigParser.__init__(self) self.read(location) self.verify From 0442d7b5542bb8406e39eedf66b58d84becc3aaf Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 26 Apr 2014 13:06:40 +0530 Subject: [PATCH 33/96] configparser is py2 only dependency --- requirements-py2.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements-py2.txt diff --git a/requirements-py2.txt b/requirements-py2.txt new file mode 100644 index 0000000..af42dda --- /dev/null +++ b/requirements-py2.txt @@ -0,0 +1 @@ +configparser From 00530607f13b9f232f018757054e5fa90d2f1b18 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 26 Apr 2014 13:06:56 +0530 Subject: [PATCH 34/96] install py2 only dependency on travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 77a2c66..53783a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: install: - pip install -r requirements.txt - pip install -r requirements-modules.txt + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install -r requirements-2.7.txt; fi - pip install coveralls - pip install flake8 script: make travis From b599bc4277d8be5c9f473921a1f0cb84f128321e Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 26 Apr 2014 13:13:00 +0530 Subject: [PATCH 35/96] fix typo in requirements file name for py2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 53783a6..411bd6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ python: install: - pip install -r requirements.txt - pip install -r requirements-modules.txt - - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install -r requirements-2.7.txt; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install -r requirements-py2.txt; fi - pip install coveralls - pip install flake8 script: make travis From 16bac5bb38e2a1eb7b428ea62f7c7a04501b4e8d Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 26 Apr 2014 13:16:41 +0530 Subject: [PATCH 36/96] use relative imports for modules within package --- silpa/factory.py | 2 +- silpa/moduleloader.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/silpa/factory.py b/silpa/factory.py index f67b132..80fe7c8 100644 --- a/silpa/factory.py +++ b/silpa/factory.py @@ -4,7 +4,7 @@ import os from flask import Flask, Blueprint -from loadconfig import config +from .loadconfig import config from logging import Formatter from logging.handlers import TimedRotatingFileHandler diff --git a/silpa/moduleloader.py b/silpa/moduleloader.py index 4c3a877..f995ba3 100644 --- a/silpa/moduleloader.py +++ b/silpa/moduleloader.py @@ -1,5 +1,5 @@ from __future__ import print_function -from loadconfig import config +from .loadconfig import config import importlib import sys From ebc577f86f6f9d9ca07aadaca93462126457f90b Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sat, 26 Apr 2014 13:21:35 +0530 Subject: [PATCH 37/96] pep8 fix, give 2 blank lines before class declaratn --- silpa/loadconfig.py | 1 + 1 file changed, 1 insertion(+) diff --git a/silpa/loadconfig.py b/silpa/loadconfig.py index 63d7ab6..a114977 100644 --- a/silpa/loadconfig.py +++ b/silpa/loadconfig.py @@ -3,6 +3,7 @@ import configparser import os + class IncompleteConfigError(Exception): def __init__(self, section, option): self.section = section From 9905f9e6c9280447584826580233eb13ec382503 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 27 Apr 2014 11:19:09 +0530 Subject: [PATCH 38/96] Fix wrong condition testing We should try invoke method if instance has method with that name, I was negating the condition which was wrong --- silpa/api/jsonrpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index 41a3e6f..09c93c3 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -97,7 +97,7 @@ def __call__(self): mod = sys.modules[module] if hasattr(mod, 'getInstance'): instance = getattr(mod, 'getInstance')() - if not hasattr(instance, method): + if hasattr(instance, method): result = getattr(instance, method)(*self.request.params) self.response = JsonRpcResultResponse(jsonrpc="2.0", result=result, From f6dfdf8b4af659ea2f18199c1460ea1090b95503 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 27 Apr 2014 11:20:00 +0530 Subject: [PATCH 39/96] New test added Test to check success result from jsonrpc call --- tests/api/jsonrpc_tests.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/api/jsonrpc_tests.py b/tests/api/jsonrpc_tests.py index 8444a3e..0090fc5 100644 --- a/tests/api/jsonrpc_tests.py +++ b/tests/api/jsonrpc_tests.py @@ -51,3 +51,10 @@ def test_request_parseerror(self): params=['Hello World!', 'kn_IN'], id=random.randint(1, 1000)) self.assertJsonRpcParseErrors(self.jpost('/api/JSONRPC', data=data)) + + def test_result_jsonrpc(self): + data = dict(jsonrpc='2.0', + method='transliteration.transliterate', + params=['Hello World!', 'kn_IN'], + id=random.randint(1, 1000)) + self.assertJsonRpcResult(self.jpost('/api/JSONRPC', data=data)) From a84d9455b65b0325b7bb3fdf97e6ffd9761f304e Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 27 Apr 2014 11:20:28 +0530 Subject: [PATCH 40/96] Typo fixes and method name change. * Method to check json alright is assertOkJson not assertJsonOk * use non existent method as param to jsonrpc call for testing method not found return value from jsonrpc Signed-off-by: Vasudev Kamath --- tests/api/jsonrpc_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/api/jsonrpc_tests.py b/tests/api/jsonrpc_tests.py index 0090fc5..b942933 100644 --- a/tests/api/jsonrpc_tests.py +++ b/tests/api/jsonrpc_tests.py @@ -33,12 +33,12 @@ def assertJsonRpcParseErrors(self, response): self.assertEquals(error_obj.code, jsonrpc.PARSE_ERRORS) def assertJsonRpcResult(self, response): - response_dict = json.loads(self.assertJsonOk(response).data) + response_dict = json.loads(self.assertOkJson(response).data) self.assertIn('result', response_dict) def test_methodnot_found(self): data = dict(jsonrpc='2.0', - method='transliteration.transliterate', + method='transliteration.transliter', params=['Hello World!', 'kn_IN'], id=random.randint(1, 1000)) self.assertJsonRpcMethodNotFound(self.jpost('/api/JSONRPC', data=data)) From 09e2b21184fe4b15c168be88bb5c6d406fed0974 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 27 Apr 2014 11:22:26 +0530 Subject: [PATCH 41/96] disable python3 tests For the time being till the modules are ported to python3 disable the tests for py3 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 411bd6e..ddce316 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: python python: - "2.7" - - "3.3" + # - "3.3" install: - pip install -r requirements.txt - pip install -r requirements-modules.txt From b8cd8beebb115b5d02f8163f308c1e86f23da217 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 27 Apr 2014 11:56:35 +0530 Subject: [PATCH 42/96] Add build status image to README --- README | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README b/README index bc9dcdc..db0b50d 100644 --- a/README +++ b/README @@ -1,5 +1,8 @@ SILPA ========== + +[![Build Status](https://travis-ci.org/Project-SILPA/Silpa-Flask.svg?branch=development)](https://travis-ci.org/Project-SILPA/Silpa-Flask) + SILPA - Indian Language computing platform which provides a web interface for different Indian language computing python modules. This is hosted at From 0a3ee1a89a153da31335f899bf3cb882638dcfd7 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 27 Apr 2014 12:32:10 +0530 Subject: [PATCH 43/96] Add coveralls status --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index db0b50d..7a864fd 100644 --- a/README +++ b/README @@ -2,6 +2,7 @@ SILPA ========== [![Build Status](https://travis-ci.org/Project-SILPA/Silpa-Flask.svg?branch=development)](https://travis-ci.org/Project-SILPA/Silpa-Flask) +[![Coverage Status](https://coveralls.io/repos/Project-SILPA/Silpa-Flask/badge.png?branch=development)](https://coveralls.io/r/Project-SILPA/Silpa-Flask?branch=development) SILPA - Indian Language computing platform which provides a web interface for different Indian language computing python modules. This is hosted at From 58678bc63f568202e3410650d1b5dbd7e2137906 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Mon, 28 Apr 2014 16:22:27 +0530 Subject: [PATCH 44/96] Rewrite the routing logic for frontend Routing logic previously written was unnecessarily complicated and was not very well utilizing the power of Flask. Rewritten with following changes. * silpa/frontend/__init__.py: Drop the route decorator generator, its not needed we can utilize the route from Blueprint instead. * silpa/frontend/pages.py: Single function serve_pages can handle both module pages and normal pages. argument should be passed from template. Drop all `request' related thigs we can get page requested from page argument to serve_pages function. * silpa/frontend/templates/{silpa,index}.html: url_for usage for pages and module pages should be different from previous code, we are using Blueprint instead of MethodView hence we can't get url generated from giving the actual URL, instead url should be generated by specifying the method and passing end point of url as *page* parameter to it Signed-off-by: Vasudev Kamath --- silpa/frontend/templates/index.html | 12 ++++++++++-- silpa/frontend/templates/silpa.html | 10 +++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/silpa/frontend/templates/index.html b/silpa/frontend/templates/index.html index ecf859d..03e46d0 100644 --- a/silpa/frontend/templates/index.html +++ b/silpa/frontend/templates/index.html @@ -5,8 +5,16 @@

Welcome to SILPA!

-

Swathanthra Indian Language Processing Applications(SILPA) is a web platform to host Free Software language processing applications. It consists of a web framework and a set of applications for processing Indian languages. It is a platform for porting existing and upcoming language processing applications to the web.

-

Silpa can also be used as a python library or as a webservice from other applications. Silpa is currently under development. If you are interested in contributing contact the developers

+

Swathanthra Indian Language Processing + Applications(SILPA) is a web platform to host Free Software language + processing applications. It consists of a web framework and a set of + applications for processing Indian languages. It is a platform for + porting existing and upcoming language processing applications to + the web.

+

Silpa can also be used as a python library or as a + webservice from other applications. Silpa is currently under + development. If you are interested in contributing + contact the developers

Read the project announcement

diff --git a/silpa/frontend/templates/silpa.html b/silpa/frontend/templates/silpa.html index dc73acc..2cf3a41 100644 --- a/silpa/frontend/templates/silpa.html +++ b/silpa/frontend/templates/silpa.html @@ -15,7 +15,7 @@ From 50e1104d6125841441940ac66c5e92aefa40abdd Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Mon, 28 Apr 2014 16:29:07 +0530 Subject: [PATCH 45/96] Use werkzeug DispatcherMiddleware for apps We have 2 apps now within same application, one is for api and second one is for frontend, we will use DispatcherMiddleware to register both these apps into single WSGI frontend. Signed-off-by: Vasudev Kamath --- wsgi.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 wsgi.py diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..5e47d59 --- /dev/null +++ b/wsgi.py @@ -0,0 +1,7 @@ +from werkzeug.serving import run_simple +from werkzeug.wsgi import DispatcherMiddleware + +from silpa import api, frontend + +application = DispatcherMiddleware(frontend.create_app(), + {'/api': api.create_app()}) From bab34baee5af1a226863cc1dc9c6589c5a16c622 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Tue, 29 Apr 2014 22:29:23 +0530 Subject: [PATCH 46/96] Add tests for frontend html Added test for index page, it just checks if it can find the a proper title for the page Signed-off-by: Vasudev Kamath --- tests/frontend/__init__.py | 19 +++++++++++++++++++ tests/frontend/mainpage_tests.py | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/frontend/__init__.py create mode 100644 tests/frontend/mainpage_tests.py diff --git a/tests/frontend/__init__.py b/tests/frontend/__init__.py new file mode 100644 index 0000000..8ad6f1c --- /dev/null +++ b/tests/frontend/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" + tests.frontend + ~~~~~~~~~~~~~~ + + frontend tests package +""" + +from silpa.frontend import create_app +from .. import SILPAAppTestCase, settings + + +class SILPAFrontEndTestCase(SILPAAppTestCase): + + def _create_app(self): + return create_app(settings) + + def setUp(self): + super(SILPAFrontEndTestCase, self).setUp() diff --git a/tests/frontend/mainpage_tests.py b/tests/frontend/mainpage_tests.py new file mode 100644 index 0000000..9a5663b --- /dev/null +++ b/tests/frontend/mainpage_tests.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +""" + tests.frontend.mainpage_tests + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + main page frontend tests module +""" + +from . import SILPAFrontEndTestCase + + +class MainPageTestCase(SILPAFrontEndTestCase): + + def test_indexpage(self): + r = self.get('/') + self.assertIn(' SILPA - Indic Language Computing Platform ' + + '', self.assertOk(r).data) From eea5f7990dd8a3a737877aef84b2d839098b3171 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Tue, 29 Apr 2014 22:30:33 +0530 Subject: [PATCH 47/96] get and post method should pass methods to _request _request function require method to invoke from test client this should be passed from the function which invokes this method. --- tests/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index 1fd0b5d..c725b57 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -15,10 +15,10 @@ def _request(self, method, *args, **kwargs): return method(*args, **kwargs) def get(self, *args, **kwargs): - return self._request(*args, **kwargs) + return self._request(self.client.get, *args, **kwargs) def post(self, *args, **kwargs): - return self._request(*args, **kwargs) + return self._request(self.client.post, *args, **kwargs) def jpost(self, *args, **kwargs): return self._request(self.client.post, *args, From 576eba481c24686a4f58a51e63368c957c3c2d98 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Tue, 29 Apr 2014 22:59:25 +0530 Subject: [PATCH 48/96] exclude site-package files in coverage report --- .coveragerc | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..a238df8 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,11 @@ +[run] +omit = + */python?.?/* + */lib-python/?.?/*.py + */lib_pypy/_*.py + */site-packages/nose/* + */pypy/* + +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover \ No newline at end of file From ba0697efee789429c51e9fe5b5788d386dcb94e3 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Thu, 1 May 2014 11:52:07 +0530 Subject: [PATCH 49/96] Enable tests for all pages and modules --- tests/frontend/mainpage_tests.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/frontend/mainpage_tests.py b/tests/frontend/mainpage_tests.py index 9a5663b..30032bb 100644 --- a/tests/frontend/mainpage_tests.py +++ b/tests/frontend/mainpage_tests.py @@ -15,3 +15,35 @@ def test_indexpage(self): r = self.get('/') self.assertIn(' SILPA - Indic Language Computing Platform ' + '', self.assertOk(r).data) + + def test_pages(self): + r = self.get('/License') + self.assertIn('

License

', self.assertOk(r).data) + + r = self.get('/Contact') + self.assertIn('

Contacts

', self.assertOk(r).data) + + r = self.get('/Credits') + self.assertIn('

Credits

', self.assertOk(r).data) + + def test_moduleloaded(self): + from silpa.loadconfig import config + modules = [module for module, need in config.items('modules') + if need == "yes"] + module_display = sorted((display_name + for module, display_name in + config.items('module_display') + if module in modules)) + r = self.get('/') + self.assertOk(r) + + for m in module_display: + # FIXME: Returned by configparser is unicode string for + # some reason breaks with assertIn not sure why :( + self.assertIn(m.encode('utf-8'), r.data) + + # TODO: URL in modules needs to be fixed befor enabling + # below tests + # r1 = self.get('/' + m) + # self.assertIn(' {} - Indic Language Computing Platform ' + + # '', self.assertOk(r1).data) From 865478534188bda43db6a7908f6097d373c28d09 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Thu, 1 May 2014 12:04:16 +0530 Subject: [PATCH 50/96] Missed rewriting logic from commit 58678bc The explanation for this commit was included in commit 58678bc but some how it didn't get included in that commit, now fixed. Signed-off-by: Vasudev Kamath --- silpa/frontend/__init__.py | 18 ------------------ silpa/frontend/pages.py | 39 ++++++++++++++++---------------------- 2 files changed, 16 insertions(+), 41 deletions(-) diff --git a/silpa/frontend/__init__.py b/silpa/frontend/__init__.py index 54ecd42..fdf9fa3 100644 --- a/silpa/frontend/__init__.py +++ b/silpa/frontend/__init__.py @@ -1,5 +1,4 @@ from .. import factory -from functools import wraps from jinja2 import PackageLoader, ChoiceLoader from ..loadconfig import config @@ -18,20 +17,3 @@ def load_module_templates(app): for module in modules: templates.append(PackageLoader(module)) app.jinja_loader = ChoiceLoader(templates) - - -def route(bp, *args, **kwargs): - def decorator(f): - # FIXME: when we drop python2 support use unpack feature from - # python3 - baseurl, module_display = args[0], args[1] - for module in module_display: - bp.add_url_rule(baseurl + module, view_func=f) - - @wraps(f) - def wrapper(*args, **kwargs): - print(args) - print(kwargs) - return f(*args, **kwargs) - return f - return decorator diff --git a/silpa/frontend/pages.py b/silpa/frontend/pages.py index 0306923..cfaf3a1 100644 --- a/silpa/frontend/pages.py +++ b/silpa/frontend/pages.py @@ -1,6 +1,5 @@ -from flask import request, Blueprint, render_template, abort +from flask import Blueprint, render_template, abort from ..loadconfig import config -from . import route _BASE_URL = config.get('main', 'baseurl') _modules = [module for module, need in config.items('modules') @@ -16,37 +15,31 @@ bp = Blueprint('frontend', __name__) -@bp.route(_BASE_URL) -@bp.route(_BASE_URL + 'index.html') -@bp.route(_BASE_URL + 'License') -@bp.route(_BASE_URL + 'Credits') -@bp.route(_BASE_URL + 'Contact') -def serve_pages(): - if request.path == _BASE_URL: +@bp.route(_BASE_URL, defaults={'page': 'index.html'}) +@bp.route(_BASE_URL + '') +def serve_pages(page): + if page == "index.html": return render_template('index.html', title='SILPA', main_page=_BASE_URL, modules=_modulename_to_display) - elif request.path == _BASE_URL + "License": + elif page == "License": return render_template('license.html', title='SILPA License', main_page=_BASE_URL, modules=_modulename_to_display) - elif request.path == _BASE_URL + "Credits": + elif page == "Credits": return render_template('credits.html', title='Credits', main_page=_BASE_URL, modules=_modulename_to_display) - elif request.path == _BASE_URL + "Contact": + elif page == "Contact": return render_template('contact.html', title='Contact SILPA Team', main_page=_BASE_URL, modules=_modulename_to_display) - - -@route(bp, _BASE_URL, _modulename_to_display) -def serve_module_page(): - request_mod = request.path.split('/')[-1] - if request_mod in _display_module_map: - return render_template(_display_module_map[request_mod] + '.html', - title=request_mod, main_page=_BASE_URL, - modules=_modulename_to_display) else: - # Did we encounter something which is not registered by us? - return abort(404) + # modules requested!. + if page in _display_module_map: + return render_template(_display_module_map[page] + '.html', + title=page, main_page=_BASE_URL, + modules=_modulename_to_display) + else: + # Did we encounter something which is not registered by us? + return abort(404) From c760b7e6b6d160b9823670b496990bdd19ba24e6 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Thu, 1 May 2014 12:26:07 +0530 Subject: [PATCH 51/96] Separate invalid request and parse error logic Previously parse error was hidden by invalid request the logic is now fixed to handle both case and test files are updated. Signed-off-by: Vasudev Kamath --- silpa/api/jsonrpc.py | 21 ++++++++------------- tests/api/jsonrpc_tests.py | 13 ++++++++----- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index 09c93c3..7fbe983 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -63,6 +63,14 @@ def __init__(self, data): self.error_response = None try: self.request = JsonRpcRequest(**json.loads(data)) + except TypeError as e: + error = JsonRpcError(code=INVALID_REQUEST, + message="Not a valid JSON-RPC request", + data='') + error_dict = dict(zip(error._fields, error)) + self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", + error=error_dict, + id='') except Exception as e: # Unable to parse json error = JsonRpcError(code=PARSE_ERRORS, message=e.message, @@ -72,19 +80,6 @@ def __init__(self, data): error._fields, error)), id='') - else: - # successfully parsed now verify request - if self.request.jsonrpc != "2.0" or len(self.request.method) == 0 \ - or len(self.request.params) == 0 or \ - len(str(self.request.id)) == 0: - # not valid request - error = JsonRpcError(code=INVALID_REQUEST, - message="Not a valid JSON-RPC request", - data='') - error_dict = dict(zip(error._fields, error)) - self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", - error=error_dict, - id='') def __call__(self): # process request here diff --git a/tests/api/jsonrpc_tests.py b/tests/api/jsonrpc_tests.py index b942933..797707e 100644 --- a/tests/api/jsonrpc_tests.py +++ b/tests/api/jsonrpc_tests.py @@ -43,14 +43,17 @@ def test_methodnot_found(self): id=random.randint(1, 1000)) self.assertJsonRpcMethodNotFound(self.jpost('/api/JSONRPC', data=data)) - def test_request_parseerror(self): - # notice missing jsonrpc key? this is invalid request but our - # code will trigger parse error as we can't convert this to - # JsonRpcRequest object + def test_invalidrequest(self): data = dict(method='transliteration.transliterate', params=['Hello World!', 'kn_IN'], id=random.randint(1, 1000)) - self.assertJsonRpcParseErrors(self.jpost('/api/JSONRPC', data=data)) + self.assertJsonRpcInvalidRequest(self.jpost('/api/JSONRPC', data=data)) + + def test_request_parseerror(self): + self.assertJsonRpcParseErrors(self.post('/api/JSONRPC', data=''' + {"jsonrpc": "2.0", "method": "transliteration.transliterate", "params": + [ + ''')) def test_result_jsonrpc(self): data = dict(jsonrpc='2.0', From 103c53ac60565b9b96a3e3b6d9ed8dac4db1024a Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Thu, 1 May 2014 12:38:22 +0530 Subject: [PATCH 52/96] Add tests for page not found error Signed-off-by: Vasudev Kamath --- tests/api/jsonrpc_tests.py | 4 ++++ tests/frontend/mainpage_tests.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/tests/api/jsonrpc_tests.py b/tests/api/jsonrpc_tests.py index 797707e..4884481 100644 --- a/tests/api/jsonrpc_tests.py +++ b/tests/api/jsonrpc_tests.py @@ -61,3 +61,7 @@ def test_result_jsonrpc(self): params=['Hello World!', 'kn_IN'], id=random.randint(1, 1000)) self.assertJsonRpcResult(self.jpost('/api/JSONRPC', data=data)) + + def test_notfound(self): + r = self.post('/api/JS') + self.assertStatusCode(r, 404) diff --git a/tests/frontend/mainpage_tests.py b/tests/frontend/mainpage_tests.py index 30032bb..a171a9b 100644 --- a/tests/frontend/mainpage_tests.py +++ b/tests/frontend/mainpage_tests.py @@ -47,3 +47,7 @@ def test_moduleloaded(self): # r1 = self.get('/' + m) # self.assertIn(' {} - Indic Language Computing Platform ' + # '', self.assertOk(r1).data) + + def test_pagenotfound(self): + r = self.get('/blablabla') + self.assertStatusCode(r, 404) From b5728e9e212520d67fe09cbd91068e18ba22579e Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Thu, 29 May 2014 22:38:05 +0530 Subject: [PATCH 53/96] Move configuration out of package Configuration should not be part of package, placed it under etc in root of application Signed-off-by: Vasudev Kamath --- {silpa => etc}/silpa.conf | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {silpa => etc}/silpa.conf (100%) diff --git a/silpa/silpa.conf b/etc/silpa.conf similarity index 100% rename from silpa/silpa.conf rename to etc/silpa.conf From 114498ff4b930347731d12f865c8e48521a912ac Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Thu, 29 May 2014 22:39:27 +0530 Subject: [PATCH 54/96] Fix typo in clean target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 25c5dcf..927c3b3 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ travis: clean: find . -name "*.pyc" -exec rm -vf {} \; - find -name __pycache__ -deete + find -name __pycache__ -delete tox: tox From a39bbbf8674a6210b71da12ae262922feea78ddb Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 09:17:43 +0530 Subject: [PATCH 55/96] Rename moduleloader to helper module * Refactor to contain class ModuleConfigHelper, which has class level objects which generates module names and required information from config one time which can be accessed for life time of application using relevant class methods * load_modules is now a class method of ModuleConfigHelper class Signed-off-by: Vasudev Kamath --- silpa/helper.py | 43 +++++++++++++++++++++++++++++++++++++++++++ silpa/moduleloader.py | 18 ------------------ 2 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 silpa/helper.py delete mode 100644 silpa/moduleloader.py diff --git a/silpa/helper.py b/silpa/helper.py new file mode 100644 index 0000000..921e5e6 --- /dev/null +++ b/silpa/helper.py @@ -0,0 +1,43 @@ +from __future__ import print_function + +import importlib +import sys + + +class ModuleConfigHelper(object): + module_names = [] + module_display = {} + base_url = None + + def __new__(cls, *args, **kwargs): + config = kwargs['config'] + cls.module_names = [module for module, need in config.items('modules') + if need == 'yes'] + + cls.module_display = {module:display_name for module, display_name in + config.items('module_display') + if module in cls.module_names} + cls.base_url = config.get('main', 'baseurl') + return super(ModuleConfigHelper, cls).__new__(cls) + + @classmethod + def get_modules(cls): + return cls.module_names + + @classmethod + def get_module_displaynames(cls): + return cls.module_display + + @classmethod + def get_baseurl(cls): + return cls.base_url + + @classmethod + def load_modules(cls): + for module in cls.module_names: + try: + importlib.import_module(module) + except ImportError as e: + print("Failed to import {module}: {message}". + format(module=module, message=e.message), + file=sys.stderr) diff --git a/silpa/moduleloader.py b/silpa/moduleloader.py deleted file mode 100644 index f995ba3..0000000 --- a/silpa/moduleloader.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import print_function -from .loadconfig import config - -import importlib -import sys - -_modules = [module for module, need in config.items("modules") - if need == "yes"] - - -def load_modules(): - for module in _modules: - try: - importlib.import_module(module) - except ImportError as e: - print("Failed to import {module}: {message}". - format(module=module, message=e.message), - file=sys.stderr) From 9e60242ebde54cf99c07ef9fe452f245d6428da5 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 09:21:14 +0530 Subject: [PATCH 56/96] Remove hard coded config initialization from class Till now there was a instance config created once by using local config file which didn't allow testing with various configuration, remove it now and made Config class public which should be initialzied by factory with alternate configuration files if required. --- silpa/loadconfig.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/silpa/loadconfig.py b/silpa/loadconfig.py index a114977..a5ea48a 100644 --- a/silpa/loadconfig.py +++ b/silpa/loadconfig.py @@ -1,4 +1,4 @@ -__all__ = ['IncompleteConfigError', 'config'] +__all__ = ['IncompleteConfigError', 'Config'] import configparser import os @@ -19,7 +19,7 @@ def __str__(self): section=self.section) -class _Config(configparser.ConfigParser): +class Config(configparser.ConfigParser): def __init__(self, location="silpa.conf"): configparser.ConfigParser.__init__(self) @@ -42,5 +42,3 @@ def verify(self): def _verify_item(self, section, option): if not self.has_option(section, option): raise IncompleteConfigError(section, option) - -config = _Config(os.path.join(os.path.dirname(__file__), "silpa.conf")) From 2153896af803f27809819e1ec60003344fa7bcf6 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 09:30:19 +0530 Subject: [PATCH 57/96] Refactor to use new configuration and helper class * Configuration file should be passed to factory via create_app * Multiple module and module_display maps are created once and used via helper module. * Modify template to take module and display name tuples as argument and use module name as url end point. Fixes #23. Signed-off-by: Vasudev Kamath --- silpa/api/__init__.py | 7 ++----- silpa/factory.py | 21 ++++++++++++++----- silpa/frontend/__init__.py | 9 ++++---- silpa/frontend/pages.py | 32 +++++++++++++---------------- silpa/frontend/templates/silpa.html | 4 ++-- 5 files changed, 38 insertions(+), 35 deletions(-) diff --git a/silpa/api/__init__.py b/silpa/api/__init__.py index 50facbb..7f3c109 100644 --- a/silpa/api/__init__.py +++ b/silpa/api/__init__.py @@ -1,7 +1,6 @@ from flask import jsonify from functools import wraps from .. import factory -from .. import moduleloader def route(bp, *args, **kwargs): @@ -16,13 +15,11 @@ def wrapper(*args, **kwargs): return decorator -def create_app(settings_override=None): +def create_app(conffile, settings_override=None): app = factory.create_app(__name__, __path__, - settings_override) + settings_override, conffile) app.errorhandler(404)(on_404) - # Load enabled modules from the config - moduleloader.load_modules() return app diff --git a/silpa/factory.py b/silpa/factory.py index 80fe7c8..bc54f72 100644 --- a/silpa/factory.py +++ b/silpa/factory.py @@ -3,11 +3,12 @@ import logging import os + from flask import Flask, Blueprint -from .loadconfig import config +from .loadconfig import Config from logging import Formatter from logging.handlers import TimedRotatingFileHandler - +from .helper import ModuleConfigHelper def register_blueprints(app, package_name, package_path): rv = [] @@ -22,7 +23,7 @@ def register_blueprints(app, package_name, package_path): return rv -def configure_logging(app): +def configure_logging(app, config): log_level = config.get('logging', 'log_level') log_folder = config.get('logging', 'log_folder') log_name = config.get('logging', 'log_name') @@ -49,13 +50,23 @@ def configure_logging(app): app.logger.addHandler(handler) -def create_app(package_name, package_path, settings_override=None): +def create_app(package_name, package_path, settings_override=None, + conffile="silpa.conf"): app = Flask(package_name, instance_relative_config=True) app.config.from_object("silpa.settings") app.config.from_pyfile('settings.cfg', silent=True) app.config.from_object(settings_override) + config = Config(conffile) + configure_logging(app, config) + + # Create ModuleConfigHelper class and pass it config this will + # instantiate class variables + ModuleConfigHelper(config=config) + ModuleConfigHelper.load_modules() + + # Register blueprints at end so we have module,display and other + # stuff created register_blueprints(app, package_name, package_path) - configure_logging(app) return app diff --git a/silpa/frontend/__init__.py b/silpa/frontend/__init__.py index fdf9fa3..090257b 100644 --- a/silpa/frontend/__init__.py +++ b/silpa/frontend/__init__.py @@ -1,18 +1,17 @@ from .. import factory from jinja2 import PackageLoader, ChoiceLoader -from ..loadconfig import config +from ..helper import ModuleConfigHelper -def create_app(settings_override=None): +def create_app(conffile, settings_override=None): app = factory.create_app(__name__, __path__, - settings_override) + settings_override, conffile) load_module_templates(app) return app def load_module_templates(app): - modules = [module for module, need in config.items('modules') - if need == 'yes'] + modules = ModuleConfigHelper.get_modules() templates = [app.jinja_loader] for module in modules: templates.append(PackageLoader(module)) diff --git a/silpa/frontend/pages.py b/silpa/frontend/pages.py index cfaf3a1..bb128bb 100644 --- a/silpa/frontend/pages.py +++ b/silpa/frontend/pages.py @@ -1,16 +1,12 @@ from flask import Blueprint, render_template, abort -from ..loadconfig import config +from ..helper import ModuleConfigHelper -_BASE_URL = config.get('main', 'baseurl') -_modules = [module for module, need in config.items('modules') - if need == 'yes'] -_modulename_to_display = sorted((display_name - for module, display_name in - config.items('module_display') - if module in _modules)) -_display_module_map = {display_name: module for module, display_name in - config.items('module_display') - if module in _modules} +_BASE_URL = ModuleConfigHelper.get_baseurl() +_modules = ModuleConfigHelper.get_modules() +_modulename_to_display = ModuleConfigHelper.get_module_displaynames() + +_display_module_map = sorted(zip(_modulename_to_display.keys(), + _modulename_to_display.values())) bp = Blueprint('frontend', __name__) @@ -21,25 +17,25 @@ def serve_pages(page): if page == "index.html": return render_template('index.html', title='SILPA', main_page=_BASE_URL, - modules=_modulename_to_display) + modules=_display_module_map) elif page == "License": return render_template('license.html', title='SILPA License', main_page=_BASE_URL, - modules=_modulename_to_display) + modules=_display_module_map) elif page == "Credits": return render_template('credits.html', title='Credits', main_page=_BASE_URL, - modules=_modulename_to_display) + modules=_display_module_map) elif page == "Contact": return render_template('contact.html', title='Contact SILPA Team', main_page=_BASE_URL, - modules=_modulename_to_display) + modules=_display_module_map) else: # modules requested!. - if page in _display_module_map: - return render_template(_display_module_map[page] + '.html', + if page in _modules: + return render_template(page + '.html', title=page, main_page=_BASE_URL, - modules=_modulename_to_display) + modules=_display_module_map) else: # Did we encounter something which is not registered by us? return abort(404) diff --git a/silpa/frontend/templates/silpa.html b/silpa/frontend/templates/silpa.html index 2cf3a41..ef9cfdf 100644 --- a/silpa/frontend/templates/silpa.html +++ b/silpa/frontend/templates/silpa.html @@ -33,8 +33,8 @@
From be4a9cc6a94a1021849bc83f3e0d4ca7b87322d5 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 09:34:50 +0530 Subject: [PATCH 58/96] Pass configuration file path to create_app Pass config file path to newly modified function and add debug run method (run_simple) --- wsgi.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/wsgi.py b/wsgi.py index 5e47d59..0648a0e 100644 --- a/wsgi.py +++ b/wsgi.py @@ -2,6 +2,13 @@ from werkzeug.wsgi import DispatcherMiddleware from silpa import api, frontend +import os -application = DispatcherMiddleware(frontend.create_app(), - {'/api': api.create_app()}) +conffile = os.path.join(os.path.dirname(__file__), "etc", "silpa.conf") + +application = DispatcherMiddleware(frontend.create_app(conffile), + {'/api': api.create_app(conffile)}) + +if __name__ == "__main__": + run_simple('0.0.0.0', 5000, application, + use_reloader=True, use_debugger=True) From 258f2f099b7a5b38f2c87b03cbce17756446a561 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 18:56:25 +0530 Subject: [PATCH 59/96] Implement module not loaded, interface not found error * Implement the error to be returned when module is not loaded either because its disabled by admin or not found in system. * Implement error return value when getInstance interface is not implemented by requested module. Signed-off-by: Vasudev Kamath --- silpa/api/jsonrpc.py | 24 +++++++++++++++++++++--- tests/api/jsonrpc_tests.py | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index 7fbe983..60dabd8 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -85,8 +85,17 @@ def __call__(self): # process request here module, method = self.request.method.split('.') if module not in sys.modules: - # Module is not yet loaded? handle this - pass + # Module is not yet loaded or the request module is not + # enabled pass an error here. + error = JsonRpcError(code=INTERNAL_ERROR, + message="Requested module is not loaded or not\ + enabled by Admin", + data="{} is not loaded".format(module)) + self.error_response = JsonRpcErrorResponse(jsonrpc="2.0", + error=dict(zip( + error._fields, + error)), + id=self.request.id) else: # module is present in sys mod = sys.modules[module] @@ -110,4 +119,13 @@ def __call__(self): id=self.request.id) else: # module doesn't provide an interface to us? - pass + error = JsonRpcError(code=INTERNAL_ERROR, + message="Requested module doesn't provide \ + getInstance interface", + data="{} is the module requested".format( + module)) + self.error_response = JsonRpcErrorResponse(jsonrpc='2.0', + error=dict(zip( + error._fields, + error)), + id=self.request.id) diff --git a/tests/api/jsonrpc_tests.py b/tests/api/jsonrpc_tests.py index 4884481..74093dc 100644 --- a/tests/api/jsonrpc_tests.py +++ b/tests/api/jsonrpc_tests.py @@ -32,6 +32,12 @@ def assertJsonRpcParseErrors(self, response): error_obj = jsonrpc.JsonRpcError(**response_dict['error']) self.assertEquals(error_obj.code, jsonrpc.PARSE_ERRORS) + def assertJsonRpcInternalError(self, response): + response_dict = json.loads(self.assertBadJson(response).data) + self.assertIn('error', response_dict) + error_obj = jsonrpc.JsonRpcError(**response_dict['error']) + self.assertEquals(error_obj.code, jsonrpc.INTERNAL_ERROR) + def assertJsonRpcResult(self, response): response_dict = json.loads(self.assertOkJson(response).data) self.assertIn('result', response_dict) @@ -65,3 +71,19 @@ def test_result_jsonrpc(self): def test_notfound(self): r = self.post('/api/JS') self.assertStatusCode(r, 404) + + def test_module_notloaded(self): + # Test assumes scriptrender module will never be enabled in + # test setup configuration + data = dict(jsonrpc='2.0', + method='scriptrender.render_text', + params=['some text', 'png', 'Black'], + id=random.randint(1, 1000)) + self.assertJsonRpcInternalError(self.jpost('/api/JSONRPC', data=data)) + + def test_no_interface(self): + data = dict(jsonrpc='2.0', + method='flask.Flask', + params=[__name__], + id=random.randint(1,1000)) + self.assertJsonRpcInternalError(self.jpost('/api/JSONRPC', data=data)) From c1d95e24cef70c61344ea92684c16c02ba576c9c Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 18:58:58 +0530 Subject: [PATCH 60/96] Use custom configuration file for testing Signed-off-by: Vasudev Kamath --- tests/api/__init__.py | 5 +++- tests/frontend/__init__.py | 5 +++- tests/resources/silpa.conf | 60 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/resources/silpa.conf diff --git a/tests/api/__init__.py b/tests/api/__init__.py index fb2e1d2..894aad2 100644 --- a/tests/api/__init__.py +++ b/tests/api/__init__.py @@ -1,11 +1,14 @@ from silpa.api import create_app from .. import SILPAAppTestCase, settings +import os class SILPAApiTestCase(SILPAAppTestCase): def _create_app(self): - return create_app(settings) + self.conffile = os.path.join(os.path.dirname(__file__), '../resources', + 'silpa.conf') + return create_app(self.conffile, settings) def setUp(self): super(SILPAApiTestCase, self).setUp() diff --git a/tests/frontend/__init__.py b/tests/frontend/__init__.py index 8ad6f1c..6e8399f 100644 --- a/tests/frontend/__init__.py +++ b/tests/frontend/__init__.py @@ -8,12 +8,15 @@ from silpa.frontend import create_app from .. import SILPAAppTestCase, settings +import os class SILPAFrontEndTestCase(SILPAAppTestCase): def _create_app(self): - return create_app(settings) + self.conffile = os.path.join(os.path.dirname(__file__), '../resources', + 'silpa.conf') + return create_app(self.conffile, settings) def setUp(self): super(SILPAFrontEndTestCase, self).setUp() diff --git a/tests/resources/silpa.conf b/tests/resources/silpa.conf new file mode 100644 index 0000000..daff214 --- /dev/null +++ b/tests/resources/silpa.conf @@ -0,0 +1,60 @@ +[main] +# SITE Name +site = SILPA + +# If your site is not hosted in document root please give baseurl +# eg: www.example.org/silpa +# Leave the / if SILPA is hosted directly under documentroot +# leave it as / if you are using WSGI even if you are not under document root +baseurl = / + +[logging] +# Set a logging level +# Allowed values are info,debug,warn,error +log_level = debug + +# Which folder log should be located +log_folder = /tmp + +# log file name +log_name = silpa.log + +[modules] +# These section provides list of modules use +# 'yes' to enable them and 'no' to disable +spellchecker = no +inexactsearch = yes +soundex = yes +transliteration = yes +hyphenation = no +chardetails = no +payyans = no +indicsyllabifier = no +indicfortune = no +ucasort = no +indicngram = no +shingling = no +textsimilarity = no +indicstemmer = no +katapayadi = no +scriptrender = no + +[module_display] +# Section gives names to be displayed on HTML and +# additionally the end point for each module +soundex = Soundex +inexactsearch = ApproxSearch +spellchecker = SpellCheck +transliteration = Transliteration +hyphenation = Hyphenate +chardetails = Chardetails +payyans = Payyans +indicsyllabifier = Syllabalizer +indicfortune = Fortune +ucasort = UCA Sort +indicngram = N-gram +shingling = Shingling +textsimilarity = Similar Texts +indicstemmer = Stemmer +katapayadi = Katapayadi Numbers +scriptrender = Script Render From 6e0593aff6cd5e96934b5052bb4ca83ff61d68ef Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 19:03:47 +0530 Subject: [PATCH 61/96] Rename blueprint name for JSONRPC New name for JSONRPC handling blueprint is api_jsonrpc, this should be used in all module templates with url_for call. Signed-off-by: Vasudev Kamath --- silpa/api/jsonrpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silpa/api/jsonrpc.py b/silpa/api/jsonrpc.py index 60dabd8..b6648de 100644 --- a/silpa/api/jsonrpc.py +++ b/silpa/api/jsonrpc.py @@ -20,7 +20,7 @@ INTERNAL_ERROR = -32603 -bp = Blueprint('JSONRPC', __name__, url_prefix='/api') +bp = Blueprint('api_jsonrpc', __name__, url_prefix='/api') @route(bp, '/JSONRPC', methods=['POST']) From 23979b12eae83d8d55ccaa7685afadb186ada8f1 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 19:36:14 +0530 Subject: [PATCH 62/96] fix to use new interfaces --- tests/frontend/mainpage_tests.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/tests/frontend/mainpage_tests.py b/tests/frontend/mainpage_tests.py index a171a9b..7235f82 100644 --- a/tests/frontend/mainpage_tests.py +++ b/tests/frontend/mainpage_tests.py @@ -27,26 +27,24 @@ def test_pages(self): self.assertIn('

Credits

', self.assertOk(r).data) def test_moduleloaded(self): - from silpa.loadconfig import config - modules = [module for module, need in config.items('modules') - if need == "yes"] - module_display = sorted((display_name - for module, display_name in - config.items('module_display') - if module in modules)) + from silpa.helper import ModuleConfigHelper + module_display = ModuleConfigHelper.get_module_displaynames() + r = self.get('/') self.assertOk(r) - for m in module_display: + for m, d in module_display.items(): # FIXME: Returned by configparser is unicode string for # some reason breaks with assertIn not sure why :( - self.assertIn(m.encode('utf-8'), r.data) + self.assertIn(d.encode('utf-8'), r.data) # TODO: URL in modules needs to be fixed befor enabling - # below tests - # r1 = self.get('/' + m) - # self.assertIn(' {} - Indic Language Computing Platform ' + - # '', self.assertOk(r1).data) + # below tests, for now skip transliteration which is + # enabled in test conf but url is not fixed + # if m != 'transliteration' and m != 'soundex': + # r1 = self.get('/' + m) + # self.assertIn(' {} - Indic Language Computing Platform' + + # ' '.format(m), self.assertOk(r1).data) def test_pagenotfound(self): r = self.get('/blablabla') From 86cee27225081eb73000bd8836f5728cd3b84942 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 19:36:26 +0530 Subject: [PATCH 63/96] pep8 cleaning --- silpa/factory.py | 1 + silpa/helper.py | 6 +++--- silpa/loadconfig.py | 1 - tests/api/jsonrpc_tests.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/silpa/factory.py b/silpa/factory.py index bc54f72..a3d64f9 100644 --- a/silpa/factory.py +++ b/silpa/factory.py @@ -10,6 +10,7 @@ from logging.handlers import TimedRotatingFileHandler from .helper import ModuleConfigHelper + def register_blueprints(app, package_name, package_path): rv = [] for _, name, _ in pkgutil.iter_modules(package_path): diff --git a/silpa/helper.py b/silpa/helper.py index 921e5e6..8a6239c 100644 --- a/silpa/helper.py +++ b/silpa/helper.py @@ -14,9 +14,9 @@ def __new__(cls, *args, **kwargs): cls.module_names = [module for module, need in config.items('modules') if need == 'yes'] - cls.module_display = {module:display_name for module, display_name in - config.items('module_display') - if module in cls.module_names} + cls.module_display = {module: display_name for module, display_name in + config.items('module_display') + if module in cls.module_names} cls.base_url = config.get('main', 'baseurl') return super(ModuleConfigHelper, cls).__new__(cls) diff --git a/silpa/loadconfig.py b/silpa/loadconfig.py index a5ea48a..5446f0c 100644 --- a/silpa/loadconfig.py +++ b/silpa/loadconfig.py @@ -1,7 +1,6 @@ __all__ = ['IncompleteConfigError', 'Config'] import configparser -import os class IncompleteConfigError(Exception): diff --git a/tests/api/jsonrpc_tests.py b/tests/api/jsonrpc_tests.py index 74093dc..260fde5 100644 --- a/tests/api/jsonrpc_tests.py +++ b/tests/api/jsonrpc_tests.py @@ -85,5 +85,5 @@ def test_no_interface(self): data = dict(jsonrpc='2.0', method='flask.Flask', params=[__name__], - id=random.randint(1,1000)) + id=random.randint(1, 1000)) self.assertJsonRpcInternalError(self.jpost('/api/JSONRPC', data=data)) From c048085317839b6e7d5ad8a8c4a2a4eef1076086 Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 19:50:01 +0530 Subject: [PATCH 64/96] Enable module page visiting tests --- tests/frontend/mainpage_tests.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/frontend/mainpage_tests.py b/tests/frontend/mainpage_tests.py index 7235f82..8499d4d 100644 --- a/tests/frontend/mainpage_tests.py +++ b/tests/frontend/mainpage_tests.py @@ -41,10 +41,11 @@ def test_moduleloaded(self): # TODO: URL in modules needs to be fixed befor enabling # below tests, for now skip transliteration which is # enabled in test conf but url is not fixed - # if m != 'transliteration' and m != 'soundex': - # r1 = self.get('/' + m) - # self.assertIn(' {} - Indic Language Computing Platform' + - # ' '.format(m), self.assertOk(r1).data) + if m != 'transliteration': + r1 = self.get('/' + m) + self.assertIn(' ' + m.encode('utf-8') + + ' - Indic Language Computing Platform' + + ' ', self.assertOk(r1).data) def test_pagenotfound(self): r = self.get('/blablabla') From 05059ddd32a165a83d8cd3e66016a4953f91af7c Mon Sep 17 00:00:00 2001 From: Vasudev Kamath Date: Sun, 1 Jun 2014 20:47:42 +0530 Subject: [PATCH 65/96] Use webassets to minimize load time Signed-off-by: Vasudev Kamath --- silpa/frontend/__init__.py | 2 ++ silpa/frontend/assets.py | 26 ++++++++++++++++++++++++++ silpa/frontend/templates/silpa.html | 13 ++++++------- 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 silpa/frontend/assets.py diff --git a/silpa/frontend/__init__.py b/silpa/frontend/__init__.py index 090257b..79a73a0 100644 --- a/silpa/frontend/__init__.py +++ b/silpa/frontend/__init__.py @@ -1,11 +1,13 @@ from .. import factory from jinja2 import PackageLoader, ChoiceLoader from ..helper import ModuleConfigHelper +from . import assets def create_app(conffile, settings_override=None): app = factory.create_app(__name__, __path__, settings_override, conffile) + assets.init_app(app) load_module_templates(app) return app diff --git a/silpa/frontend/assets.py b/silpa/frontend/assets.py new file mode 100644 index 0000000..bd13f7b --- /dev/null +++ b/silpa/frontend/assets.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +""" + silpa.frontend.assets + ~~~~~~~~~~~~~~~~~~~~~ + + frontend application asset "pipeline" +""" + +from flask.ext.assets import Environment, Bundle + + +css_all = Bundle("css/bootstrap.min.css", "css/jquery.ime.css", + "css/main.css", filters="cssmin", output="css/silpa.min.css") + + +js_all = Bundle("js/bootstrap.min.js", "js/jquery.ime.js", "js/jquery.js", + filters="jsmin", output="js/silpa.min.js") + + +def init_app(app): + webassets = Environment(app) + webassets.register('css_all', css_all) + webassets.register('js_all', js_all) + webassets.manifest = 'cache' if not app.debug else False + webassets.cache = not app.debug + webassets.debug = app.debug diff --git a/silpa/frontend/templates/silpa.html b/silpa/frontend/templates/silpa.html index ef9cfdf..b77da69 100644 --- a/silpa/frontend/templates/silpa.html +++ b/silpa/frontend/templates/silpa.html @@ -6,10 +6,9 @@ - - - - + {% assets "css_all" %} + + {% endassets %}