Bug fix, layout and charting improvements

community
Steve L. Nyemba 6 years ago
parent 407eb6f8ca
commit 117cd3840f

@ -270,6 +270,9 @@ def get_clients():
return ('',403) return ('',403)
@app.route('/1/clients',methods=['POST']) @app.route('/1/clients',methods=['POST'])
def set_clients() : def set_clients() :
"""
adding a client to the
"""
if 'key' in session : if 'key' in session :
clients = request.get_json(silent=True) clients = request.get_json(silent=True)
# #
@ -284,6 +287,9 @@ def set_clients() :
# writer.set({"clients":clients}) # writer.set({"clients":clients})
writer.set(clients=clients) writer.set(clients=clients)
return ('',200) return ('',200)
else:
ERROR_CODE = 403 if 'key' not in session else 400
return ('',ERROR_CODE)
@app.route("/1/emails",methods=['GET']) @app.route("/1/emails",methods=['GET'])
def get_emails(): def get_emails():
@ -379,7 +385,7 @@ def client_log():
logs = [body] logs = [body]
LIMIT = 40 LIMIT = 40
if len(logs) > LIMIT : if len(logs) > LIMIT :
log = logs[LIMIT:] log = logs[:LIMIT]
document[context][node] = {"date":date,"logs":logs} document[context][node] = {"date":date,"logs":logs}
writer.set(**document) writer.set(**document)
# writer.write (label=context,data=row) # writer.write (label=context,data=row)
@ -519,49 +525,56 @@ def folder_logs(id):
except Exception,e: except Exception,e:
print e; print e;
return r,status return r,status
@app.route("/1/set/logs",methods=["POST"])
def set_logs() :
if 'key' not in session :
return ('',403)
else :
key = session['key']
@app.route('/1/get/logs') pass
def get_logs() : # @app.route('/1/get/logs')
""" # def get_logs() :
@deprecate # """
""" # @deprecate
r = {} # """
#session['key'] = 'c259e8b1-e2fb-40df-bf03-f521f8ee352d' # r = {}
key = session['key'] if 'key' in session else None # #session['key'] = 'c259e8b1-e2fb-40df-bf03-f521f8ee352d'
if key is None and 'key' in request.headers: # key = session['key'] if 'key' in session else None
key = request.headers['key'] # if key is None and 'key' in request.headers:
if key is None : # key = request.headers['key']
return json.dumps(r) # if key is None :
else: # return json.dumps(r)
try: # else:
# try:
#gReader = factory.instance(type=class_read,args=p)
#plan = gReader.view('plans/active',key=key) # #gReader = factory.instance(type=class_read,args=p)
plan = session['plan'] # #plan = gReader.view('plans/active',key=key)
if plan : # plan = session['plan']
dbname = plan['name'] # if plan :
args = str(json.dumps(p)) # dbname = plan['name']
args = json.loads(args) # args = str(json.dumps(p))
args['dbname'] = dbname # args = json.loads(args)
args['uid'] = key # args['dbname'] = dbname
# # args['uid'] = key
# Let us persis this information (as well as the key) # #
#session['plan'] = plan['name'] # # Let us persis this information (as well as the key)
session['store']= args # #session['plan'] = plan['name']
session['key'] = key # session['store']= args
# session['key'] = key
scope = ['app_resources','folder_info','app_status_details','app_resource_usage_details'] #,'emails','log_size']
gReader = factory.instance(type=class_read,args=args) # scope = ['app_resources','folder_info','app_status_details','app_resource_usage_details'] #,'emails','log_size']
for id in scope : # gReader = factory.instance(type=class_read,args=args)
view = ('summary/'+id).strip() # for id in scope :
r[id] = gReader.view(view,key=key) # view = ('summary/'+id).strip()
if 'logs' in session : # r[id] = gReader.view(view,key=key)
for id in session['logs'] : # if 'logs' in session :
r[id] = session['logs'][id] # for id in session['logs'] :
# r[id] = r[node_id] # r[id] = session['logs'][id]
except Exception,e: # # r[id] = r[node_id]
print (e) # except Exception,e:
return json.dumps(r) # print (e)
# return json.dumps(r)
@app.route("/1/set/logs",methods=['PUT']) @app.route("/1/set/logs",methods=['PUT'])
def update_profile(): def update_profile():
try: try:
@ -616,71 +629,71 @@ def get_usage_trend():
return json.dumps(r) return json.dumps(r)
@app.route("/1/app/usage/trend") # @app.route("/1/app/usage/trend")
def get_usage_detail(): # def get_usage_detail():
""" # """
@deprecate # @deprecate
This function returns detailed information about usage per application monitored. It will return the 24 most recent observations in the logs # This function returns detailed information about usage per application monitored. It will return the 24 most recent observations in the logs
@param node node identifier e.g: apps@zulu.io # @param node node identifier e.g: apps@zulu.io
@return {node_x:{app_1:{memory_usage:[],cpu_usage:[]}},...} # @return {node_x:{app_1:{memory_usage:[],cpu_usage:[]}},...}
""" # """
r = {} # r = {}
try: # try:
if 'id' not in request.args and 'node' not in request.args : # if 'id' not in request.args and 'node' not in request.args :
id = session['default.node'] # id = session['default.node']
else: # else:
id = request.args['id'] if 'id' in request.args else request.args.get('node') # id = request.args['id'] if 'id' in request.args else request.args.get('node')
if 'app' not in request.args : # if 'app' not in request.args :
if 'default.app' in session : # if 'default.app' in session :
app_id = session['default.app'] # app_id = session['default.app']
else: # else:
app_id = None # app_id = None
else: # else:
app_id = request.args.get('app') # app_id = request.args.get('app')
# # #
# removing trailing white spaces # # removing trailing white spaces
gReader = factory.instance(type=class_read,args=p)
r = gReader.view('summary/app_resource_usage_details',key=p['uid'])
id = id.strip()
if app_id is not None :
app_id = app_id.strip()
r = r[id][app_id]
else :
r = r[id]
except Exception,e:
print ' *** ',(e)
return json.dumps(r)
@app.route('/1/app/status') # gReader = factory.instance(type=class_read,args=p)
def app_status() : # r = gReader.view('summary/app_resource_usage_details',key=p['uid'])
""" # id = id.strip()
@deprecate # if app_id is not None :
This function aggregates the number of crashes/running/idle instances found in the past 24 log entries # app_id = app_id.strip()
for a particular application # r = r[id][app_id]
@param nid node identifier e.g: app@zulu.io # else :
@param app application identifier e.g: kate, firefox, chrome ... specified in the configuraiton # r = r[id]
"""
r = [] # except Exception,e:
try: # print ' *** ',(e)
nid = request.args.get('node') # Node identifier # return json.dumps(r)
aid = request.args.get('app') # application identifier
# @app.route('/1/app/status')
# def app_status() :
# """
# @deprecate
# This function aggregates the number of crashes/running/idle instances found in the past 24 log entries
# for a particular application
# @param nid node identifier e.g: app@zulu.io
# @param app application identifier e.g: kate, firefox, chrome ... specified in the configuraiton
# """
# r = []
# try:
# nid = request.args.get('node') # Node identifier
# aid = request.args.get('app') # application identifier
gReader = factory.instance(type=class_read,args=p) # gReader = factory.instance(type=class_read,args=p)
r = gReader.view('summary/app_status_details',key=p['uid']) # r = gReader.view('summary/app_status_details',key=p['uid'])
# # #
#@TODO: Once the back-end enables the nodes in which the application is running, uncomment the line below # #@TODO: Once the back-end enables the nodes in which the application is running, uncomment the line below
# # #
print[nid,aid] # print[nid,aid]
r = r[nid][aid] # r = r[nid][aid]
except Exception,e: # except Exception,e:
print e # print e
return json.dumps(r) # return json.dumps(r)
#@app.route('/get/<id>') #@app.route('/get/<id>')
@ -831,30 +844,30 @@ def InitCollector():
This function/endpoint will assess n-virtual environments and return the results This function/endpoint will assess n-virtual environments and return the results
@TODO: Should this be stored for future mining (I don't think so but could be wrong) @TODO: Should this be stored for future mining (I don't think so but could be wrong)
""" """
@app.route('/sandbox') # @app.route('/sandbox')
def sandbox(): # def sandbox():
global CONFIG # global CONFIG
if 'sandbox' in CONFIG: #CONFIG['monitor']: # if 'sandbox' in CONFIG: #CONFIG['monitor']:
#handler = HANDLERS['sandbox']['class'] # #handler = HANDLERS['sandbox']['class']
#conf = HANDLERS['sandbox']['config'] # #conf = HANDLERS['sandbox']['config']
r = [] # r = []
# p = Factory.instance('sandbox',CONFIG) # # p = Factory.instance('sandbox',CONFIG)
handler = monitor.Sandbox() # handler = monitor.Sandbox()
conf = CONFIG['sandbox'] # conf = CONFIG['sandbox']
for id in conf: # for id in conf:
try: # try:
handler.init(conf[id]) # handler.init(conf[id])
r.append (dict(handler.composite(),**{"label":id})) # r.append (dict(handler.composite(),**{"label":id}))
except Exception as e: # except Exception as e:
pass # pass
else: # else:
r = [] # r = []
return json.dumps(r) # return json.dumps(r)
#@app.route('/trends') #@app.route('/trends')
#def trends (): #def trends ():
#id = request.args.get('id') #id = request.args.get('id')
@ -1046,43 +1059,86 @@ def user():
#d = [] #d = []
#return json.dumps(d) #return json.dumps(d)
@app.route("/1/plot/<id>",methods=['POST'])
def prepare_plot(id):
if 'key' in session :
body = request.get_json(silent=True)
REF = {"apps":['mem','cpu'],"folders":['files','size_in_kb']}
try:
node = body['node']
names = body['names']
params = REF[id]
args = {"type":SYS_STORE['class']['read'],"args":SYS_STORE['args']}
args['args']['uid'] = session['key']
args['context'] = SYS_ARGS.PARAMS['context']
dhandler = didact(config=args,key=session['key'])
logs = dhandler.m[id].filter_logs(node=node,names=names,params=params)
#
#
df = pd.DataFrame(logs)
q = []
XRAM = []
XCPU = []
xn = 0
xn_=0
html = """
The following are the last :count entries of
The analysis an average of :mean % usage, with recorded variatons of :sqrt
<br>Drops or spikes that are +/- :n would classify
"""
for name in names :
xram_ = df[df.name == name]['mem'].values.tolist()[-12:]
xcpu_ = df[df.name == name]['cpu'].values.tolist()[-12:]
xn = len(xram_) if len(xram_) > xn else xn
xn_ = len(xcpu_) if len(xcpu_) > xn_ else xn_
XRAM.append(xram_)
XCPU.append(xcpu_)
mean = np.mean(xram_)
max_ = np.max(xram_)
# X.append(np.random.choice(100,4).tolist())
q = [{"info":"Monitoring RAM consumption trends","ylabel":"% used","node":node,"x":XRAM,"labels":np.arange(1,xn).tolist(),"series":names,"title":"RAM Usage"}]
q.append({"info":"Monitoring CPU consumption trends","ylabel":"% used","node":node,"x":XCPU,"labels":np.arange(1,xn_).tolist(),"series":names,"title":"CPU Usage"})
key = 'app.trend.'+node
session[key] = q
return (key,200)
except Exception,e:
print (e)
return ('',400)
# return render_template("dashboard/apps/data-grid.html",body)
else:
return ('',403)
@app.route("/1/plot/<format>/<id>/<key>",methods=['GET']) @app.route("/1/plot/<format>/<id>/<key>",methods=['GET'])
def get_charts(format,id,key): def get_charts(format,id,key):
remove = request.args['no'].split(',') if 'no' in request.args else [] remove = request.args['no'].split(',') if 'no' in request.args else []
series = ['series_1'] series = ['series_1']
# args = dict(session[key],**{"remove":remove})
# print session['cloud-info']
# if id == 'pie' :
# X = list(np.arange(3) * np.random.randn() + 10)
# X[2] = 23.5
# else :
# X = []
# series = []
# for i in range(0,3):
# X.append([ np.random.randn()+10 for i in range(0,3)])
# series.append('series '+str(i))
# labels = session['cloud-info']['labels'] #['Foo','Boo','Joo']
# title = 'Do More'
# series = [ 'Series '+str(i+1) for i in range(len(labels))]
# args = {"x":X,"labels":labels,"title":title,"series":series,"remove":remove}
args = session[key] args = session[key]
if isinstance(args,list) : if isinstance(args,list) and 'index' in request.args:
index = int(request.args['index']) index = int(request.args['index'])
args = args[index] args = args[index]
# if remove is not None :
args['remove'] = remove args['remove'] = remove
info = [Graph.instance(format,id,**args)]
else:
# print args
info = [ Graph.instance(format,id, **dict(item,**{'remove':remove})) for item in args]
# if remove is not None :
# args['remove'] = remove
info = Graph.instance(format,id,**args) # info = Graph.instance(format,id,**args)
if format in ['image','png','jpeg','jpg'] : if format in ['image','png','jpeg','jpg'] :
# g = ImageGraphs() # g = ImageGraphs()
# stream = g.pie(X,labels,title) # stream = g.pie(X,labels,title)
return Response(info,mimetype='image/png') return Response(info,mimetype='image/png')
else: else:
args = {'chart_id': ('g_'+id)} args = {'chart_id': ('g_'+id)}
args['config'] = info args['info'] = info
args['context'] = SYS_ARGS.PARAMS['context'] if 'context' in SYS_ARGS.PARAMS else '' args['context'] = SYS_ARGS.PARAMS['context'] if 'context' in SYS_ARGS.PARAMS else ''
return render_template('dashboard/graphs/chart.html',**args) return render_template('dashboard/graphs/chart.html',**args)
if __name__== '__main__': if __name__== '__main__':

@ -16,7 +16,7 @@ body {
.left {float:left} .left {float:left}
.right{float:right} .right{float:right}
.no-float{float:none} .no-float{float:none}
.maroon {color:maroon}
.title-bar {padding:4px; margin:4px} .title-bar {padding:4px; margin:4px}
.title-bar .title {font-size:18px;} .title-bar .title {font-size:18px;}
.jsgrid-edit-row > .jsgrid-cell { .jsgrid-edit-row > .jsgrid-cell {

@ -1,44 +1,24 @@
<meta class="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
<link href="{{context}}/static/css/default.css" type="text/css" rel="stylesheet">
<link href="{{context}}static/css/fa/css/font-awesome.css" type="text/css" rel="stylesheet"> <link href="{{context}}/static/css/fa/css/font-awesome.css" type="text/css" rel="stylesheet">
<link href="{{context}}static/css/fa/animation.css" rel="stylesheet" type="text/css"> <link href="{{context}}/static/css/fa/animation.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jquery/jquery.min.js"></script> <script src="{{context}}/static/js/jquery/jquery.min.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script> <script src="{{context}}/static/js/jx/dom.js"></script>
<link href="{{context}}/static/js/jsgrid/jsgrid.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/js/jsgrid/jsgrid-theme.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jsgrid/jsgrid.js"></script> <script src="{{context}}/static/js/jsgrid/jsgrid.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script> <script src="{{context}}/static/js/jx/dom.js"></script>
<script src="{{context}}/static/js/jx/rpc.js"></script> <script src="{{context}}/static/js/jx/rpc.js"></script>
<link href="{{context}}/static/css/default.css" rel="stylesheet" type="text/css"> <link href="{{context}}/static/css/default.css" rel="stylesheet" type="text/css">
<script>
var DATA_GRID={{grid|tojson}}
var STATUS_ICONS = {
"S":"fa ellipsis-h",
"T":"fa fa-times",
"Z":"fa fa-exclamation-triangle",
"R":"fa fa-check"
}
DATA_GRID.width = '100%'
DATA_GRID.rowClass = function(item,index){
return "small"
}
$(document).ready(function(){
$("#grid").jsGrid(DATA_GRID)
})
</script>
<body> <body>
<div class="title-bar"> <div>
<span class="title">Application/Processes</span> <div>
</div> <div class="bold">RAM</div>
<div class="search-box border" style="padding:2px; margin-bottom:4px; display:grid; grid-template-columns: auto 64px">
<input type="text" id="search" placeholder="[Enter Application Name]" onkeyup="on_keyup(event)"/>
<div class="default" style="display:grid; grid-template-columns: 50% 50%; grid-gap:2px">
<div class="active" align="center" onclick="save_emails()"><i class="fa fa-check"></i></div>
<div class="active" align="center" onclick="clear_emails()"><i class="fa fa-times"></i></div>
</div> </div>
<div>
<div class="bold">CPU</div>
</div> </div>
<div id="grid"></div> </div>
</body> </body>

@ -1,3 +1,4 @@
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
<!-- <!--
This file is designed to provide a summary view of a given node This file is designed to provide a summary view of a given node
@ -6,6 +7,7 @@
<link href="{{context}}/static/css/fa/css/font-awesome.css" type="text/css" rel="stylesheet"> <link href="{{context}}/static/css/fa/css/font-awesome.css" type="text/css" rel="stylesheet">
<link href="{{context}}/static/css/fa/font-awesome-animation.css" rel="stylesheet" type="text/css"> <link href="{{context}}/static/css/fa/font-awesome-animation.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jquery/jquery.min.js"></script> <script src="{{context}}/static/js/jquery/jquery.min.js"></script>
<script src="{{context}}/static/js/jx/ext/modal.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script> <script src="{{context}}/static/js/jx/dom.js"></script>
<link href="{{context}}/static/js/jsgrid/jsgrid.css" rel="stylesheet" type="text/css"> <link href="{{context}}/static/js/jsgrid/jsgrid.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/js/jsgrid/jsgrid-theme.css" rel="stylesheet" type="text/css"> <link href="{{context}}/static/js/jsgrid/jsgrid-theme.css" rel="stylesheet" type="text/css">
@ -42,8 +44,8 @@
font-size:18px; font-size:18px;
align-items: center; align-items: center;
display:grid; display:grid;
grid-template-columns: auto 95px; grid-template-columns: 70% auto;
grid-gap:2px; grid-gap:1px;
min-height:24px; min-height:24px;
} }
.widget .chart { .widget .chart {
@ -104,6 +106,7 @@
.fa-times {color:maroon} .fa-times {color:maroon}
.fa-folder-open {color:#FF7F24;} .fa-folder-open {color:#FF7F24;}
.fa-cog {color:gray} .fa-cog {color:gray}
.fa-dashboard {color:#4682b4}
.busy { .busy {
display:flex ; display:flex ;
align-items: center; align-items: center;
@ -111,6 +114,17 @@
height:100%; height:100%;
} }
.action-group {display:grid; grid-gap:1px; grid-template-columns:repeat(3,64px); justify-content: end}
.hidden {display:none}
.dialog-frame {
padding:4px;
width:50%;
}
.dialog {padding:4px; font-size:12px; min-height:10%}
.dialog .title { font-weight: bold; padding:4px; margin-bottom:4px; background:#f3f3f3; }
.dialog .title i {color:ff5733}
.dialog .message {padding:8px; font-family:verdana; min-height:10%}
</style> </style>
<script> <script>
@ -126,19 +140,129 @@
// config.data = apps // config.data = apps
// $('#grid').jsGrid(config) // $('#grid').jsGrid(config)
// }) // })
var grid = {}
grid.format = {}
grid.format.apps = function(){
var m = {}
jx.utils.patterns.visitor(logs,function(row){
var id = row.name
if (m[id] == null){
m[id] = {"name":id,"users":1,"proc_count":1,"mem":row.mem,"cpu":row.cpu}
}else{
m[id].users += 1
m[id].proc_count += 1
m[id].mem += row.mem
m[id].cpu += row.cpu
}
})
}
{{app_summary|tojson}} {{app_summary|tojson}}
var render_grid = function(name){ var render_grid = function(name){
var logs = {{app_logs|tojson}}[name]['logs'] var logs = {{app_logs|tojson}}[name]['logs']
var id = '#app_grid_'+name
var config = {{ app_grid|tojson}} var config = {{ app_grid|tojson}}
config.data = logs config.data = logs
var _id = id.replace('#','')
jx.dom.set.attribute(_id,'selected',{})
config.fields[0].itemTemplate = function(value,item){
var i = jx.dom.get.instance('I')
var selected = jx.dom.get.attribute(_id,'selected')
var key = item.name+'@'+item.user
if(selected[key] == null){
i.className = 'fa fa-square-o'
}else{
i.className = 'fa fa-check'
}
i.data = item
i.onclick = function(){
var selected = jx.dom.get.attribute(_id,'selected')
var key = this.data.name+'@'+this.data.user
if(this.className == 'fa fa-square-o') {
this.className = 'fa fa-check'
selected[key] = this.data
}else{
this.className = 'fa fa-square-o'
delete selected[key]
}
jx.dom.set.attribute(_id,'selected',selected)
$(id).jsGrid("option","fields")[0].headerTemplate() //.headerTemplate()
// $(id).jsGrid("refresh")
// console.log(p)
}
return i
}
config.fields[0].headerTemplate = function(){
// if(_id == null){
// this.title = null
// }else{
// console.log(keys.length == 0)
// if (keys.length > 0){
var i = jx.dom.get.instance('I')
i.className = 'active fa fa-line-chart'
i.onclick = function(){
var selected = jx.dom.get.attribute(_id,'selected')
var keys = Object.keys(selected)
if(keys.length == 0){
jx.dom.set.value('dialog_title','<i class="fa fa-warning"></i> Trends')
jx.dom.set.value('dialog_msg','Select a row to visualize trends')
var html = jx.dom.get.value('dialog').replace(/hidden/,'')
jx.modal.show(html)
}else{
var uri = '/monitor/1/plot/apps'
var body = {node:_id.replace(/app_grid_/,'')}
body.names = jx.utils.patterns.visitor(keys,function(id){
return selected[id].name
})
var httpclient = HttpClient.instance()
httpclient.setData(JSON.stringify(body))
httpclient.setHeader("Content-Type","application/json")
httpclient.post(uri,function(x){
if(x.status == 200){
var key = x.responseText.trim()
jx.modal.show({url:'/monitor/1/plot/html/line/'+key+'?draw-all' })
}else{
console.log(x.status)
}
})
}
}
this.title = i
// }
return (this.title === undefined || this.title === null) ? this.name : this.title;
// }else{
}
config.fields[config.fields.length-1].itemTemplate = function(value,item){ config.fields[config.fields.length-1].itemTemplate = function(value,item){
var p = {"X":"fa fa-times","S":"fa fa-ellipsis-h","S+":"fa fa-ellipsis-h","R":"fa fa-check"} var p = {"X":"fa fa-times","S":"fa fa-ellipsis-h","S+":"fa fa-ellipsis-h","R":"fa fa-check"}
var i = jx.dom.get.instance('I') var i = jx.dom.get.instance('I')
i.className = p[value] != null? p[value]: 'fa fa-cog fa-spin' i.className = p[value] != null? p[value]: 'fa fa-cog fa-spin'
return i return i
} }
var id = '#app_grid_'+name
$(id).jsGrid(config) $(id).jsGrid(config)
} }
/** /**
@ -161,23 +285,42 @@
<div class="small bold" style="margin-top:10px; color:#4682B4">Loading dashboard ...</div> <div class="small bold" style="margin-top:10px; color:#4682B4">Loading dashboard ...</div>
</div> </div>
</div> </div>
<div id="dialog" class="dialog-frame hidden">
<div class="dialog">
<div id="dialog_title" class="title">
</div>
<div id="dialog_msg" class="message">
</div>
<div>
<div class="button border-round border" align="center">
Ok
</div>
</div>
</div>
</div>
<div class="board"> <div class="board">
<div class="search"> <!-- <div class="search">
<input type="text" placeholder="[Node/Data Collector]"> <input type="text" placeholder="[Node/Data Collector]">
<div align="left" style="align-items: center"> <div align="left" style="align-items: center">
<span class="active"><i class="fa fa-trash"></i></span> <span class="active"><i class="fa fa-trash"></i></span>
</div> </div>
</div> </div> -->
<div class=" border-round"> <div class=" border-round">
{% for name in nodes %} {% for name in nodes %}
<div class="widget"> <div class="widget">
<div class="title border-bottom" align='center'> <div class="title border-bottom" align='center'>
<div align="center">
<h3>{{name}}</h3> <h3>{{name}}</h3>
<div> </div>
<div class="action-group" align="right">
<span class="active"><i class="fa fa-file-pdf-o"></i></span>
<span id="{{name}}_o" class="active" onclick="show_hide(['{{name}}_data','{{name}}_i'],['{{name}}','{{name}}_o'])" ><i class="fa fa-th" style="color:darkgray"></i></span> <div id="{{name}}_i" title="Dashboard" class="active" onclick="show_hide(['{{name}}'],['{{name}}_data'])" style="margin:1px; padding-left:20px; padding-right:20px; background-color:#f3f3f3"><i class="fa fa-dashboard" style=""></i></div>
<span id="{{name}}_i" class="active" onclick="show_hide(['{{name}}','{{name}}_o'],['{{name}}_i','{{name}}_data'])" style="display:none"><i class="fa fa-pie-chart" style=" color:orangered"></i></span> <div id="{{name}}_o" title="Data Trends" class="active" onclick="show_hide(['{{name}}_data'],['{{name}}'])" style="margin:1px; padding-left:20px; padding-right:20px; background-color:#f3f3f3"><i class="fa fa-th" style=""></i></div>
<div class="active" title="Download Data" style="margin:1px; padding-left:20px; padding-right:20px; background-color:#f3f3f3"><i class="fa fa-download"></i></div>
</div> </div>
@ -328,6 +471,12 @@
</div> </div>
<div id="{{name}}_data"class="grid no-border" style="display:none;"> <div id="{{name}}_data"class="grid no-border" style="display:none;">
<p>
Apps On <b>{{name}}</b>
<div class="small">
click select names and visualize trends
</div>
</p>
<div id="app_grid_{{name}}" style="margin-top:10px;"></div> <div id="app_grid_{{name}}" style="margin-top:10px;"></div>
<script> <script>

@ -1,3 +1,4 @@
<meta charset="utf-8"/>
<!-- <!--
generating client keys generating client keys
--> -->

@ -1,9 +1,13 @@
<meta charset="utf-8"/>
<!-- <link rel="shortcut icon" href="{{context}}/static/img/logo-small.png" type="image/x-icon"> --> <!-- <link rel="shortcut icon" href="{{context}}/static/img/logo-small.png" type="image/x-icon"> -->
<link rel="icon" href="{{context}}/static/img/logo-small.png" type="image/x-icon"> <link rel="icon" href="{{context}}/static/img/logo-small.png" type="image/x-icon">
<link href="{{context}}/static/css/default.css" type="text/css" rel="stylesheet">
<link href="{{context}}/static/chartist/chartist.css" type="text/css" rel="stylesheet"> <link href="{{context}}/static/chartist/chartist.css" type="text/css" rel="stylesheet">
<script src="{{context}}/static/js/chart.js/chart.bundle.js"></script> <script src="{{context}}/static/js/chart.js/chart.bundle.js"></script>
<script src="{{context}}/static/js/jquery/jquery.min.js"></script> <script src="{{context}}/static/js/jquery/jquery.min.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<script src="{{context}}/static/js/jx/utils.js"></script>
<script src="{{context}}/static/js/jx/ext/math.js"></script>
{% block content %} {% endblock%} {% block content %} {% endblock%}

@ -1,53 +1,53 @@
{% extends 'dashboard/graphs/base.html' %} {% extends 'dashboard/graphs/base.html' %}
{% block content %} {% block content %}
<style>
.board {
display:grid;
grid-template-columns: repeat(auto-fit,49%);
grid-gap:0px;
}
.border-round {padding:8px; margin:8px; border-radius:8px;}
.title {margin:4px; font-size:18px; font-family:verdana}
</style>
<script> <script>
$(document).ready(function(){
// Chart.plugins.register({
// afterDatasetsDraw: function(chart) {
// var ctx = chart.chart.ctx; var render_chart =function(index,config){
// chart.data.datasets.forEach(function(dataset, i) { var id = 'chart_'+index
// var meta = chart.getDatasetMeta(i); var canvas = jx.dom.get.instance('CANVAS')
// if (!meta.hidden) { var context = canvas.getContext('2d')
// meta.data.forEach(function(element, index) { jx.dom.append(id,canvas)
// // Draw the text in black, with the specified font new Chart(context, config)
// ctx.fillStyle = 'rgb(0, 0, 0)';
// var fontSize = 16;
// var fontStyle = 'normal';
// var fontFamily = 'Helvetica Neue';
// ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
// // Just naively convert to string for now
// var dataString = dataset.datalabels[index].toString();
// // Make sure alignment settings are correct
// ctx.textAlign = 'center';
// ctx.textBaseline = 'middle';
// ctx.fillStyle = 'white'
// var padding = 5;
// var position = element.tooltipPosition();
// ctx.fillText(dataString, position.x -40 , position.y - (fontSize / 2) - padding);
// });
// }
// });
// }
// });
var context = document.getElementById('{{chart_id|safe}}').getContext('2d') }
var args = {{config|tojson}} // $(document).ready(function(){
// args.options.plugins.datalabels = {backgroundColor:'rgba(242,242,242,0.7)',
// display:function(context){
// var labels = context.dataset.datalabels
// var index = context.dataIndex
// return labels[index]
// }
// }
new Chart(context, args)
})
// })
</script> </script>
<div> <body>
<canvas id="{{chart_id|safe}}"></canvas> {% if info|length == 1 %}
</div> <div>
{% else %}
<div class="board">
{% endif %}
{% for config in info %}
{% if info|length > 1%}
<div class="border border-round">
{% else %}
<div>
{% endif %}
<div class="title bold" align="center">{{config.title}}</div>
<!-- <canvas id="{{chart_id_|safe}}"></canvas> -->
<div id="chart_{{loop.index|int}}"></div>
<div id="descr_{{loop.index|int}}" class="border-top" align="center">
<br>{{config.info}}
</div>
<script>
render_chart({{loop.index|int}},{{config|tojson}})
</script>
</div>
{% endfor %}
</div>
</body>
{% endblock %} {% endblock %}

@ -1,6 +1,7 @@
<!-- <!--
Managing user emails that will be notified or receive emails from the user Managing user emails that will be notified or receive emails from the user
--> -->
<meta charset="utf-8"/>
<script src="{{context}}/static/js/jquery/jquery.min.js"></script> <script src="{{context}}/static/js/jquery/jquery.min.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script> <script src="{{context}}/static/js/jx/dom.js"></script>

@ -19,6 +19,7 @@ class Graph :
@param format format of the chart to be rendered <{image,png,jpeg}| html> @param format format of the chart to be rendered <{image,png,jpeg}| html>
@param name name of the chart (pie|scatter|line|bar) @param name name of the chart (pie|scatter|line|bar)
""" """
_objects = []
if format.lower() in ['jpeg','svg','image','png','jpg','bmp','tiff'] : if format.lower() in ['jpeg','svg','image','png','jpg','bmp','tiff'] :
_object = ImageGraphs(format,**args) _object = ImageGraphs(format,**args)
else: else:
@ -115,6 +116,7 @@ class HTMLGraph(Graph):
if len(X) == 2 : if len(X) == 2 :
config['options']['cutoutPercentage']= 70 config['options']['cutoutPercentage']= 70
config['info'] = self.args['info'] if 'info' in self.args else ''
return config return config
def bar(self): def bar(self):
config = self.line() config = self.line()
@ -127,7 +129,7 @@ class HTMLGraph(Graph):
labels = self.args['labels'] labels = self.args['labels']
title = self.args['title'] title = self.args['title']
series = self.args['series'] series = self.args['series']
config = {"type":"line","responsive":True,"data":{"datasets":[],"labels":labels}} config = {"title":title,"type":"line","responsive":True,"data":{"datasets":[],"labels":labels}}
config["options"] = {"responsive":True,"legend":{"position":"right","display":False},"title":{"display":True,"text":title},"animation":{"animateScale":True,"animateRotate":True}} config["options"] = {"responsive":True,"legend":{"position":"right","display":False},"title":{"display":True,"text":title},"animation":{"animateScale":True,"animateRotate":True}}
if 'legend' not in self.args['remove'] : if 'legend' not in self.args['remove'] :
config['options']['legend']["display"] = True config['options']['legend']["display"] = True
@ -137,6 +139,7 @@ class HTMLGraph(Graph):
if "xlabel" in self.args : if "xlabel" in self.args :
config['options']['scales']['xAxes'] = [{"scaleLabel":{"display":True,"labelString":self.args['xlabel']}}] config['options']['scales']['xAxes'] = [{"scaleLabel":{"display":True,"labelString":self.args['xlabel']}}]
config["data"]["labels"] = labels config["data"]["labels"] = labels
config['info'] = self.args['info'] if 'info' in self.args else ''
if isinstance(X[0],list) == False : if isinstance(X[0],list) == False :
X = [X[0]] X = [X[0]]

@ -19,7 +19,37 @@ class analytics :
self.cache = {} self.cache = {}
self.cache['key'] = args['key'] self.cache['key'] = args['key']
self.cache['name'] = self.__class__.__name__ self.cache['name'] = self.__class__.__name__
self.init() self.init()
#
# Let's extract the logs for processing later on
#
id = self.get('name')
self.raw_logs = self.handler.read()[id]
def filter_logs(self,**args):
"""
This function will filter logs given node, names and parameters
@param node identifier of the node
@param names items to be extracted (folernames, app names)
@param params variables of interest
"""
id = args['node']
names = args['names']
params = args['params']
if id in self.raw_logs :
matrix = self.raw_logs[id]['logs']
r = []
LIMIT = 24
for row in matrix :
r += row
r= pd.DataFrame(r)[['name']+params]
return r[r.name.isin( names)].to_dict(orient='records')
# .to_dict(orient='records')
else:
return None
def get_formatted_date(self,row): def get_formatted_date(self,row):
m = {1:"Jan",2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",7:"Jul",8:"Aug",9:"Sep",10:"Oct",11:"Nov",12:"Dec"} m = {1:"Jan",2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",7:"Jul",8:"Aug",9:"Sep",10:"Oct",11:"Nov",12:"Dec"}
return "-".join([m[row['month']],str(row['day']),str(row['year'])]) +" "+ " ".join([str(row['hour']),'h :',str(row['minute']),'min' ]) return "-".join([m[row['month']],str(row['day']),str(row['year'])]) +" "+ " ".join([str(row['hour']),'h :',str(row['minute']),'min' ])
@ -38,17 +68,6 @@ class analytics :
self.set('summary',self.summary(logs)) self.set('summary',self.summary(logs))
self.format(logs) self.format(logs)
# if id in r :
# nodes = r[id].keys()
# print nodes
# self.set('nodes',nodes)
# logs = {}
# for name in nodes :
# logs[name] = r[id][name]['log']
# self.set('logs',logs)
# self.set('summary',self.summary(r[id]))
# self.format(self.summary(r[id]))
def summary(self,logs) : def summary(self,logs) :
raise Exception("needs to be implemented") raise Exception("needs to be implemented")
@ -78,11 +97,7 @@ class apps(analytics) :
""" """
def __init__(self,**args): def __init__(self,**args):
analytics.__init__(self,**args) analytics.__init__(self,**args)
logs = self.get('logs')
# for node in logs :
# data = logs[node]
# df = pd.DataFrame(data)
# df.groupby(['name'],as_index=False).count()
def init(self): def init(self):
""" """
This function will handle initialization of critical variables This function will handle initialization of critical variables
@ -90,7 +105,7 @@ class apps(analytics) :
""" """
analytics.init(self) analytics.init(self)
grid = {"width":"100%","editing":False,"rowClass":"small"} grid = {"width":"100%","editing":False,"rowClass":"small"}
grid['fields'] = [{"name":"name","title":"Process","headercss":"small"},{"name":"cpu","title":"CPU Usage","headercss":"small","type":"number"},{"name":"mem","title":"RAM Usage","headercss":"small","type":"number"},{"name":"status","title":"Status","headercss":"small","align":"center","width":"32px"}] grid['fields'] = [{"name":"","width":32,"align":"center"},{"name":"name","title":"Name","headercss":"small"},{"name":"user","title":"User","headercss":"small"},{"name":"cpu","title":"CPU","headercss":"small","type":"number"},{"name":"mem","title":"RAM","headercss":"small","type":"number"},{"name":"status","title":"Status","headercss":"small","align":"center","width":"32px"}]
self.set('grid',grid) self.set('grid',grid)
def summary(self,data): def summary(self,data):
@ -112,24 +127,11 @@ class apps(analytics) :
r.append({"date":date,"node":node,"running":running,"idle":idle,"crash":crash,"formatted_date":formatted_date}) r.append({"date":date,"node":node,"running":running,"idle":idle,"crash":crash,"formatted_date":formatted_date})
return r return r
# logs = pd.DataFrame(self.get('logs'))
# r = []
# for id in logs :
# row = pd.DataFrame(logs[id]['log'])
# crash = np.sum(row.status == 'X')
# idle = np.sum(row.status == 'S') + np.sum(row.status == 'S+')
# cpu = row.cpu.sum()
# mem = row.mem.sum() #{"sum":row.mem.sum(),"mean":row.mem.mean(),"sd":np.sqrt(row.mem.var())}
# running = row.shape[0] - crash - idle
# r.append({"node":id,"date":logs[id]['date']['long'],"running":running,"idle":idle,"crash":crash,"mem":mem,"cpu":cpu})
# return r
# self.set("summary",)
def format(self,data) : def format(self,data) :
""" """
This function adds other somewhat important statistics : This function adds other somewhat important statistics :
- resources used for the node - resources used for the node
- the data is limited to the last pull
""" """
r = [] r = []
@ -235,8 +237,10 @@ class didact():
key = args['key'] key = args['key']
collection = [apps(config=config,key=key),folders(config=config,key=key)] collection = [apps(config=config,key=key),folders(config=config,key=key)]
self.cache = {} self.cache = {}
self.m = {}
for item in collection : for item in collection :
self.cache = dict(self.cache,**item.cache) self.cache = dict(self.cache,**item.cache)
self.m[item.get('name')] = item
r = [] r = []
for item in collection: for item in collection:

Loading…
Cancel
Save