diff --git a/cms/__init__.py b/cms/__init__.py
index 2a4b745..98795ba 100644
--- a/cms/__init__.py
+++ b/cms/__init__.py
@@ -6,7 +6,7 @@ import copy
from jinja2 import Environment, BaseLoader, FileSystemLoader
import importlib
import importlib.util
-from cms import disk, cloud
+from cms import disk, cloud, engine
# import cloud
class components :
diff --git a/cms/engine/__init__.py b/cms/engine/__init__.py
new file mode 100644
index 0000000..18c283b
--- /dev/null
+++ b/cms/engine/__init__.py
@@ -0,0 +1,309 @@
+import json
+
+from genericpath import isdir
+import os
+import pandas as pd
+import transport
+import copy
+from jinja2 import Environment, BaseLoader, FileSystemLoader
+import importlib
+import importlib.util
+from cms import disk, cloud
+
+
+class Loader :
+ def __init__(self,**_args):
+ f = open (_args['path'])
+ self._config = json.loads(f.read())
+ #
+ #
+ self._location = None
+ self._caller = None
+ if 'caller' in _args and _args['caller'] :
+ self._caller = _args['caller']
+ self._location = _args['location'].split(os.sep) # needed for plugin loading
+ self._location = os.sep.join(self._location[:-1])
+ self._config['system']['portal'] = self._caller != None
+ self._menu = {}
+ self._plugins={}
+ self.load()
+
+ def load(self):
+ """
+ This function will load menu (overwrite) and plugins
+ """
+ self.init_menu()
+ self.init_plugins()
+
+ def init_menu(self):
+ """
+ This function will read menu and sub-menu items from disk structure,
+ The files are loaded will
+ """
+
+ _config = self._config
+ if 'source' in _config['system'] and _config['system']['source']['id'] == 'cloud' :
+ _sourceHandler = cloud
+ else:
+ _sourceHandler = disk
+ _object = _sourceHandler.build(_config)
+ #
+ # After building the site's menu, let us add the one from 3rd party apps
+ #
+
+
+ _layout = copy.deepcopy(_config['layout'])
+ _overwrite = _layout['overwrite'] if 'overwrite' in _layout else {}
+
+ #
+ # @TODO: Find a way to translate rename/replace keys of the _object (menu) here
+ #
+ #-- applying overwrites to the menu items
+ for _name in _object :
+ _submenu = _object[_name]
+ _index = 0
+ for _item in _submenu :
+ text = _item['text'].strip()
+ if text in _overwrite :
+ if 'uri' in _item and 'url' in 'url' in _overwrite[text] :
+ del _item['uri']
+ _item = dict(_item,**_overwrite[text])
+
+ if 'uri' in _item and _item['type'] != 'open':
+ _item['uri'] = _item['uri'].replace(_layout['root'],'')
+
+ _submenu[_index] = _item
+ _index += 1
+ self.init_apps(_object)
+ self._menu = _object
+ self._order()
+
+ def init_apps (self,_menu):
+ """
+ Insuring that the apps are loaded into the menu with an approriate label
+ """
+ _system = self._config['system']
+ _context = _system['context']
+ if 'routes' in _system :
+ # _items = []
+ _overwrite = {} if 'overwrite' not in self._config['layout'] else self._config['layout']['overwrite']
+ for _text in _system['routes'] :
+ _item = _system['routes'][_text]
+ if 'menu' not in _item :
+ continue
+ uri = f'{_context}/set/{_text}'
+ # _items.append ({"text":_text,'uri':uri,'type':'open'})
+ _label = _item['menu']
+ if _label not in _menu :
+ _menu [_label] = []
+ _menu[_label].append ({"text":_text,'uri':uri,'type':'open'})
+ # _overwrite[_text] = {'text': _text.replace('-',' ').replace('_',' '),'uri':uri,'type':'open'}
+ # _menu['products'] = _items
+ #
+ # given that the menu items assumes redirecting to a page ...
+ # This is not the case
+ #
+ # self._config['overwrite'] = _overwrite
+ else:
+ pass
+
+ pass
+ def _order (self):
+ _config = self._config
+ if 'order' in _config['layout'] and 'menu' in _config['layout']['order']:
+ _sortedmenu = {}
+ _menu = self._menu
+ for _name in _config['layout']['order']['menu'] :
+ if _name in _menu :
+ _sortedmenu[_name] = _menu[_name]
+
+ _menu = _sortedmenu if _sortedmenu else _menu
+ #
+ # If there are missing items in the sorting
+ _missing = list(set(self._menu.keys()) - set(_sortedmenu))
+ if _missing :
+ for _name in _missing :
+ _menu[_name] = self._menu[_name]
+ _config['layout']['menu'] = _menu #cms.components.menu(_config)
+ self._menu = _menu
+ self._config = _config
+ def init_plugins(self) :
+ """
+ This function looks for plugins in the folder on disk (no cloud support) and attempts to load them
+ """
+ _config = self._config
+ PATH= os.sep.join([_config['layout']['root'],'_plugins'])
+ if not os.path.exists(PATH) and self._location and os.path.exists(self._location) :
+ #
+ # overriding the location of plugins ...
+ PATH = self._location
+
+ _map = {}
+ # if not os.path.exists(PATH) :
+ # return _map
+ if 'plugins' not in _config :
+ _config['plugins'] = {}
+ _conf = _config['plugins']
+
+ for _key in _conf :
+
+ _path = os.sep.join([PATH,_key+".py"])
+ if not os.path.exists(_path):
+ continue
+ for _name in _conf[_key] :
+ _pointer = self._load_plugin(path=_path,name=_name)
+ if _pointer :
+ _uri = "/".join(["api",_key,_name])
+ _map[_uri] = _pointer
+ #
+ # We are adding some source specific plugins to the user-defined plugins
+ # This is intended to have out-of the box plugins...
+ #
+ if 'source' in _config['system'] and _config['system']['source']['id'] == 'cloud' :
+ _plugins = cloud.plugins()
+ else:
+ _plugins = disk.plugins()
+ #
+ # If there are any plugins found, we should load them and use them
+
+ if _plugins :
+ _map = dict(_map,**_plugins)
+ else:
+ pass
+ self._plugins = _map
+ self._config['plugins'] = self._plugins
+
+ def _load_plugin(self,**_args):
+ """
+ This function will load external module form a given location and return a pointer to a function in a given module
+ :path absolute path of the file (considered plugin) to be loaded
+ :name name of the function to be applied
+ """
+ _path = _args['path'] #os.sep.join([_args['root'],'plugin'])
+ if os.path.isdir(_path):
+ files = os.listdir(_path)
+ if files :
+ files = [name for name in files if name.endswith('.py')]
+ if files:
+ _path = os.sep.join([_path,files[0]])
+ else:
+ return None
+ else:
+ return None
+ #-- We have a file ...
+ _name = _args['name']
+
+ spec = importlib.util.spec_from_file_location(_name, _path)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+
+ return getattr(module,_name) if hasattr(module,_name) else None
+
+class Getter (Loader):
+ def __init__(self,**_args):
+ super().__init__(**_args)
+ _system = self.system()
+ _logo = _system['logo']
+ if 'source' in _system and 'id' in _system['source'] and (_system['source']['id'] == 'cloud'):
+ _icon = f'/api/cloud/download?doc=/{_logo}'
+ _system['icon'] = _icon
+
+ else:
+ _root = self._config['layout']['root']
+ _icon = os.sep.join([_root,_icon])
+ _system['icon'] = _icon
+ self._config['system'] = _system
+ def html(self,uri,id,_args={},_system={}) :
+ """
+ This function reads a given uri and returns the appropriate html document, and applies environment context
+
+ """
+ _system = self._config['system']
+ if 'source' in _system and _system['source']['id'] == 'cloud':
+ _html = cloud.html(uri,dict(_args,**{'system':_system}))
+
+ else:
+ _html = disk.html(uri)
+ # _html = (open(uri)).read()
+
+
+ #return ' '.join(['
'.replace(':id',id),_html,'
'])
+ _html = ' '.join(['
'.replace(':id',id),_html,'
'])
+ appContext = Environment(loader=BaseLoader()).from_string(_html)
+ #
+ # If the rendering of the HTML happens here we should plugin custom functions (at the very least)
+ #
+
+ return appContext.render(**_args)
+ # return _html
+
+ def data (self,_args):
+ """
+ :store data-store parameters (data-transport, github.com/lnyemba/data-transport)
+ :query query to be applied against the store (expected data-frame)
+ """
+ _store = _args['store']
+ reader = transport.factory.instance(**_store)
+ _queries= copy.deepcopy(_store['query'])
+ _data = reader.read(**_queries)
+ return _data
+ def csv(self,uri) :
+ return pd.read(uri).to_html()
+
+ return _map
+ def menu(self):
+ return self._config['menu']
+ def plugins(self):
+ return copy.deepcopy(self._plugins) if 'plugins' in self._config else {}
+ def context(self):
+ """
+ adding custom variables functions to Jinja2, this function should be called after plugins are loaded
+ """
+ _plugins = self.plugins()
+ # if not location:
+ # env = Environment(loader=BaseLoader())
+ # else:
+ location = self._config['layout']['root']
+ # env = Environment(loader=FileSystemLoader(location))
+ env = Environment(loader=BaseLoader())
+ # env.globals['routes'] = _config['plugins']
+ return env
+ def config(self):
+ return copy.deepcopy(self._config)
+ def system(self,skip=[]):
+ """
+ :skip keys to ignore in the object ...
+ """
+ _data = copy.deepcopy(self._config['system'])
+ _system = {}
+ if skip and _system:
+ for key in _data.keys() :
+ if key not in skip :
+ _system[key] = _data[key]
+ else:
+ _system= _data
+ return _system
+ def get_app(self):
+ return self._config['system']['app']
+
+
+class Router :
+ def __init__(self,**_args) :
+ path = _args['path']
+ _app = Getter (path = path)
+ self._id = 'main'
+ # _app.load()
+ self._apps = {}
+ _system = _app.system()
+ if 'routes' in _system :
+ _system = _system['routes']
+ for _name in _system :
+ _path = _system[_name]['path']
+ self._apps[_name] = Getter(path=_path,caller=_app,location=_path)
+ # self._apps[_name].load()
+ self._apps['main'] = _app
+ def set(self,_id):
+ self._id = _id
+ def get(self):
+
+ return self._apps['main'] if self._id not in self._apps else self._apps[self._id]
\ No newline at end of file
diff --git a/cms/plugins.py b/cms/plugins.py
index 8819807..e34862b 100644
--- a/cms/plugins.py
+++ b/cms/plugins.py
@@ -2,7 +2,7 @@
These are a few default plugins that will be exported and made available to the calling code
The purpose of plugins is to perform data processing
"""
-
+import json
diff --git a/index.py b/index.py
index 3c8cd44..73b0aa2 100644
--- a/index.py
+++ b/index.py
@@ -2,7 +2,7 @@ __doc__ = """
arguments :
--config path of the configuration otherwise it will look for the default in the working directory
"""
-from flask import Flask,render_template,send_from_directory,request
+from flask import Flask,render_template,send_from_directory,request, redirect
import flask
import transport
from transport import providers
@@ -16,35 +16,42 @@ import base64
from jinja2 import Environment, BaseLoader
+
_app = Flask(__name__)
@_app.route('/favicon.ico')
def favicon():
- global _config
-
- return send_from_directory(os.path.join(_app.root_path, 'static/img'),
- 'logo.png', mimetype='image/vnd.microsoft.icon')
+ global _route
+ _system = _route.get ().system()
+ _logo =_system['icon'] if 'icon' in _system else 'static/img/logo.svg'
+ return _logo
+ # # _root = _route.get().config()['layout']['root']
+ # # print ([_system])
+ # # if 'source' in _system and 'id' in _system['source'] and (_system['source']['id'] == 'cloud'):
+ # # uri = f'/api/cloud/downloads?doc=/{_logo}'
+ # # print (['****' , uri])
+ # # return redirect(uri,200) #,{'content-type':'application/image'}
+ # # else:
+
+ # # return send_from_directory(_root, #_app.root_path, 'static/img'),
+ # _logo, mimetype='image/vnd.microsoft.icon')
@_app.route("/")
def _index ():
global _config
+ global _route
+ _handler = _route.get()
+ _config = _handler.config()
+ _system = _handler.system()
+ _plugins= _handler.plugins()
_args = {}
- if 'plugins' in _config :
- _args['routes']=['plugins']
- _system = cms.components.get_system(_config) #copy.deepcopy(_config['system'])
+ # if 'plugins' in _config :
+ # _args['routes']=_config['plugins']
+ # _system = cms.components.get_system(_config) #copy.deepcopy(_config['system'])
try:
- _args['layout'] = _config['layout']
- # _args = dict(_args,**_config['layout'])
- # _args = copy.copy(_config)
-
uri = os.sep.join([_config['layout']['root'], _config['layout']['index']])
- _html = cms.components.html(uri,'index',_args,_system)
-
- _args['index'] = _html
- # e = Environment(loader=BaseLoader()).from_string(_html)
- # e = cms.components.context(_config).from_string(_html)
- # _args['index'] = e.render(**_args)
+ _html = _route.get().html(uri,'index',_config,_system)
_index_page = "index.html"
except Exception as e:
print ()
@@ -52,9 +59,10 @@ def _index ():
_index_page = "404.html"
_args['uri'] = request.base_url
pass
- if 'source' in _system :
- del _system['source']
- _args['system'] = _system
+ # if 'source' in _system :
+ # del _system['source']
+ _args = {'layout':_config['layout'],'index':_html}
+ _args['system'] = _handler.system(skip=['source','app','route'])
return render_template(_index_page,**_args)
@@ -68,24 +76,28 @@ def _index ():
@_app.route('/dialog')
def _dialog ():
- global _config
-
+ # global _config
+ global _route
+ _handler = _route.get()
+ _system = _handler.system()
+ _config = _handler.config()
_uri = os.sep.join([_config['layout']['root'],request.headers['uri']])
# _uri = request.headers['uri']
_id = request.headers['dom']
# _data = cms.components.data(_config)
_args = {} #{'system':_config['system']}
_args['title'] = _id
- if 'plugins' in _config :
- _args['routes'] = _config['plugins']
- _system = copy.deepcopy(_config['system'])
+ # if 'plugins' in _config :
+ # _args['routes'] = _config['plugins']
+ # _system = copy.deepcopy(_config['system'])
- _html = cms.components.html(_uri,_id,_config,_system)
+ # _html = cms.components.html(_uri,_id,_config,_system)
+ _html = _handler.html(_uri,_id,_config,_system)
e = Environment(loader=BaseLoader()).from_string(_html)
- if 'source' in _system :
- del _system['source']
- _args['system'] = _system
+ # if 'source' in _system :
+ # del _system['source']
+ _args['system'] = _handler.system(skip=['source','routes','app'])
_args['html'] = _html
_html = ''.join(["
",str( e.render(**_args)),'
'])
@@ -104,17 +116,20 @@ def _getproxy(module,name) :
:_module entry specified in plugins of the configuration
:_name name of the function to execute
"""
- global _config
+ # global _config
+ global _route
+ _handler = _route.get()
+
uri = '/'.join(['api',module,name])
_args = dict(request.args,**{})
- _args['config'] = copy.deepcopy(_config)
-
- if uri not in _config['plugins'] :
+ _args['config'] = _handler.config()
+ _plugins = _handler.plugins()
+ if uri not in _plugins :
_data = {}
_code = 404
else:
- pointer = _config['plugins'][uri]
+ pointer = _plugins[uri]
if _args :
_data = pointer (**_args)
else:
@@ -125,15 +140,18 @@ def _getproxy(module,name) :
return _data,_code
@_app.route("/api//" , methods=['POST'])
def _post (module,name):
- global _config
+ # global _config
+ global _route
+ _config = _route.get().config()
+ _plugins = _route.plugins()
uri = '/'.join(['api',module,name])
_args = request.json
code = 404
_info = ""
- if uri in _config['plugins'] and _args:
- _pointer = _config['plugins'][uri]
+ if uri in _plugins and _args:
+ _pointer = _plugins[uri]
_info = _pointer(**_args)
if _info:
code = 200
@@ -154,7 +172,11 @@ def cms_page():
"""
return the content of a folder formatted for a menu
"""
- global _config
+ # global _config
+ global _route
+ _handler = _route.get()
+ _config = _handler.config()
+
_uri = os.sep.join([_config['layout']['root'],request.headers['uri']])
_id = request.headers['dom']
_args = {'layout':_config['layout']}
@@ -162,32 +184,38 @@ def cms_page():
_args['routes'] = _config['plugins']
- _system = cms.components.get_system(_config)
- print ([_uri])
- _html = cms.components.html(_uri,_id,_args,_system)
+ _system = _handler.system() #cms.components.get_system(_config)
+ _html = _handler.html(_uri,_id,_args,_system) #cms.components.html(_uri,_id,_args,_system)
e = Environment(loader=BaseLoader()).from_string(_html)
# _data = {} #cms.components.data(_config)
_system = cms.components.get_system(_config)
- _args['system'] = _system
+ _args['system'] = _handler.system(skip=['source','app'])
_html = e.render(**_args)
return _html,200
@_app.route('/page')
def _cms_page ():
- global _config
+ # global _config
+ global _route
+ _handler = _route.get()
+ _config = _handler.config()
_uri = request.args['uri']
_uri = os.sep.join([_config['layout']['root'],_uri])
_title = request.args['title'] if 'title' in request.args else ''
- _args = {'system':cms.components.get_system(_config) }
- if 'plugins' in _config:
- _args['routes'] = _config['plugins']
-
- _html = cms.components.html(_uri,_title,_args)
- e = Environment(loader=BaseLoader()).from_string(_html)
+ _args = {'system':_handler.system()} #cms.components.get_system(_config) }
+ # if 'plugins' in _config:
+ # _args['routes'] = _config['plugins']
+ _html = _handler.html(_uri,_title,_args) # cms.components.html(_uri,_title,_args)
+ e = Environment(loader=BaseLoader()).from_string(_html)
+ _args['system'] = _handler.system(skip=['app','source'])
return e.render(**_args),200
-@_app.route('/reload',methods=['POST'])
-def reload():
- pass
+
+@_app.route('/set/')
+def set(id):
+ global _route
+ _handler = _route.set(id)
+ return redirect('/')
+
#
# Let us bootup the application
SYS_ARGS = {}
@@ -210,44 +238,48 @@ if len(sys.argv) > 1:
i += 2
if __name__ == '__main__' :
+ pass
_path = SYS_ARGS['config'] if 'config' in SYS_ARGS else 'config.json'
if os.path.exists(_path):
- _config = json.loads((open (_path)).read())
- if 'theme' not in _config['system'] :
- _config['system']['theme'] = 'magazine.css'
- #
- # root can be either on disk or in the cloud ...
- # root: "" reading from disk
- # root: {uid,token,folder}
- #
+ _route = cms.engine.Router(path=_path)
+ _args = _route.get().get_app()
+ _app.run(**_args)
+ # _config = json.loads((open (_path)).read())
+ # if 'theme' not in _config['system'] :
+ # _config['system']['theme'] = 'magazine.css'
+ # #
+ # # root can be either on disk or in the cloud ...
+ # # root: "" reading from disk
+ # # root: {uid,token,folder}
+ # #
- _root = _config['layout']['root']
- _menu = cms.components.menu(_config)
- if 'order' in _config['layout'] and 'menu' in _config['layout']['order']:
- _sortedmenu = {}
- for _name in _config['layout']['order']['menu'] :
- if _name in _menu :
- _sortedmenu[_name] = _menu[_name]
+ # _root = _config['layout']['root']
+ # _menu = cms.components.menu(_config)
+ # if 'order' in _config['layout'] and 'menu' in _config['layout']['order']:
+ # _sortedmenu = {}
+ # for _name in _config['layout']['order']['menu'] :
+ # if _name in _menu :
+ # _sortedmenu[_name] = _menu[_name]
- _menu = _sortedmenu if _sortedmenu else _menu
- _config['layout']['menu'] = _menu #cms.components.menu(_config)
- # if 'data' in _config :
- # _config['data'] = cms.components.data(_config['data'])
- #
- _map = cms.components.plugins(_config)
- _config['plugins'] = _map
- # Let us load the plugins if any are available
- # if 'plugins' in _config :
- # _map = cms.components.plugins(_config)
- # if _map :
- # _config['plugins'] = _map
- #
- # register the functions with Jinja2
- # cms.components.context(_config)
+ # _menu = _sortedmenu if _sortedmenu else _menu
+ # _config['layout']['menu'] = _menu #cms.components.menu(_config)
+ # # if 'data' in _config :
+ # # _config['data'] = cms.components.data(_config['data'])
+ # #
+ # _map = cms.components.plugins(_config)
+ # _config['plugins'] = _map
+ # # Let us load the plugins if any are available
+ # # if 'plugins' in _config :
+ # # _map = cms.components.plugins(_config)
+ # # if _map :
+ # # _config['plugins'] = _map
+ # #
+ # # register the functions with Jinja2
+ # # cms.components.context(_config)
- _args = _config['system']['app']
- _app.run(**_args)
- else:
- print (__doc__)
- print ()
+ # _args = _config['system']['app']
+ # _app.run(**_args)
+ # else:
+ # print (__doc__)
+ # print ()
diff --git a/static/css/default.css b/static/css/default.css
index 77e81d3..2712a28 100644
--- a/static/css/default.css
+++ b/static/css/default.css
@@ -18,7 +18,7 @@ body {
.bold {font-weight:bold}
-.header img { width:100%;}
+.header img { width:100%; height: 100%;}
.footer {
text-align:center;
diff --git a/static/css/themes/oss.css b/static/css/themes/oss.css
index 6be1c32..90e949f 100644
--- a/static/css/themes/oss.css
+++ b/static/css/themes/oss.css
@@ -18,6 +18,8 @@
}
+.main .header .icon {width:40px; height:40px;}
+.main .header .icon img {width:40px; height:40px;}
.main .header .title { font-size:32px; text-transform: uppercase; font-weight:bold}
.main .header .subtitle {font-style:italic;font-size:14px; color:gray; text-transform: capitalize;}
@@ -37,9 +39,7 @@
blockquote {
border-left:8px solid #D3D3D3; padding:4px;
}
- .main .content table {border-spacing: 2;}
-.main .content table .fa-check {color:green; font-size:20px;}
-.main .content table .fa-times {color:RED; font-size:20px;}
+.main .content table {border-spacing: 2;}
.main .content table .active {font-size:14px}
.main .content .banner {
height:300px; margin:4px;
diff --git a/templates/header.html b/templates/header.html
index 0a3d6b6..7b6cfca 100644
--- a/templates/header.html
+++ b/templates/header.html
@@ -1,5 +1,7 @@
{% if layout.header.logo == True %}
-
+
+
+
{% endif %}
diff --git a/templates/index.html b/templates/index.html
index b2aff4d..11677ee 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -17,7 +17,7 @@ Vanderbilt University Medical Center
{{layout.header.title}}
-
+
diff --git a/templates/menu.html b/templates/menu.html
index e9dcfaa..5cb1312 100644
--- a/templates/menu.html
+++ b/templates/menu.html
@@ -1,4 +1,8 @@
-
+{%if system.portal %}
+
+{% else %}
+
+{% endif %}
{% for _name in layout.menu %}
@@ -8,14 +12,18 @@
{% for _item in layout.menu[_name] %}
- {%if _item.uri and _item.type not in ['dialog','embed'] %}
-