analytics.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. import pandas as pd
  2. import numpy as np
  3. import os
  4. import io
  5. import json
  6. from multiprocessing import Process
  7. import transport
  8. import sqlite3 as lite
  9. import numpy as np
  10. import transport
  11. import matplotlib.pyplot as plt
  12. import re, base64
  13. # from weasyprint import HTML, CSS
  14. COLORS = ["#fbd1a2","#00b2ca","#1d4e89","#4682B4","#c5c3c6","#4c5c68","#1985a1","#f72585","#7209b7","#3a0ca3","#4361ee","#4cc9f0","#ff595e","#ffca3a","#8ac926","#1982c4","#6a4c93"]
  15. class stdev :
  16. def __init__(self) :
  17. self.values = []
  18. def step(self,value):
  19. if value : #and type in [np.int64, np.int32,np.float64,np.float32, int]:
  20. self.values.append(value)
  21. def finalize(self):
  22. return np.std(self.values) if self.values else None
  23. # conn = lite.connect("/home/steve/healthcare-io/healthcare-io.db3")
  24. # conn.create_aggregate("stdev",1,stdev)
  25. # df = pd.read_sql("select count(distinct (json_extract(data,'$.patient_id'))) as patient_count, avg(json_array_length(data,'$.procedures')) mean, stdev(json_array_length(data,'$.procedures')) stdev from claims",conn)
  26. ROOT_FOLDER = 'stats'
  27. # plt.gcf().subplots_adjust(bottom=0.15)
  28. # from matplotlib import rcParams
  29. # rcParams.update({'figure.autolayout': True})
  30. class Chart :
  31. @staticmethod
  32. def remove_borders(axes,wedges,labels,item) :
  33. # plt.axes()
  34. axes.spines["top"].set_visible(False)
  35. # plt.axes().
  36. axes.spines["right"].set_visible(False)
  37. axes.legend(wedges, labels #,title=item['label']
  38. ,loc="upper right",fontsize=12,bbox_to_anchor=(1, 0, 0.5, 1),fancybox=True,framealpha=0.2)
  39. # plt.axes().
  40. # axes.spines["left"].set_visible(False)
  41. if 'axis' in item['chart'] :
  42. axes.set_ylabel(item['chart']['axis']['y'])
  43. axes.set_xlabel(item['chart']['axis']['x'])
  44. @staticmethod
  45. def donut(item,**args) :
  46. df = item['data']
  47. x = item['chart']['x'] #args['x']
  48. labels = item['chart']['y']
  49. labels = df[labels]
  50. # figure = plt.figure()
  51. figure, axes = plt. subplots()
  52. # wedges, texts = plt.pie(df[x],labels=labels)
  53. colors = COLORS[:len(labels)] #np.random.choice(COLORS,len(labels),replace=False)
  54. wedges = axes.pie(df[x],labels=labels,wedgeprops=dict(width=0.3),colors=colors,autopct=lambda pct: "{:.2f}%\n({:.0f})".format(pct,int((pct/100)*df[x].sum() ))) #,autopct=lambda pct: func(pct, df[x].values))
  55. # my_circle=plt.Circle( (0,0), 0.7, color='#ffffff',fill=True)
  56. # p=plt.gcf()
  57. # p.gca().add_artist(my_circle)
  58. # plt.legend(wedges, labels,title=item['label'],loc="upper right",bbox_to_anchor=(1, 0, 0.5, 1))
  59. # axes.legend(wedges[0], labels,title=item['label'],loc="upper right",bbox_to_anchor=(1, 0, 0.5, 1),framealpha=0,edgecolor='#CAD5E0',
  60. # )
  61. # x = plt.show()
  62. Chart.remove_borders(axes,wedges[0],labels,item)
  63. plt.close()
  64. return figure
  65. @staticmethod
  66. def barh(item,**args):
  67. """
  68. This function will return/render a bar chart (horizontal) which is conducive to showing distributions of things like diagnosis codes
  69. """
  70. # figure = plt.figure()
  71. figure, axes = plt. subplots()
  72. y_labels = item['chart']['y'][0]
  73. x_labels = item['chart']['x'] #[args['x']] if type(args['x']) == str else args['x']
  74. df = item['data'].iloc[:9].copy()
  75. # odf = item['data'].iloc[9:].copy().mean().to_frame().T
  76. # odf[y_labels] = 'Other'
  77. # df = df.append(odf)
  78. wedges = []
  79. # COLORS = ['#003f5c','#7a5195','#374c80','#bc5090','#ef5675','#ff764a','#ffa600']
  80. for x_ in x_labels:
  81. index = x_labels.index(x_)
  82. color = COLORS[index]
  83. w = axes.barh(df[y_labels],df[x_],align='edge',label='counts' ,color=color)
  84. wedges += [w]
  85. # labels = [name.replace('_',' ') for name in x_labels]
  86. # axes.legend(wedges,[name.replace('_',' ') for name in x_labels],
  87. # title=item['label'],
  88. # framealpha=0,
  89. # edgecolor='#CAD5E0',
  90. # loc="upper right",bbox_to_anchor=(1, 0, 0.5, 1)
  91. # )
  92. Chart.remove_borders(axes,wedges,[name.replace('_',' ') for name in x_labels],item)
  93. plt.close()
  94. return figure
  95. @staticmethod
  96. def spline(item,**args):
  97. """
  98. """
  99. df = item['data']
  100. # figure = plt.figure()
  101. figure, axes = plt. subplots()
  102. wedges = []
  103. item['chart']['x'] = [item['chart']['x']]if type(item['chart']['x']) == str else item['chart']['x']
  104. # COLORS = ['#003f5c','#7a5195','#374c80','#bc5090','#ef5675','#ff764a','#ffa600']
  105. for xl in item['chart']['x'] :
  106. x = df[xl]
  107. index = 0
  108. for yl in item['chart']['y'] :
  109. y = df[yl]
  110. color = COLORS[index]
  111. if 'scatter' in args :
  112. w = plt.plot(x,y,'o',color=color)
  113. else:
  114. w = plt.plot(x,y,color=color,marker='o')
  115. wedges += w
  116. index += 1
  117. # print (item['chart']['x'])
  118. # if 'axis' in item :
  119. # axes.set_ylabel(item['axis']['y'])
  120. # axes.set_xlabel(item['axis']['x'])
  121. # plt.title(item['label'])
  122. # axes.legend(wedges,[name.replace('_',' ') for name in item['chart']['y']],
  123. # title=item['label'],
  124. # framealpha=0,
  125. # edgecolor='#CAD5E0',
  126. # loc="upper right",bbox_to_anchor=(1, 0, 0.5, 1)
  127. # )
  128. axes.grid(b=False,which='major',axis='x')
  129. Chart.remove_borders(axes,wedges,[name.replace('_',' ') for name in item['chart']['y']],item)
  130. plt.close()
  131. return figure
  132. @staticmethod
  133. def scatter(item,**args):
  134. return Chart.spline(item,scatter=True)
  135. class Apex :
  136. """
  137. This class will format a data-frame to work with Apex charting engine
  138. """
  139. @staticmethod
  140. def apply(item,theme={'mode':'light','palette':'palette6'}):
  141. pointer = item['chart']['type']
  142. if hasattr(Apex,pointer) :
  143. pointer = getattr(Apex,pointer)
  144. options = pointer(item)
  145. if 'apex' in options and 'colors' in options['apex'] :
  146. del options['apex']['colors']
  147. if 'apex' in options :
  148. options['apex']['theme'] = theme
  149. options['responsive']= [
  150. {
  151. 'breakpoint': 1,
  152. 'options': {
  153. 'plotOptions':item['plotOptions'] if 'plotOptions' in item else None,
  154. }
  155. }
  156. ]
  157. return options
  158. else:
  159. print ("Oops")
  160. pass
  161. @staticmethod
  162. def radial(item):
  163. df = item['data']
  164. x = item['chart']['axis']['x']
  165. y = item['chart']['axis']['y']
  166. labels = df[y].tolist()
  167. values = [float(np.round(value,2)) for value in df[x].tolist()]
  168. chart = {"type":"radialBar","height":200}
  169. option = {"chart":chart,"series":values,"labels":labels,"plotOptions":{"radialBar":{"hollow":{"size":"70%"}}}}
  170. return {'apex':option}
  171. @staticmethod
  172. def scatter(item):
  173. options = Apex.spline(item)
  174. options['apex']['chart']['type'] = 'scatter'
  175. return options
  176. @staticmethod
  177. def scalar(item):
  178. _df = item['data']
  179. name = _df.columns.tolist()[0]
  180. value = _df[name].values.round(2)[0]
  181. html = '<div class="scalar"><div class="value">:value</div><div class="label">:label</div></div>'
  182. if value > 999 and value < 1000000 :
  183. value = " ".join([str(np.divide(value,1000).round(2)),"K"])
  184. elif value > 999999 :
  185. #@ Think of considering the case of a billion ...
  186. value = " ".join([str(np.divide(value,1000000).round(2)),"M"])
  187. else:
  188. value = str(value)
  189. unit = name.replace('_',' ') if 'unit' not in item else item['unit']
  190. return {'html':html.replace(':value',value).replace(":label",unit)}
  191. @staticmethod
  192. def column(item):
  193. df = item['data']
  194. N = df.shape[0] if df.shape[0] < 10 else 10
  195. axis = item['chart']['axis']
  196. x = axis['x']
  197. if type(x) == list :
  198. x = x[0]
  199. axis['y'] = [axis['y']] if type(axis['y']) != list else axis['y']
  200. series = []
  201. for y in axis['y'] :
  202. series += [{"data": df[y].values.tolist()[:N],"name":y.upper().replace('_',' ')}]
  203. xtitle,ytitle = Apex.get_labels(item)
  204. options = {"chart":{"type":"bar"},"plotOptions":{"bar":{"horizontal":False,"width:":2,"color":["transparent"]}},"dataLabels":{"enabled":False},"legend":{"position":"right"}}
  205. options['xaxis'] = {"categories":df[x].values.tolist()[:N],"title":xtitle['title']}
  206. options['yaxis'] = ytitle
  207. options['series'] = series
  208. options['colors'] = COLORS[:df[x].size]
  209. return {"apex":options}
  210. # options = Apex.barh(item)
  211. # options['chart']['type'] = 'column'
  212. # options['plotOptions']['bar'] = {'horizontal':False,'columnWidth':'55%'}
  213. # options['stroke']={'show':True,'width':2,'colors':['transparent']}
  214. # return {"apex":options}
  215. @staticmethod
  216. def get_labels(item):
  217. xtitle = ytitle = ""
  218. if "labels" not in item['chart'] :
  219. xtitle = item['chart']['axis']['x']
  220. ytitle = item['chart']['axis']['y']
  221. else:
  222. xtitle = item['chart']['labels']['x']
  223. ytitle = item['chart']['labels']['y']
  224. xtitle = xtitle if type(xtitle) != list else xtitle[0]
  225. ytitle = ytitle if type(ytitle) != list else ytitle[0]
  226. return {"title":{"text":xtitle.lower().replace('_',' '),"style":{"fontWeight":"lighter"}}},{"title":{"text":ytitle.lower().replace('_',' '),"style":{"fontWeight":"lighter"}}}
  227. @staticmethod
  228. def bar(item):
  229. return Apex.barh(item)
  230. @staticmethod
  231. def barh(item):
  232. """
  233. rendering a horizontal bar chart assuming for now that only one series is involved
  234. @TODO: alias this with bar (!= column)
  235. """
  236. df = item['data']
  237. N = df.shape[0] if df.shape[0] < 10 else 10
  238. axis = item['chart']['axis']
  239. y = axis['y']
  240. if type(y) == list :
  241. y = y[0]
  242. axis['x'] = [axis['x']] if type(axis['x']) != list else axis['x']
  243. # if not set(axis['x']) & set(df.columns.tolist()) :
  244. # print (set(axis['x']) & set(df.columns.tolist()))
  245. # print (axis['x'])
  246. # print (df.columns)
  247. # df.columns = axis['x']
  248. series = []
  249. _min=_max = 0
  250. for x in axis['x'] :
  251. series += [{"data": df[x].values.tolist()[:N],"name":x.upper().replace('_',' ')}]
  252. _min = df[x].min() if df[x].min() < _min else _min
  253. _max = df[x].max() if df[x].max() > _max else _max
  254. xtitle , ytitle = Apex.get_labels(item)
  255. options = {"chart":{"type":"bar"},"plotOptions":{"bar":{"horizontal":True}},"dataLabels":{"enabled":False},"legend":{"position":"right"}}
  256. options['xaxis'] = {"categories":df[y].values.tolist()[:N],"title":xtitle['title']}
  257. options['yaxis'] = ytitle
  258. options['series'] = series
  259. options['colors'] = COLORS[:df[x].size]
  260. return {"apex":options}
  261. @staticmethod
  262. def spline(item):
  263. series = []
  264. df = item['data']
  265. N = df.shape[0] if df.shape[0] < 10 else 10
  266. axis = item['chart']['axis']
  267. x = axis['x']
  268. _min=_max = 0
  269. for y in axis['y'] :
  270. series += [{"data":df[y].values[:N].tolist(),"name":y.upper().replace('_',' ')}]
  271. _min = df[y].min() if df[y].min() < _min else _min
  272. _max = df[y].max() if df[y].max() > _max else _max
  273. colors = COLORS[:len(axis['y'])]
  274. options = {"chart":{"type":"line"},"series":series,"stroke":{"curve":"smooth"},"colors":colors,"legend":{"position":"right"}}
  275. xtitle , ytitle = Apex.get_labels(item)
  276. options['xaxis'] = {"categories":df[x].values[:N].tolist(),"title":xtitle['title']}
  277. options['yaxis'] = ytitle
  278. return {"apex":options}
  279. @staticmethod
  280. def donut(item):
  281. """
  282. :pre data must have more than one item otherwise just make it a scalar
  283. here we will use the key as labels and the values as the values (obviously)
  284. labels are y-axis
  285. values are x-axis
  286. """
  287. df = item['data']
  288. if df.shape [0]> 1 :
  289. y_cols,x_cols = item['chart']['axis']['y'],item['chart']['axis']['x']
  290. labels = df[y_cols].values.tolist()
  291. values = df[x_cols].values.round(2).tolist()
  292. else:
  293. labels = [name.upper().replace('_',' ') for name in df.columns.tolist()]
  294. df = df.astype(float)
  295. values = df.values.round(2).tolist()[0] if df.shape[1] > 1 else df.values.round(2).tolist()
  296. colors = COLORS[:len(values)]
  297. options = {"series":values,"colors":colors,"labels":labels,"dataLabels":{"enabled":True,"style":{"colors":["#000000"]},"dropShadow":{"enabled":False}},"chart":{"type":"donut","width":200},"plotOptions":{"pie":{"customScale":.9}},"legend":{"position":"right"}}
  298. return {"apex":options}
  299. pass
  300. class engine :
  301. """
  302. This engine is designed to load the configuration and run the queries given they are remittance or claims
  303. @TODO:
  304. - make sure the readers of the queries are configurable i.e use data-transport
  305. """
  306. def __init__(self,path) :
  307. """
  308. Loading configuration file from a designated location ...
  309. """
  310. f = open(path) ;
  311. _config = json.loads(f.read())
  312. self.store_config = _config['store']
  313. self.info = _config['analytics']
  314. _args = self.store_config
  315. if self.store_config['type'] == 'mongo.MongoWriter' :
  316. _args['type'] = 'mongo.MongoReader'
  317. else:
  318. _args['type'] = 'disk.SQLiteReader'
  319. self.store_config = _args ;
  320. def filter (self,**args):
  321. """
  322. type: claims or remits
  323. filter optional identifier claims, procedures, taxonomy, ...
  324. """
  325. _m = {'claim':'837','claims':'837','remits':'835','remit':'835'}
  326. table = _m[ args['type']]
  327. _analytics = self.info[table]
  328. if 'index' in args :
  329. index = int(args['index'])
  330. _analytics = [_analytics[index]]
  331. _info = list(_analytics) #if 'filter' not in args else [item for item in analytics if args['filter'] == item['id']]
  332. # conn = lite.connect(self.store_config['args']['path'],isolation_level=None)
  333. # conn.create_aggregate("stdev",1,stdev)
  334. DB_TYPE = 'mongo' if (type(self.reader) == transport.mongo.MongoReader) else 'sql'
  335. if DB_TYPE == 'mongo' :
  336. self.store_config['args']['doc'] = args['type']
  337. self.reader = transport.factory.instance(**self.store_config)
  338. r = []
  339. for row in _info :
  340. pipeline = row['pipeline']
  341. index = 0
  342. for item in pipeline:
  343. if not item[DB_TYPE] :
  344. continue
  345. query = {DB_TYPE:item[DB_TYPE]}
  346. df = pd.DataFrame(self.reader.read(**query)) #item)
  347. df = df.fillna('N/A')
  348. # item['data'] = df
  349. chart = item['chart']
  350. pipe = {"data":df,"chart":chart}
  351. for key in list(item.keys()) :
  352. if key not in ["chart","data","mongo","sql","couch"] :
  353. pipe[key] = item[key]
  354. r.append(pipe)
  355. self.reader.close()
  356. return {"id":_info[0]['id'],'pipeline':r}
  357. def apply (self,**args) :
  358. """
  359. type: claims or remits
  360. filter optional identifier claims, procedures, taxonomy, ...
  361. """
  362. _m = {'claim':'837','claims':'837','remits':'835','remit':'835'}
  363. # key = '837' if args['type'] == 'claims' else '835'
  364. table = _m[ args['type']]
  365. _analytics = self.info[table]
  366. if 'index' in args :
  367. index = int(args['index'])
  368. _analytics = [_analytics[index]]
  369. _info = list(_analytics) if 'filter' not in args else [item for item in analytics if args['filter'] == item['id']]
  370. # conn = lite.connect(self.store_config['args']['path'],isolation_level=None)
  371. # conn.create_aggregate("stdev",1,stdev)
  372. #
  373. # @TODO: Find a better way to handle database variance
  374. #
  375. # DB_TYPE = 'mongo' if (type(self.reader) == transport.mongo.MongoReader) else 'sql'
  376. if 'mongo' in self.store_config['type'] :
  377. DB_TYPE='mongo'
  378. else:
  379. DB_TYPE='sql'
  380. self.store_config['args']['table'] = args['type']
  381. self.reader = transport.factory.instance(**self.store_config)
  382. r = []
  383. for row in _info :
  384. pipeline = row['pipeline']
  385. index = 0
  386. for item in pipeline:
  387. # item['data'] = pd.read_sql(item['sql'],conn)
  388. # query = {DB_TYPE:item[DB_TYPE]}
  389. query = item[DB_TYPE]
  390. if not query :
  391. continue
  392. if DB_TYPE == 'sql' :
  393. query = {"sql":query}
  394. item['data'] = self.reader.read(**query) #item)
  395. if 'serialize' in args :
  396. # item['data'] = json.dumps(item['data'].to_dict(orient='record')) if type(item['data']) == pd.DataFrame else item['data']
  397. item['data'] = json.dumps(item['data'].to_dict('record')) if type(item['data']) == pd.DataFrame else item['data']
  398. else:
  399. item['data'] = (pd.DataFrame(item['data']))
  400. pipeline[index] = item
  401. index += 1
  402. #
  403. #
  404. row['pipeline']= pipeline
  405. # if 'info' in item:
  406. # item['info'] = item['info'].replace(":rows",str(item["data"].shape[0]))
  407. # conn.close()
  408. self.reader.close()
  409. return _info
  410. def _html(self,item) :
  411. figure = None
  412. df = item['data']
  413. label = ['<div class="label">',item['label'],'</div>']
  414. text = ['<div class="grid">',df.describe().iloc[:].round(2).to_html().replace('_',' '),'</div>']
  415. info = ['<div class="info">',item['info'],'</div>'] if 'info' in item else []
  416. if item['chart']['type'] in ['pie','donut','doughnut'] :
  417. figure = Chart.donut(item)
  418. text = ['<div class="grid">',df.to_html(index=False).replace('_',' '),'</div>']
  419. elif item['chart']['type'] == 'scatter' :
  420. figure = Chart.scatter(item)
  421. elif item['chart']['type'] == 'spline' :
  422. figure = Chart.spline(item)
  423. elif item['chart']['type'] in ['barh','hbar'] :
  424. figure = Chart.barh(item)
  425. elif item['chart']['type'] == 'scalar' :
  426. figure = (item['data'].apply(lambda col: '<div class="scalar"><div class="value bold">'+str(col.values[0].round(2))+'</div><div class="value-text">'+col.name.replace('_', ' ')+'</div></div>' ).tolist())
  427. label = text = []
  428. pass
  429. if figure and item['chart']['type'] != 'scalar':
  430. stream = io.BytesIO()
  431. figure.savefig(stream,format='png',dpi=300,quality=95, bbox_inches = "tight",transparent=True)
  432. stream.seek(0)
  433. stream = base64.b64encode(stream.getvalue()).decode("utf-8")
  434. stream = "data:image/png;base64,"+stream
  435. figure = ['<div class="figure"><img src="'+stream+'">',"</div>"]
  436. # figure.canvas.draw()
  437. # figure = "".join( map(chr,figure.canvas.tostring_argb())) #--bytes
  438. # else:
  439. # figure = [ ]
  440. if item['chart']['type'] != 'scalar':
  441. return ['<div class="frame"><div class="chart '+ item['chart']['type']+'">'] + [ " ".join(row) for row in [label,figure,text,info] if row] + ["</div></div>"]
  442. else:
  443. return [ " ".join(row) for row in [label,figure,text,info] if row]
  444. pass
  445. def _csv(self,item):
  446. pass
  447. def export(self,item,format):
  448. """
  449. We have a pipeline here and we should attempt to build a figure using seaborn within an html template using jinja2
  450. This is considered a page (or an item) of an analysis where we will have both data and rendering information with accompanying text
  451. """
  452. html = []
  453. for row in item['pipeline'] :
  454. p = [ "<h2>",row['label'].replace('_',' '),"</h2>"]
  455. y_label = [name for name in row['data'].columns if 'count' in name]
  456. x_label = list(set(row['data'].columns) - set(y_label))
  457. N = row.shape[0]
  458. if 'info' in row :
  459. p += ["<div class='info'>",row['info'],'</div>']
  460. pass
  461. class LogAnalytics :
  462. def __init__(self,path):
  463. logs = open(path).read().split('\n')
  464. logs = [json.loads(row) for row in logs if row.strip() != '']
  465. self.remits = {
  466. "completed": np.sum([1 for row in logs if row['completed'] == True]),
  467. "files":len(logs)
  468. }
  469. # m = LogAnalytics('/home/steve/healthcare-io/remits.log')
  470. css = """
  471. <meta charset="utf-8">
  472. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  473. <title>HealthcareIO - :title </title>
  474. <style>
  475. body{
  476. padding:8px;
  477. padding-left:4%;
  478. padding-right:4%;
  479. }
  480. .pane{
  481. padding:4px;
  482. display:grid;
  483. gap:16px;
  484. grid-template-columns:repeat(2,1fr) ;
  485. }
  486. .numbers {
  487. display:grid;
  488. grid-template-columns:repeat(2,1fr);
  489. gap:16px;
  490. /*padding:2px;*/
  491. /*border:1px solid #CAD5E0;*/
  492. }
  493. .numbers .scalar {
  494. padding:8px;
  495. background-image: linear-gradient(to bottom, #f3f3f3,#d3d3d3, #ffffff);
  496. border:1px solid #CAD5E0;
  497. font-family:sans-serif;
  498. text-transform:capitalize;
  499. text-align:right;
  500. font-size:12px;
  501. display:grid;
  502. grid-template-rows:auto 28px; gap:2px;
  503. }
  504. .numbers .scalar .value-text {
  505. border-top:1px solid #CAD5E0;
  506. padding:8px;
  507. font-weight:bold;
  508. align-items:center;
  509. font-size:14px;
  510. display:grid;
  511. }
  512. .numbers .scalar .value {
  513. display:grid;
  514. color:#004b79;
  515. align-content:center;
  516. font-size:48px; text-align:right; font-weight:bold;}
  517. .frame {
  518. background-image: linear-gradient(to bottom, #f3f3f3,#d3d3d3, #ffffff);
  519. padding:2px;
  520. border:1px solid #CAD5E0;
  521. }
  522. .figure {grid-area:figure; width:500px; height:350px; display:grid; align-items:center}
  523. .info {height:28px; width:100%; grid-area:info;
  524. display:grid;
  525. align-items:center;
  526. text-align:center; text-transform:capitalize; padding:4px; font-size:12px; font-family:sans-serif; border-top:1px solid #CAD5E0;}
  527. .grid {grid-area:grid; }
  528. .label {grid-area:label; font-weight:bold; font-size: 22px; text-align:center; text-transform:capitalize}
  529. .chart {
  530. padding:4px;
  531. padding:8px;
  532. display:grid; grid-template-areas:
  533. "label label label"
  534. "figure grid grid"
  535. "info info info" ;
  536. gap:2px;
  537. }
  538. img {height:auto; max-width:100% ;}
  539. table {width:100%; border-collapse: collapse;}
  540. table , TH, TD{ font-size:14px; padding:8px; font-family:sans-serif; border:1px; border:1px solid #CAD5E0;}
  541. table thead, tbody th { padding:4px; text-transform:capitalize; background-color:#4682B4; color:#ffffff; text-align:center}
  542. table thead tr th {text-align:center}
  543. table tbody td {text-align:right; font-weight: lighter}
  544. table tbody tr:nth-child(odd) {background: #95bce0}
  545. table tbody tr:nth-child(even) {background: #c8e5ff}
  546. </style>
  547. """
  548. # folder = '/home/steve/.healthcareio/config.json'
  549. # e = engine(path=folder)
  550. # p = e.apply(type='claims')
  551. # values = []
  552. # html = [css]
  553. # for row in p :
  554. # frame = []
  555. # for item in row['pipeline'] :
  556. # if row['pipeline'].index(item) == 0 :
  557. # if item['chart']['type'] != 'scalar' :
  558. # # frame = ['<div class="frame">']
  559. # pass
  560. # else:
  561. # frame = ['<div><div class="numbers">']
  562. # frame += e._html(item) #p[3]['pipeline'][0])
  563. # frame += ['</div></div>'] if item['chart']['type'] == 'scalar' else []
  564. # html += frame
  565. # html = '<div class="pane">' + "\n".join(html) + "</div></div>"
  566. # f = open('out.html','w')
  567. # f.write(html.replace(":title","Claims"))
  568. #
  569. # HTML(string=html).write_pdf('out.pdf',stylesheets=[CSS(string=css)])
  570. # x.write_pdf('./out.pdf')
  571. # print (p[2]['pipeline'][0]['data'])
  572. # e.export (p[0])
  573. # features = ['diagnosis.code']
  574. # split(folder = folder, features=features)