commit
ad56951807
@ -0,0 +1,37 @@
|
||||
#
|
||||
# Let us create an image for healthcareio
|
||||
# The image will contain the {X12} Parser and the
|
||||
# FROM ubuntu:bionic-20200403
|
||||
FROM ubuntu:focal
|
||||
RUN ["apt-get","update","--fix-missing"]
|
||||
RUN ["apt-get","upgrade","-y"]
|
||||
|
||||
RUN ["apt-get","-y","install","apt-utils"]
|
||||
|
||||
RUN ["apt-get","update","--fix-missing"]
|
||||
RUN ["apt-get","upgrade","-y"]
|
||||
RUN ["apt-get","install","-y","mongodb","sqlite3","sqlite3-pcre","libsqlite3-dev","python3-dev","python3","python3-pip","git","python3-virtualenv","wget"]
|
||||
#
|
||||
#
|
||||
RUN ["pip3","install","--upgrade","pip"]
|
||||
RUN ["pip3","install","numpy","pandas","git+https://dev.the-phi.com/git/steve/data-transport","botocore","matplotlib"]
|
||||
# RUN ["pip3","install","git+https://healthcare.the-phi.com/git/code/parser.git","botocore"]
|
||||
# RUN ["useradd", "-ms", "/bin/bash", "health-user"]
|
||||
# USER health-user
|
||||
#
|
||||
# This volume is where the data will be loaded from (otherwise it is assumed the user will have it in the container somehow)
|
||||
#
|
||||
VOLUME ["/data","/app/healthcareio"]
|
||||
WORKDIR /app
|
||||
ENV PYTHONPATH="/app"
|
||||
|
||||
#
|
||||
# This is the port from which some degree of monitoring can/will happen
|
||||
EXPOSE 80
|
||||
EXPOSE 27017
|
||||
# wget https://healthcareio.the-phi.com/git/code/parser.git/bootup.sh
|
||||
COPY bootup.sh bootup.sh
|
||||
ENTRYPOINT ["bash","-C"]
|
||||
CMD ["bootup.sh"]
|
||||
# VOLUME ["/home/health-user/healthcare-io/","/home-healthuser/.healthcareio"]
|
||||
# RUN ["pip3","install","git+https://healthcareio.the-phi.com/git"]
|
@ -0,0 +1,10 @@
|
||||
set -e
|
||||
/etc/init.d/mongodb start
|
||||
cd /app
|
||||
export
|
||||
export PYTHONPATH=$PWD
|
||||
ls
|
||||
# python3 healthcareio/healthcare-io.py --signup $EMAIL --store mongo
|
||||
# python3 healthcareio/healthcare-io.py --analytics --port 80 --debug
|
||||
|
||||
bash
|
@ -0,0 +1,170 @@
|
||||
"""
|
||||
This file serves as proxy to healthcare-io, it will be embedded into the API
|
||||
"""
|
||||
import os
|
||||
import transport
|
||||
import numpy as np
|
||||
import x12
|
||||
import pandas as pd
|
||||
import smart
|
||||
from analytics import Apex
|
||||
import time
|
||||
class get :
|
||||
PROCS = []
|
||||
PATH = os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])
|
||||
@staticmethod
|
||||
def resume (files,args):
|
||||
"""
|
||||
This function will determine the appropriate files to be processed by performing a simple complementary set operation against the logs
|
||||
@TODO: Support data-stores other than mongodb
|
||||
:param files list of files within a folder
|
||||
:param _args configuration
|
||||
"""
|
||||
_args = args['store'].copy()
|
||||
if 'mongo' in _args['type'] :
|
||||
_args['type'] = 'mongo.MongoReader'
|
||||
reader = transport.factory.instance(**_args)
|
||||
_files = []
|
||||
try:
|
||||
pipeline = [{"$match":{"completed":{"$eq":True}}},{"$group":{"_id":"$name"}},{"$project":{"name":"$_id","_id":0}}]
|
||||
_args = {"aggregate":"logs","cursor":{},"allowDiskUse":True,"pipeline":pipeline}
|
||||
_files = reader.read(mongo = _args)
|
||||
_files = [item['name'] for item in _files]
|
||||
except Exception as e :
|
||||
pass
|
||||
print (["found ",len(files),"\tProcessed ",len(_files)])
|
||||
return list(set(files) - set(_files))
|
||||
|
||||
@staticmethod
|
||||
def processes(_args):
|
||||
_info = pd.DataFrame(smart.top.read(name='healthcare-io.py'))[['name','cpu','mem']]
|
||||
|
||||
if _info.shape[0] == 0 :
|
||||
_info = pd.DataFrame({"name":["healthcare-io.py"],"cpu":[0],"mem":[0]})
|
||||
# _info = pd.DataFrame(_info.groupby(['name']).sum())
|
||||
# _info['name'] = ['healthcare-io.py']
|
||||
m = {'cpu':'CPU','mem':'RAM','name':'name'}
|
||||
_info.columns = [m[name] for name in _info.columns.tolist()]
|
||||
_info.index = np.arange(_info.shape[0])
|
||||
|
||||
charts = []
|
||||
for label in ['CPU','RAM'] :
|
||||
value = _info[label].sum()
|
||||
df = pd.DataFrame({"name":[label],label:[value]})
|
||||
charts.append (
|
||||
Apex.apply(
|
||||
{"data":df, "chart":{"type":"radial","axis":{"x":label,"y":"name"}}}
|
||||
)['apex']
|
||||
)
|
||||
#
|
||||
# This will update the counts for the processes, upon subsequent requests so as to show the change
|
||||
#
|
||||
N = 0
|
||||
lprocs = []
|
||||
for proc in get.PROCS :
|
||||
if proc.is_alive() :
|
||||
lprocs.append(proc)
|
||||
N = len(lprocs)
|
||||
get.PROCS = lprocs
|
||||
return {"process":{"chart":charts,"counts":N}}
|
||||
@staticmethod
|
||||
def files (_args):
|
||||
_info = smart.folder.read(path='/data')
|
||||
N = _info.files.tolist()[0]
|
||||
if 'mongo' in _args['store']['type'] :
|
||||
store_args = dict(_args['store'].copy(),**{"type":"mongo.MongoReader"})
|
||||
# reader = transport.factory.instance(**_args)
|
||||
|
||||
pipeline = [{"$group":{"_id":"$name","count":{"$sum":{"$cond":[{"$eq":["$completed",True]},1,0]}} }},{"$group":{"_id":None,"count":{"$sum":"$count"}}},{"$project":{"_id":0,"status":"completed","count":1}}]
|
||||
query = {"mongo":{"aggregate":"logs","allowDiskUse":True,"cursor":{},"pipeline":pipeline}}
|
||||
# _info = pd.DataFrame(reader.read(mongo={"aggregate":"logs","allowDiskUse":True,"cursor":{},"pipeline":pipeline}))
|
||||
pipeline = [{"$group":{"_id":"$parse","claims":{"$addToSet":"$name"}}},{"$project":{"_id":0,"type":"$_id","count":{"$size":"$claims"}}}]
|
||||
_query = {"mongo":{"aggregate":"logs","cursor":{},"allowDiskUse":True,"pipeline":pipeline}} #-- distribution claims/remits
|
||||
|
||||
|
||||
else:
|
||||
store_args = dict(_args['store'].copy(),**{"type":"disk.SQLiteReader"})
|
||||
store_args['args']['table'] = 'logs'
|
||||
query= {"sql":"select count(distinct json_extract(data,'$.name')) as count, 'completed' status from logs where json_extract(data,'$.completed') = true"}
|
||||
_query={"sql":"select json_extract(data,'$.parse') as type,count(distinct json_extract(data,'$.name')) as count from logs group by type"} #-- distribution claim/remits
|
||||
reader = transport.factory.instance(**store_args)
|
||||
_info = pd.DataFrame(reader.read(**query))
|
||||
if not _info.shape[0] :
|
||||
_info = pd.DataFrame({"status":["completed"],"count":[0]})
|
||||
_info['count'] = np.round( (_info['count'] * 100 )/N,2)
|
||||
|
||||
charts = [Apex.apply({"data":_info,"chart":{"type":"radial","axis":{"y":"status","x":"count"}}})['apex']]
|
||||
#
|
||||
# Let us classify the files now i.e claims / remits
|
||||
#
|
||||
|
||||
|
||||
# pipeline = [{"$group":{"_id":"$parse","claims":{"$addToSet":"$name"}}},{"$project":{"_id":0,"type":"$_id","count":{"$size":"$claims"}}}]
|
||||
# _args = {"aggregate":"logs","cursor":{},"allowDiskUse":True,"pipeline":pipeline}
|
||||
# r = pd.DataFrame(reader.read(mongo=_args))
|
||||
r = pd.DataFrame(reader.read(**_query)) #-- distribution claims/remits
|
||||
r = Apex.apply({"chart":{"type":"donut","axis":{"x":"count","y":"type"}},"data":r})['apex']
|
||||
r['chart']['height'] = '100%'
|
||||
r['legend']['position'] = 'bottom'
|
||||
|
||||
charts += [r]
|
||||
|
||||
|
||||
return {"files":{"counts":N,"chart":charts}}
|
||||
|
||||
pass
|
||||
#
|
||||
# Process handling ....
|
||||
|
||||
|
||||
def run (_args) :
|
||||
"""
|
||||
This function will run the jobs and insure as processes (as daemons).
|
||||
:param _args system configuration
|
||||
"""
|
||||
FILES = []
|
||||
BATCH = int(_args['args']['batch']) #-- number of processes (poorly named variable)
|
||||
|
||||
for root,_dir,f in os.walk(_args['args']['folder']) :
|
||||
if f :
|
||||
FILES += [os.sep.join([root,name]) for name in f]
|
||||
FILES = get.resume(FILES,_args)
|
||||
FILES = np.array_split(FILES,BATCH)
|
||||
|
||||
for FILE_GROUP in FILES :
|
||||
|
||||
FILE_GROUP = FILE_GROUP.tolist()
|
||||
# logger.write({"process":index,"parse":_args['parse'],"file_count":len(row)})
|
||||
# proc = Process(target=apply,args=(row,info['store'],_info,))
|
||||
parser = x12.Parser(get.PATH) #os.sep.join([PATH,'config.json']))
|
||||
parser.set.files(FILE_GROUP)
|
||||
parser.daemon = True
|
||||
parser.start()
|
||||
get.PROCS.append(parser)
|
||||
time.sleep(3)
|
||||
#
|
||||
# @TODO:consider submitting an update to clients via publish/subscribe framework
|
||||
#
|
||||
return get.PROCS
|
||||
def stop(_args):
|
||||
for job in get.PROCS :
|
||||
if job.is_alive() :
|
||||
job.terminate()
|
||||
get.PROCS = []
|
||||
#
|
||||
# @TODO: consider submitting an update to clients via publish/subscribe framework
|
||||
pass
|
||||
def write(src_args,dest_args,files) :
|
||||
#
|
||||
# @TODO: Support for SQLite
|
||||
pass
|
||||
def publish (src_args,dest_args,folder="/data"):
|
||||
FILES = []
|
||||
for root,_dir,f in os.walk(folder) :
|
||||
if f :
|
||||
FILES += [os.sep.join([root,name]) for name in f]
|
||||
#
|
||||
# @TODO: Add support for SQLite ....
|
||||
|
||||
FILES = np.array_split(FILES,4)
|
||||
|
@ -0,0 +1,21 @@
|
||||
<div class="dialog">
|
||||
<div class="title-bar">
|
||||
<div class="title bold" ></div>
|
||||
<div class="active close" align="center"><i class="fas fa-times"></i></div>
|
||||
</div>
|
||||
<div class="message">
|
||||
<div class="icon" align="center">
|
||||
<i id="msg-icon"></i>
|
||||
</div>
|
||||
<div class="text"></div>
|
||||
</div>
|
||||
|
||||
<div class="action">
|
||||
<div class="active-button border-round" align="center">
|
||||
<div align="center">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
<div class="bold">Ok</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,75 @@
|
||||
/***
|
||||
* This file will handle the dialog boxes as they and their associated configurations and function binding
|
||||
*/
|
||||
if (!dialog){
|
||||
var dialog = {}
|
||||
}
|
||||
|
||||
dialog.open = function(title,msg,pointer){
|
||||
if (sessionStorage.dialog == null){
|
||||
|
||||
|
||||
var http = HttpClient.instance()
|
||||
http.get(sessionStorage.io_context+'/static/dialog.html',function(x){
|
||||
var html = x.responseText
|
||||
jx.modal.show({html:html,id:'dialog'})
|
||||
$('.dialog .title').text(title)
|
||||
$('.dialog .message .text').text(msg)
|
||||
dialog.status.ask()
|
||||
$('.dialog .action .active-button').on('click',pointer)
|
||||
$('.dialog .title-bar .close').on('click',function(){dialog.close(0)})
|
||||
|
||||
})
|
||||
}else{
|
||||
var html = sessionStorage.dialog
|
||||
jx.modal.show({html:html,id:'dialog'})
|
||||
dialog.status.ask()
|
||||
$('.dialog .action .active-button').on('click',pointer)
|
||||
$('.dialog .title-bar .close').on('click',function(){dialog.close(0)})
|
||||
|
||||
}
|
||||
}
|
||||
dialog.bind = function(pointer){
|
||||
if (pointer == null){
|
||||
pointer = dialog.close
|
||||
}
|
||||
$('.dialog .action .active-button').off()
|
||||
$('.dialog .action .active-button').on('click',pointer)
|
||||
}
|
||||
dialog.close = function(delay){
|
||||
delay = (delay == null)?1750:delay
|
||||
setTimeout(function(){
|
||||
if ( $('.dialog').length > 0){
|
||||
jx.modal.close()
|
||||
}
|
||||
},delay)
|
||||
}
|
||||
dialog.status = {}
|
||||
dialog.status.wait = function(){
|
||||
$('.dialog .action .active-button').hide()
|
||||
}
|
||||
dialog.status.confirm = function(){
|
||||
$('.dialog .action .active-button').show()
|
||||
}
|
||||
dialog.status.busy = function(){
|
||||
$('.dialog .message #msg-icon').removeClass()
|
||||
$('.dialog .message #msg-icon').addClass('fas fa-cog fa-4x fa-spin')
|
||||
|
||||
}
|
||||
dialog.status.fail = function(){
|
||||
$('.dialog .message #msg-icon').removeClass()
|
||||
$('.dialog .message #msg-icon').addClass('fas fa-times fa-4x')
|
||||
}
|
||||
dialog.status.ask = function(){
|
||||
$('.dialog .message #msg-icon').removeClass()
|
||||
$('.dialog .message #msg-icon').addClass('far fa-question-circle fa-4x')
|
||||
}
|
||||
dialog.status.warn = function(){
|
||||
$('.dialog .message #msg-icon').removeClass()
|
||||
$('.dialog .message #msg-icon').addClass('fas fa-exclamation-triangle fa-4x')
|
||||
}
|
||||
dialog.status.success = function(){
|
||||
$('.dialog .message #msg-icon').removeClass()
|
||||
$('.dialog .message #msg-icon').addClass('fas fa-check fa-4x')
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
if (!healthcare) {
|
||||
var healthcare = {io:{}}
|
||||
}
|
||||
healthcare.io = {'dialog':dialog,'confirmed':confirmed,'reset':reset,'update':update,'run':run,'publish':publish}
|
||||
healthcare.io.apply = function(){
|
||||
var value = $('.input-form .item .procs').val()
|
||||
var folder= $('.input-form .item .folder').val()
|
||||
$('.code .batch').html(value)
|
||||
var http = HttpClient.instance()
|
||||
http.setData({"batch":value,"resume":true,"folder":folder},"application/json")
|
||||
http.post(sessionStorage.io_context+'/io/params',function(x){})
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,182 @@
|
||||
/**
|
||||
* This file will depend on dialog.js (soft dependency). Some functions here will make calls to resources in dialog.js
|
||||
*/
|
||||
|
||||
var reset = function(){
|
||||
dialog.open('Healthcare/IO::Parser', 'Are you sure you would like to delete all data parsed? Click Ok to confirm',confirmed.reset)
|
||||
|
||||
}
|
||||
var update= function(){
|
||||
dialog.open('Healthcare/IO::Parser','Update will change parsing configuration. Would you like to continue ?',confirmed.update)
|
||||
}
|
||||
var run = function(){
|
||||
dialog.open('Healthcare/IO::Parser','Preparing parser, confirm to continue',confirmed.run)
|
||||
}
|
||||
var _queue = {socket:null}
|
||||
var confirmed = {}
|
||||
confirmed.run = function(){
|
||||
dialog.status.busy()
|
||||
dialog.status.wait()
|
||||
$('.dialog .message .text').html('Initiating Parsing ...')
|
||||
setTimeout(function(){
|
||||
var http = HttpClient.instance()
|
||||
http.post(sessionStorage.io_context+'/io/run',function(x){
|
||||
// dialog.handler = setInterval(function(){monitor.data()},750)
|
||||
monitor.data()
|
||||
//dialog.close()
|
||||
|
||||
})
|
||||
|
||||
},1000)
|
||||
}
|
||||
confirmed.reset = function(){
|
||||
var uri = sessionStorage.io_context+'/reset'
|
||||
var http= HttpClient.instance()
|
||||
dialog.status.busy()
|
||||
dialog.status.wait()
|
||||
http.post(uri,function(x){
|
||||
setTimeout(function(){
|
||||
if (x.status == 200 && x.responseText == "1"){
|
||||
dialog.status.success()
|
||||
$('.dialog .message .text').html('Reset Healthcare/IO::Parser was successful!<br><div align="center">Dialog will be closing</div>')
|
||||
dialog.close()
|
||||
}else{
|
||||
dialog.status.fail()
|
||||
|
||||
|
||||
}
|
||||
|
||||
},2000)
|
||||
})
|
||||
}
|
||||
|
||||
confirmed.update = function(){
|
||||
var uri = sessionStorage.io_context+'/update'
|
||||
var email = $('#email').val()
|
||||
//
|
||||
//-- validate the email
|
||||
if (email.match(/^([^\s]+)@([^\s@]+)\.(org|com|edu|io)$/i)){
|
||||
dialog.status.wait()
|
||||
dialog.status.busy()
|
||||
var http = HttpClient.instance()
|
||||
http.setData({"email":email},"application/son")
|
||||
setTimeout(function(){
|
||||
http.post(uri,function(x){
|
||||
if(x.status == 200 && x.responseText == "1"){
|
||||
dialog.status.success()
|
||||
}else{
|
||||
|
||||
dialog.status.fail()
|
||||
$('.dialog .message .text').html('Error code '+x.status)
|
||||
dialog.bind()
|
||||
dialog.status.confirm()
|
||||
$('.dialog .title-bar .title').html("Error found")
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
},1000)
|
||||
}else{
|
||||
dialog.status.fail()
|
||||
dialog.bind()
|
||||
$('.dialog .title-bar .title').text("Error found")
|
||||
$('.dialog .message .text').html('Invvalid Email entered')
|
||||
dialog.status.confirm()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This namespace is designed to export data to either the cloud or to a database
|
||||
*/
|
||||
var publish={set:{}}
|
||||
publish.post = function(){
|
||||
|
||||
if($('.jxmodal').length > 0){
|
||||
jx.modal.close()
|
||||
}
|
||||
dialog.open('Export/ETL','Please wait')
|
||||
dialog.status.busy()
|
||||
|
||||
var http = HttpClient.instance()
|
||||
http.setData(JSON.parse(sessionStorage.export),"application/json")
|
||||
http.post(sessionStorage.io_context+'/export',function(x){
|
||||
if (x.status != 200){
|
||||
setTimeout(function(){
|
||||
$('.dialog .message .text').html('An error occurred with code '+x.status)
|
||||
dialog.status.fail()
|
||||
dialog.status.wait()
|
||||
|
||||
},1500)
|
||||
|
||||
}
|
||||
//
|
||||
// @TODO: Have progress be monitored for this bad boy i.e open the connection to socket and read in ...
|
||||
//
|
||||
})
|
||||
|
||||
}
|
||||
publish.set.file = function(){
|
||||
var file = $('#file')[0].files[0]
|
||||
$('.file .name').html(file.name)
|
||||
var button = $('.cloud input').prop('disabled',true)
|
||||
var div = $('.cloud .file .fa-file-upload')[0]
|
||||
$(div).empty()
|
||||
$(div).addClass('fas fa-cog fa-spin')
|
||||
var reader = new FileReader()
|
||||
reader.readAsText(file)
|
||||
|
||||
|
||||
reader.onload = function(){
|
||||
_args = {"type":$('.cloud .id').html().trim(),"content":reader.result}
|
||||
// _args = JSON.stringify(_args)
|
||||
if (_args.content.match(/^\{.+/i) == null){
|
||||
content = _args.content.split('\n')[1].split(',')
|
||||
_args.content = {'bucket':'healthcareio','access_key':content[0].trim(),'secret_key':content[1].trim()}
|
||||
}
|
||||
sessionStorage.export = JSON.stringify(_args)
|
||||
}
|
||||
|
||||
reader.onloadend = function(){
|
||||
setTimeout(function(){
|
||||
var div = $('.cloud .file .fa-cog')[0]
|
||||
$(div).empty()
|
||||
$(div).addClass('fas fa-check')
|
||||
$(div).removeClass('fa-spin')
|
||||
// jx.modal.close()
|
||||
|
||||
//setTimeout(jx.modal.close,1500)
|
||||
publish.post()
|
||||
},2000)
|
||||
}
|
||||
}
|
||||
publish.database = {}
|
||||
|
||||
publish.database.init = function(id){
|
||||
//
|
||||
// we are expecting id in {mongo,couch,postgresql,mysql,sqlite}
|
||||
// @TODO: Account for cloud service brokers like dropbox, box, one-drive and google-drive
|
||||
sessionStorage.export = "{}"
|
||||
p = {'id':id}
|
||||
if (id.match(/(mongodb|postgresql|mysql|sqlite|couchdb)/i)){
|
||||
var hide_id = '.store .cloud'
|
||||
var show_id = '.store .database'
|
||||
}else{
|
||||
//
|
||||
// @TODO: generate an error message
|
||||
var show_id = '.store .cloud'
|
||||
var hide_id = '.store .database'
|
||||
|
||||
}
|
||||
var http = HttpClient.instance()
|
||||
http.get(sessionStorage.io_context+'/export',function(x){
|
||||
var html = x.responseText
|
||||
jx.modal.show({'html':html,'id':'dialog'})
|
||||
$(hide_id).hide(function(){
|
||||
$(show_id).show()
|
||||
})
|
||||
|
||||
$('.store .id').text(id)
|
||||
})
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<meta charset="utf8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<link rel="shortcut icon" href="{{context}}/static/img/logo.svg" type="image/icon type">
|
||||
<link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css">
|
||||
<script src="{{context}}/static/js/jx/rpc.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/jquery.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
||||
<link href="{{context}}/static/css/borders.css" type="text/css" rel="stylesheet">
|
||||
<link href="{{context}}/static/css/fa/css/all.css" type="text/css" rel="stylesheet">
|
||||
<script src="{{context}}/static/css/fa/js/all.js"></script>
|
||||
|
||||
<div class="border-round border menu-bar">
|
||||
<div class="menu">
|
||||
Admin
|
||||
<div class="menu-items border">
|
||||
<div class="item">Setup</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu">
|
||||
Claims & Remits
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,391 @@
|
||||
<style>
|
||||
.system {height:99%; overflow:hidden;}
|
||||
.data-info .board{ height:300px; display:grid; grid-template-columns:auto 200px 200px; gap:20px; align-items:center}
|
||||
/*.board { background-image: linear-gradient(to bottom, #ffffff,#ffffff,#f3f3f3,#d3d3d3d3)}*/
|
||||
.number {font-size:48px; font-family:courier;padding:8px; ;}
|
||||
.etl {display:grid; grid-template-columns: 250px auto; gap:2;}
|
||||
.chart {box-shadow : 0px 1px 4px 2px #d3d3d3; width:200px; height:250px;
|
||||
display:grid; align-items:center;
|
||||
background-image: linear-gradient(to bottom,#f3f3f3,#ffffff);
|
||||
overflow:hidden;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.dialog { width:450px; min-height:200px; display:grid; grid-template-rows: 40px 80% auto; gap:4px}
|
||||
.dialog .title-bar { border-top-left-radius: 8px; border-top-right-radius: 8px ; padding:4px; background-color:#f3f3f3; gap:2px; display:grid; grid-template-columns: auto 32px; align-items:center}
|
||||
.dialog .action {display:grid; align-items: flex-end; padding-left:25%; padding-right:25%;}
|
||||
.dialog .message {display:grid; align-items: center; grid-template-columns: 20% auto;}
|
||||
.dialog .message .text {line-height:2; text-transform: capitalize;}
|
||||
.fa-exclamation-triangle {color:orange}
|
||||
.fa-question-circle{color:#009df7}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>
|
||||
<script src="{{context}}/static/js/io/dialog.js"></script>
|
||||
<script src="{{context}}/static/js/io/io.js"></script>
|
||||
<script src="{{context}}/static/js/io/healthcare.js"></script>
|
||||
|
||||
<script>
|
||||
var select = function(node){
|
||||
var value = $($(node).children()[0]).attr('data-value')
|
||||
|
||||
jx.utils.patterns.visitor($('.tab'),function(_item){
|
||||
var button = $(_item).children()[0]
|
||||
$(_item).removeClass('selected')
|
||||
//alert([$(button).attr('data-value'),value])
|
||||
if($(button).attr('data-value') == value){
|
||||
$(node).addClass('selected')
|
||||
$('.'+value).show()
|
||||
|
||||
}else{
|
||||
var m = '.'+ $(button).attr('data-value')
|
||||
|
||||
$(m).hide()
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
var monitor = {}
|
||||
monitor.listen = {handler:null}
|
||||
|
||||
monitor.data = function(){
|
||||
var http = HttpClient.instance()
|
||||
http.get("/data",function(x){
|
||||
var r = JSON.parse(x.responseText)
|
||||
var keys = jx.utils.keys(r) //-- process,files
|
||||
for (var i in keys){
|
||||
var prefix = keys[i]
|
||||
if(prefix == 'process'){
|
||||
if(r[prefix].counts != 0){
|
||||
//
|
||||
// We should insure the listeners are enabled
|
||||
if(monitor.listen.handler == null){
|
||||
monitor.listen.handler = setInterval(
|
||||
function(){
|
||||
console.log('running ...')
|
||||
monitor.data()},5000)
|
||||
|
||||
}
|
||||
}else{
|
||||
if (monitor.listen.handler != null){
|
||||
|
||||
clearInterval(monitor.listen.handler)
|
||||
}
|
||||
dialog.close()
|
||||
}
|
||||
}
|
||||
monitor.render(prefix,r[prefix])
|
||||
}
|
||||
})
|
||||
}
|
||||
monitor.render = function(prefix,r){
|
||||
prefix = '.'+prefix
|
||||
|
||||
var div = jx.dom.get.instance('DIV')
|
||||
var label = jx.dom.get.instance('DIV')
|
||||
div.align = 'center'
|
||||
|
||||
div.innerHTML = r.counts
|
||||
div.className = 'number'
|
||||
label.innerHTML = prefix.replace(/\./,'')
|
||||
label.style.textTransform = 'capitalize'
|
||||
label.className = 'small bold border-top'
|
||||
div.append(label)
|
||||
|
||||
|
||||
$(prefix + ' .board').empty()
|
||||
$(prefix+' .board').append(div)
|
||||
|
||||
var charts = jx.utils.patterns.visitor(r.chart,function(option){
|
||||
|
||||
|
||||
var div = jx.dom.get.instance('div')
|
||||
|
||||
div.className = 'chart'
|
||||
div.align='center'
|
||||
$(prefix+' .board').append(div)
|
||||
|
||||
var chart = new ApexCharts($(div)[0],option)
|
||||
//chart.render()
|
||||
div.chart = chart
|
||||
|
||||
return chart
|
||||
|
||||
})
|
||||
var observers = jx.utils.patterns.visitor(charts,function(_item){
|
||||
var m = function(_chart){
|
||||
this.chart = _chart ;
|
||||
this.apply = function(caller){this.chart.render();
|
||||
caller.notify()
|
||||
}
|
||||
}
|
||||
return new m(_item)
|
||||
})
|
||||
jx.utils.patterns.observer(observers,'apply')
|
||||
//jx.utils.patterns.iterator(charts,'render')
|
||||
|
||||
/*setTimeout(function(){
|
||||
jx.utils.patterns.visitor(charts,function(_item){_item.render()})
|
||||
},1000) */
|
||||
}
|
||||
var setup = {}
|
||||
setup.open = function(){
|
||||
$('.dashboard').slideUp(
|
||||
function(){
|
||||
$('.setup').slideDown()
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
/*var shandler = new io();
|
||||
|
||||
if (shandler.disconnected ==false){
|
||||
shandler.disconnect()
|
||||
}
|
||||
var socket = io.connect()
|
||||
socket.on('connect',function(e){
|
||||
socket.emit('connect',{name:'steve'})
|
||||
})
|
||||
socket.on('update',function(e){
|
||||
console.log(e)
|
||||
console.log()
|
||||
})
|
||||
var socket = io.connect('http://localhost:81',{cors:{AccessControlAllowOrigin:'*'}}) //('http://localhost:81/stream')
|
||||
socket.on('procs',function(e){
|
||||
|
||||
})
|
||||
socket.on('data',function(e){
|
||||
|
||||
$('.logs').empty()
|
||||
var div = $('.logs')
|
||||
|
||||
var option = e.apex
|
||||
option.plotOptions.pie.size = 220
|
||||
option.plotOptions.pie = {dataLabels: {show:true,name:{show:true},value:{show:true}}}
|
||||
|
||||
option.legend.show = false
|
||||
console.log(option)
|
||||
c = new ApexCharts(div[0],option)
|
||||
c.render()
|
||||
socket.emit("procs",{"name":"steve"})
|
||||
})*/
|
||||
|
||||
select($('.tab')[0])
|
||||
monitor.data()
|
||||
|
||||
$('.email').text($('#email').val())
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<div class="system setup">
|
||||
<div class="status">
|
||||
|
||||
{% if not store.type %}
|
||||
<div >
|
||||
<span class="caption bold border-bottom" style="padding-right:10">Current Configuration</span>
|
||||
<p></p>
|
||||
<div style="display:grid; align-items:center; grid-template-columns:32px auto;">
|
||||
<i class="fa fa-times" style="font-size:28; margin:4px;"></i> <span>System needs to be initialized !</span>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="active-button border-round" style="width:50%">
|
||||
<div class="icon">
|
||||
<i class="fas fa-cog" style="font-size:28"></i>
|
||||
</div>
|
||||
<div class="bold" align="center">Initialize</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="border-right">
|
||||
|
||||
<span class="caption bold border-bottom" style="padding-right:10">Current Configuration</span>
|
||||
<p>
|
||||
</p>
|
||||
<div class="item" style="display:grid; align-items:center">
|
||||
<div class="bold" style="text-transform: capitalize;">Owner </div><div class="bold">:</div>
|
||||
<div>
|
||||
|
||||
<input type="text" id="email" value="{{owner}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="bold" style="text-transform: capitalize;">store</div><div class="bold">:</div>
|
||||
<div class="">{{store.type}}</div>
|
||||
</div>
|
||||
<p></p>
|
||||
<div class="active-button border-round" style="width:50%" onclick="healthcare.io.update()">
|
||||
<div class="icon">
|
||||
<I class="fas fa-download" style="font-size:28"></I>
|
||||
</div>
|
||||
<div class="bold" align="center">Update Config</div>
|
||||
</div>
|
||||
<p>
|
||||
<div class="code">
|
||||
#<br>
|
||||
healthcare-io.py --init <span class="email"></span> --store mongo
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{%endif%}
|
||||
<p></p>
|
||||
<br>
|
||||
<div class="border-right">
|
||||
<span class="caption bold border-bottom" style="padding-right:10">Manage Plan</span>
|
||||
<p></p>
|
||||
<div style="line-height: 2;">Insure your account is tied to a cloud service provider.
|
||||
<br>We support <span class="bold">google-drive, dropbox, one-drive or box. </span>
|
||||
</div>
|
||||
<p>
|
||||
<div class="bold active-button border-round" style="width:50%" onclick="jx.modal.show({url:'https://healthcareio.the-phi.com/store/healthcareio/plans'})">
|
||||
<div>
|
||||
<img src="{{context}}/static/img/logo.svg" />
|
||||
</div>
|
||||
<div align="center">Open Plan Console</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<br>
|
||||
<div class="border-right" style="height:30%"></div>
|
||||
</div>
|
||||
<div class="_border-right"></div>
|
||||
<div >
|
||||
<span class="caption bold border-bottom" style="padding-right:10">
|
||||
Manage Processes</span>
|
||||
<p>
|
||||
<div class="input-form" style="grid-template-columns: 30% auto;">
|
||||
<div class="item" style="grid-row:1; grid-column:1; ">
|
||||
<div class="label">Process #</div>
|
||||
<input type="text" class="procs batch"placeholder="#" style="width:64px; text-align:right" value="{{args.batch}}" onchange="healthcare.io.apply()"/>
|
||||
</div>
|
||||
<div class="item" style="grid-row:1; grid-column:2 ">
|
||||
<div class="label">Folder #</div>
|
||||
<input type="text" placeholder="Process counts" value="/data"/ class="data folder" disabled>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="display:grid; grid-template-columns:repeat(2,215px); gap:2px;">
|
||||
<div class="active-button border-round bold io-apply" style="margin-top:32; display:none" onclick="healthcare.io.apply()">
|
||||
<div class="icon"><i class="far fa-save" style="font-size:28; color:#4682B4"></i></div>
|
||||
<div align="center">Apply</div>
|
||||
</div>
|
||||
<div class="active-button border-round bold" style="margin-top:32" onclick="healthcare.io.stop()">
|
||||
<i class="far fa-stop-circle" style="font-size:28; color:maroon"></i>
|
||||
|
||||
<div align="center">Stop</div>
|
||||
</div>
|
||||
<div class="active-button border-round bold" style="margin-top:32" onclick="healthcare.io.run()">
|
||||
<i class="fas fa-running" style="font-size:28; color:green"></i>
|
||||
<div align="center">Run</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<div class="code">
|
||||
<div class="bold"># The command that will be executed</div>
|
||||
<div>healthcare-io.py --parse --folder /data --batch <span class="batch">{{args.batch}}</span></div>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
<div style="display:grid; grid-template-columns:auto 48px ; gap:2px">
|
||||
<div class="bold caption border-bottom">Process Monitoring</div>
|
||||
<div class="active" align="center" title="reload" onclick="monitor.data()"><i class="fas fa-sync"></i></div>
|
||||
</div>
|
||||
<div class="small">Powered by smart-top</div>
|
||||
<p></p>
|
||||
<div class="tabs">
|
||||
<div class="tab selected" onclick="select(this)">
|
||||
<div class="active" data-value="process" >
|
||||
<i class="far fa-clock" style="color:maroon"></i>
|
||||
<span>Process</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab" onclick="select(this)">
|
||||
<div class="active" data-value="files"><i class="fas fa-file-alt"></i> Files</div>
|
||||
</div>
|
||||
<div class="tab" onclick="select(this)">
|
||||
<div class="active" data-value="export"><i class="fas fa-upload"></i> Export</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="data-info">
|
||||
<div class="process ">
|
||||
|
||||
<div class="board"></div>
|
||||
<div class="small" align="center">
|
||||
<div class="border-top bold" style="color:#4682B4;">Running Processes and resource usage</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="files">
|
||||
<div class="board"></div>
|
||||
<div class="small" align="center">
|
||||
<div class="border-top bold" style="color:#4682B4;">Summary of files found and processed</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="export">
|
||||
<p></p>
|
||||
<div class="etl">
|
||||
<div class="" >
|
||||
<div class="menu" style="position:absolute; width:200">
|
||||
<div class="items ">
|
||||
<div class="bold active" style="display:grid; grid-template-columns:80% auto;">
|
||||
<span>
|
||||
<i class="fas fa-cloud"></i>
|
||||
Cloud</span>
|
||||
<span class="glyph">
|
||||
<i class="fas fa-angle-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="item-group border-round border small">
|
||||
<div class="item" onclick="healthcare.io.publish.database.init('s3')">AWS S3</div>
|
||||
<div class="item" onclick="healthcare.io.publish.database.init('bigquery')">Google Bigquery</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="items ">
|
||||
<div class="bold active"style="display:grid; grid-template-columns:80% auto;">
|
||||
<span>
|
||||
<i class="fas fa-database"></i>
|
||||
Database</span>
|
||||
<span class="glyph">
|
||||
<i class="fas fa-angle-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="item-group border-round border small">
|
||||
<div class="bold">SQL</div>
|
||||
|
||||
<div class="item" style="margin-left:15px; margin-right:32px" onclick="healthcare.io.publish.database.init('postgresql')">PostgreSQL</div>
|
||||
<div class="item" style="margin-left:15px; margin-right:32px" onclick="healthcare.io.publish.database.init('mysql')">MySQL</div>
|
||||
|
||||
|
||||
|
||||
<div class="bold">NoSQL</div>
|
||||
<div class="item" style="margin-left:15px; margin-right:32px" onclick="healthcare.io.publish.database.init('mongodb')">Mongodb</div>
|
||||
<div class="item" style="margin-left:15px; margin-right:32px" onclick="healthcare.io.publish.database.init('couchdb')">Couchdb</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="active-button border-round" style="width:50%">
|
||||
<div class="icon"><i class="fas fa-running" style="font-size:28"></i></div> <div class="bold" align="center">Start</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,78 @@
|
||||
<link href="{{context}}/static/css/fa/css/all.css" type="text/css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{context}}/static/css/default.css" type="text/css">
|
||||
<link href="{{context}}/static/css/borders.css" type="text/css" rel="stylesheet">
|
||||
<style>
|
||||
.form , input{font-family:sans-serif; font-size:18px}
|
||||
.form .small {font-size:14px; line-height:2}
|
||||
.mongo, .couchdb{
|
||||
|
||||
display:grid;
|
||||
gap:2px;
|
||||
}
|
||||
.grid-full {display:grid; grid-template-columns: 100%;}
|
||||
.grid-split-half {display:grid; grid-template-columns: 50% 50%; gap:2px;}
|
||||
.store .title-bar {display:grid; align-items:center; grid-template-columns: auto 32px; padding:8px;}
|
||||
.file {display:grid; align-items:center; grid-template-columns: 40% auto; gap:2px; font-family:sans-serif; padding:8px}
|
||||
.file input {display:none}
|
||||
.file label {padding:8px;}
|
||||
|
||||
|
||||
</style>
|
||||
<div class="store">
|
||||
|
||||
<div class="title-bar">
|
||||
<div class="caption">Export Module <span class="bold id"></span></div>
|
||||
<div class="active" align="center" onclick="jx.modal.close()">
|
||||
<i class="fas fa-times"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p></p>
|
||||
|
||||
<div class="cloud form border-round">
|
||||
<div class="small border-round">
|
||||
Please provide select access-key file or service account key file to perform ETL
|
||||
to <span class="bold id"></span>. <br>The files will be re-created in JSON format
|
||||
</div>
|
||||
<div class="file">
|
||||
<label class="active-button border-round">
|
||||
<span style="font-family:sans-serif">
|
||||
<i class="icon fas fa-file-upload" style="font-size:24px"></i>
|
||||
</span>
|
||||
<div>Select key file</div>
|
||||
<input type="file" id="file" aria-label="File browser example" onchange="publish.set.file()">
|
||||
|
||||
</label>
|
||||
<div class="name small black bold" style="padding-left:24px"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="database form border-round">
|
||||
<div class="small border-round">
|
||||
Tables / collections will be automatically inferred in the <span class="bold id"></span>
|
||||
</div>
|
||||
<div class="mongo">
|
||||
|
||||
<div class="grid-split-half">
|
||||
<input type="text" class="host" placeholder="host:port"/>
|
||||
<input type="text" class="host" placeholder="database"/>
|
||||
</div>
|
||||
|
||||
<div class="grid-split-half">
|
||||
<input type="text" placeholder="user"/>
|
||||
|
||||
<input type="text" placeholder="password"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<p></p>
|
||||
<div>
|
||||
<br>
|
||||
<div class="active-button border-round" style="margin-left:30%; margin-right:30%">
|
||||
<div class="icon">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
<div class="bold" align="center">Export Now</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in new issue