瀏覽代碼

Merge pull request 'v2.0 upgrade' (#10) from v2.0 into master

Reviewed-on: https://hiplab.mc.vanderbilt.edu/git/hiplab/parser/pulls/10
Steve L. Nyemba 1 年之前
父節點
當前提交
6db608e657
共有 100 個文件被更改,包括 268 次插入124059 次删除
  1. 8 79
      README.md
  2. 1 0
      bin/healthcare-io
  3. 3 2
      healthcareio/Dockerfile
  4. 7 4
      healthcareio/__init__.py
  5. 210 1
      healthcareio/__main__.py
  6. 0 37
      healthcareio/docker/Dockerfile
  7. 0 10
      healthcareio/docker/bootup.sh
  8. 0 8
      healthcareio/export/__init__.py
  9. 0 388
      healthcareio/export/export.py
  10. 0 288
      healthcareio/export/workers.py
  11. 1 1
      healthcareio/healthcare-io.py
  12. 38 0
      healthcareio/logger.py
  13. 0 239
      healthcareio/server/__init__.py
  14. 0 89
      healthcareio/server/index.py
  15. 0 469
      healthcareio/server/out.html
  16. 0 168
      healthcareio/server/proxy.py
  17. 0 37
      healthcareio/server/static/css/borders.css
  18. 0 57
      healthcareio/server/static/css/default.css
  19. 0 34
      healthcareio/server/static/css/fa/LICENSE.txt
  20. 0 4556
      healthcareio/server/static/css/fa/css/all.css
  21. 0 5
      healthcareio/server/static/css/fa/css/all.min.css
  22. 0 15
      healthcareio/server/static/css/fa/css/brands.css
  23. 0 5
      healthcareio/server/static/css/fa/css/brands.min.css
  24. 0 4522
      healthcareio/server/static/css/fa/css/fontawesome.css
  25. 0 5
      healthcareio/server/static/css/fa/css/fontawesome.min.css
  26. 0 15
      healthcareio/server/static/css/fa/css/regular.css
  27. 0 5
      healthcareio/server/static/css/fa/css/regular.min.css
  28. 0 16
      healthcareio/server/static/css/fa/css/solid.css
  29. 0 5
      healthcareio/server/static/css/fa/css/solid.min.css
  30. 0 371
      healthcareio/server/static/css/fa/css/svg-with-js.css
  31. 0 5
      healthcareio/server/static/css/fa/css/svg-with-js.min.css
  32. 0 2172
      healthcareio/server/static/css/fa/css/v4-shims.css
  33. 0 5
      healthcareio/server/static/css/fa/css/v4-shims.min.css
  34. 0 4441
      healthcareio/server/static/css/fa/js/all.js
  35. 0 5
      healthcareio/server/static/css/fa/js/all.min.js
  36. 0 571
      healthcareio/server/static/css/fa/js/brands.js
  37. 0 5
      healthcareio/server/static/css/fa/js/brands.min.js
  38. 0 998
      healthcareio/server/static/css/fa/js/conflict-detection.js
  39. 0 5
      healthcareio/server/static/css/fa/js/conflict-detection.min.js
  40. 0 2478
      healthcareio/server/static/css/fa/js/fontawesome.js
  41. 0 5
      healthcareio/server/static/css/fa/js/fontawesome.min.js
  42. 0 280
      healthcareio/server/static/css/fa/js/regular.js
  43. 0 5
      healthcareio/server/static/css/fa/js/regular.min.js
  44. 0 1124
      healthcareio/server/static/css/fa/js/solid.js
  45. 0 5
      healthcareio/server/static/css/fa/js/solid.min.js
  46. 0 68
      healthcareio/server/static/css/fa/js/v4-shims.js
  47. 0 5
      healthcareio/server/static/css/fa/js/v4-shims.min.js
  48. 0 19
      healthcareio/server/static/css/fa/less/_animated.less
  49. 0 16
      healthcareio/server/static/css/fa/less/_bordered-pulled.less
  50. 0 12
      healthcareio/server/static/css/fa/less/_core.less
  51. 0 6
      healthcareio/server/static/css/fa/less/_fixed-width.less
  52. 0 1441
      healthcareio/server/static/css/fa/less/_icons.less
  53. 0 27
      healthcareio/server/static/css/fa/less/_larger.less
  54. 0 18
      healthcareio/server/static/css/fa/less/_list.less
  55. 0 56
      healthcareio/server/static/css/fa/less/_mixins.less
  56. 0 24
      healthcareio/server/static/css/fa/less/_rotated-flipped.less
  57. 0 5
      healthcareio/server/static/css/fa/less/_screen-reader.less
  58. 0 2066
      healthcareio/server/static/css/fa/less/_shims.less
  59. 0 22
      healthcareio/server/static/css/fa/less/_stacked.less
  60. 0 1453
      healthcareio/server/static/css/fa/less/_variables.less
  61. 0 23
      healthcareio/server/static/css/fa/less/brands.less
  62. 0 16
      healthcareio/server/static/css/fa/less/fontawesome.less
  63. 0 23
      healthcareio/server/static/css/fa/less/regular.less
  64. 0 24
      healthcareio/server/static/css/fa/less/solid.less
  65. 0 6
      healthcareio/server/static/css/fa/less/v4-shims.less
  66. 0 2562
      healthcareio/server/static/css/fa/metadata/categories.yml
  67. 0 57762
      healthcareio/server/static/css/fa/metadata/icons.json
  68. 0 21485
      healthcareio/server/static/css/fa/metadata/icons.yml
  69. 0 2317
      healthcareio/server/static/css/fa/metadata/shims.json
  70. 0 298
      healthcareio/server/static/css/fa/metadata/shims.yml
  71. 0 688
      healthcareio/server/static/css/fa/metadata/sponsors.yml
  72. 0 20
      healthcareio/server/static/css/fa/scss/_animated.scss
  73. 0 20
      healthcareio/server/static/css/fa/scss/_bordered-pulled.scss
  74. 0 21
      healthcareio/server/static/css/fa/scss/_core.scss
  75. 0 6
      healthcareio/server/static/css/fa/scss/_fixed-width.scss
  76. 0 1441
      healthcareio/server/static/css/fa/scss/_icons.scss
  77. 0 23
      healthcareio/server/static/css/fa/scss/_larger.scss
  78. 0 18
      healthcareio/server/static/css/fa/scss/_list.scss
  79. 0 56
      healthcareio/server/static/css/fa/scss/_mixins.scss
  80. 0 24
      healthcareio/server/static/css/fa/scss/_rotated-flipped.scss
  81. 0 5
      healthcareio/server/static/css/fa/scss/_screen-reader.scss
  82. 0 2066
      healthcareio/server/static/css/fa/scss/_shims.scss
  83. 0 31
      healthcareio/server/static/css/fa/scss/_stacked.scss
  84. 0 1458
      healthcareio/server/static/css/fa/scss/_variables.scss
  85. 0 23
      healthcareio/server/static/css/fa/scss/brands.scss
  86. 0 16
      healthcareio/server/static/css/fa/scss/fontawesome.scss
  87. 0 23
      healthcareio/server/static/css/fa/scss/regular.scss
  88. 0 24
      healthcareio/server/static/css/fa/scss/solid.scss
  89. 0 6
      healthcareio/server/static/css/fa/scss/v4-shims.scss
  90. 0 1336
      healthcareio/server/static/css/fa/sprites/brands.svg
  91. 0 463
      healthcareio/server/static/css/fa/sprites/regular.svg
  92. 0 2995
      healthcareio/server/static/css/fa/sprites/solid.svg
  93. 0 1
      healthcareio/server/static/css/fa/svgs/brands/500px.svg
  94. 0 1
      healthcareio/server/static/css/fa/svgs/brands/accessible-icon.svg
  95. 0 1
      healthcareio/server/static/css/fa/svgs/brands/accusoft.svg
  96. 0 1
      healthcareio/server/static/css/fa/svgs/brands/acquisitions-incorporated.svg
  97. 0 1
      healthcareio/server/static/css/fa/svgs/brands/adn.svg
  98. 0 1
      healthcareio/server/static/css/fa/svgs/brands/adobe.svg
  99. 0 1
      healthcareio/server/static/css/fa/svgs/brands/adversal.svg
  100. 0 0
      healthcareio/server/static/css/fa/svgs/brands/affiliatetheme.svg

+ 8 - 79
README.md

@@ -12,9 +12,8 @@ We wrote this frame to be used in both command line or as a library within in yo
 | -------- | --- |
 |X12 claims/remits| parsing of {x12} claims/remittances into JSON format with human readible attributes|
 |Multi Processing| capable of processing multiple files simultaneously to speed up processing|
-|Analytics support| descriptive statistical analytics : distribution, various counts|
 |Process Recovery| capable of recovering interrupted runs|
-|Export to RDBMS| exports data to relational format (NoSQL -> SQL) 7 supported databases <br> * PostgreSQL,<br> * Redshift, <br>* Neteeza, <br>* Mysql, <br>* Mariadb, <br>* bigquery, <br>* sqlite3 |
+|Export to RDBMS| exports data to relational format (NoSQL -> SQL) 8 supported industry standard databases <br> * PostgreSQL,<br> * Redshift, <br>* Neteeza, <br>* Mysql, <br>* Mariadb, <br>* bigquery, <br>* sqlite3<br>*databricks |
 |**Issues and Bug reports**| info@the-phi.com
 
 
@@ -31,96 +30,26 @@ For advanced features visit [Healthcare/IO::Parser](https://healthcareio.the-phi
 
 **Installation command**
 
-    pip install --upgrade git+https://hiplab.mc.vanderbilt.edu/git/lab/parse-edi.git
+    pip install --upgrade git+https://hiplab.mc.vanderbilt.edu/git/hiplab/parser.git
 
 ## Usage 
 
-Healthcare/IO is primarily intended to be used as a command line parser (for now). It is fully written in python 3+
+Healthcare/IO is primarily intended to be used as a command line parser (for now). However it can be used as a library that you integrate into custom code. It is fully written in python 3+ under MIT License
 
-**CLI :**
+**Learning More**
 
-1. signup to get parsing configuration
-
-    The parser is driven by a configuration file that specifies fields to parse and how to parse them. You need by signing up, to get a copy of the configuration file. 
-        #
-        # Use sqlite as data-store
-        healthcare-io.py --signup <email> [--store <mongo|sqlite>]
-
-2. check version       
-
-    Occasionally the attributes in the configuration file may change, This function will determine if there is a new version available.
-
-        healthcare-io.py --check-update
-
-3. parsing data stored in a folder
-
-    The parser will recursively traverse a directory with claims and or remittances
-
-        healthcare-io.py --parse --folder <path> [--batch <n>] [--resume]
-        
-        with :
-            --parse     tells the engine what to parse claims or remits
-            --folder    location of the claims|remits
-            --batch     number of processes to spawn to parse the files
-            --resume    tells the parser to resume parsing 
-                        if all files weren't processed or new files were added into the folder
-
-4. export data to a relational data-store
-
-    The parser will export data into other data-stores as a  relational tables allowing users to construct views to support a variety of studies.
-
-        healthcare-io.py --export <835|837> --export-config <path-export.json>
-
-        with:
-            --export-config    configuration to support data-store
-
-    **example**
-        1. Exporting to PostgreSQL
-
-            {"provider":"postgresql","database":"healthcareio","schema":"foo"}
-    
-    **NOTE**
-
-    The output generates a set of tables that are the result of transforming unstructured data to relational structure. The tables can be bound with the attribute **_id**
-
-    The configuration file needed to implement export is modelled after the following template:
-
-
-        {
-            "provider":"<postgresql|redshift|mysql|mariadb>",
-            "database":"<name>",["host":"server-name"],["port":5432],
-            ["user":"me"],["password":"!@#z4qm"],["schema":"target-schema"]
-                
-            }
-
-    **parameters:**
-
-        provider    postgresql,redshift,mysql or mariadb (supported providers)
-        database    name of the database
-    
-    **optional:**
-
-        schema      name of the target schema. If not provided we will assume the default
-        host        host of the database. If not provided assuming localhost
-        port        port value of the database if not provided the default will be used
-        user        database user name. If not provided we assume security settings to trust
-        password    password of database user. If not set we assume security settings to trust
+- More can be found at https://healthcareio.the-phi.com/ 
+- The source code & example code are at https://hiplab.mc.vanderbilt.edu/git/hiplab/parser
 
 **Known Limitations**
 
 1. By default it does NOT come with all {X12} Segments. 
-2. Does not support an easy way to rename attributes it parses
+2. Renaming attributes requires writing plugins
 3. Upgrade configuration may require dropping tables
-4. For now can only read {x12} from disk
+4. For now can only read {x12} from disk (or s3 bucket)
 
 There is support for additional features and attributes available at [Healthcare/IO::Parser](https://healthcareio.the-phi.com/parser).
 
-**In development**
-
-1. Wizard/UI to enable attribute renaming
-2. Dashboard for quick overview
-3. Reading {x12} from s3 and other cloud buckets
-4. Docker Image
 
 ## Credits
 

+ 1 - 0
bin/healthcare-io

@@ -0,0 +1 @@
+../healthcareio/__main__.py

+ 3 - 2
healthcareio/Dockerfile

@@ -10,11 +10,11 @@ RUN ["apt-get","-y","install","apt-utils"]
 
 RUN ["apt","update","--fix-missing"]
 RUN ["apt-get","upgrade","-y"]
-RUN ["apt-get","install","-y","mongo","sqlite3","sqlite3-pcre","libsqlite3-dev","python3-dev","python3","python3-pip","git","python3-virtualenv","wget"]
+RUN ["apt-get","install","-y","mongo","sqlite3","sqlite3-pcre","libsqlite3-dev","python3-dev","python3","python3-pip","git","wget"]
 #
 #
 RUN ["pip3","install","--upgrade","pip"]
-# RUN ["pip3","install","git+https://healthcare.the-phi.com/git/code/parser.git","botocore"]
+RUN ["pip3","install","healthcareio@git+https://healthcare.the-phi.com/git/code/parser.git"]
 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)
@@ -24,6 +24,7 @@ VOLUME ["/data"]
 # This is the port from which some degree of monitoring can/will happen
 EXPOSE 80
 # wget https://healthcareio.the-phi.com/git/code/parser.git/bootup.sh 
+RUN ["wget","https://github.com/sosedoff/pgweb/releases/download/v0.14.2/pgweb_linux_amd64.zip"]
 COPY bootup.sh bootup.sh
 ENTRYPOINT ["bash","-C"]
 CMD ["bootup.sh"]

+ 7 - 4
healthcareio/__init__.py

@@ -15,10 +15,13 @@ Usage :
 
 """
 
-from healthcareio import analytics
-from healthcareio import server
-from healthcareio import export
+# from healthcareio import analytics
+# from healthcareio import server
+# from healthcareio import export
 import healthcareio.x12 as x12
+from healthcareio.x12 import publish, plugins
 import healthcareio.params as params
-
+from healthcareio import logger
 # from healthcareio import server
+
+import meta

+ 210 - 1
healthcareio/__main__.py

@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 """
 (c) 2019 EDI Parser Toolkit, 
 Health Information Privacy Lab, Vanderbilt University Medical Center
@@ -10,8 +11,216 @@ This code is intended to process and parse healthcare x12 837 (claims) and x12 8
 The claims/outpout can be forwarded to a NoSQL Data store like couchdb and mongodb
 Usage :
     Commandline :
-    python xreader.py --parse claims|remits --config <path>
+    # parse {x12}
+    healthcare-io parse <x12_folder>
+
+    # export {x12}
+    healthcare-io export 
+
+        - 
     Embedded    :
 
 """
 # import healthcareio
+import typer
+from typing import Optional
+from typing_extensions import Annotated
+import uuid
+import os
+import meta
+import json
+import time
+from healthcareio import x12
+from healthcareio.x12.parser import X12Parser
+import requests
+import pandas as pd
+import numpy as np
+
+
+# import healthcareio
+# import healthcareio.x12.util
+# from healthcareio.x12.parser import X12Parser
+app = typer.Typer()
+CONFIG_FOLDER = os.sep.join([os.environ['HOME'],'.healthcareio'])
+HOST = "https://healthcareio.the-phi.com" if 'PARSER_HOST_URL' not in os.environ else os.environ['PARSER_HOST_URL']
+@app.command(name='init')
+def config(email:str,provider:str='sqlite') :
+    """\b
+    Generate configuration file needed with default data store. For supported data-store providers visit https://hiplab.mc.vanderbilt.edu/git/hiplab/data-transport.git
+    
+    :email      your email    
+    
+    :provider   data store provider (visit https://hiplab.mc.vanderbilt.edu/git/hiplab/data-transport.git)
+    """
+    _db = "healthcareio"
+    global CONFIG_FOLDER
+    # _PATH = os.sep.join([os.environ['HOME'],'.healthcareio'])
+    
+    if not os.path.exists(CONFIG_FOLDER) :
+        os.mkdir(CONFIG_FOLDER)
+
+    if provider in ['sqlite','sqlite3'] :        
+        _db = os.sep.join([CONFIG_FOLDER,_db+'.db3'])
+
+    _config = {
+        "store":{
+            "provider":provider,"database":_db,"context":"write"
+        },
+        "plugins":None,
+        "system":{
+            "uid":str(uuid.uuid4()),
+            "email":email,
+            "version":meta.__version__,
+            "copyright":meta.__author__
+
+        }
+    }
+    #
+    # Let create or retrieve a user's key/token to make sure he/she has access to features they need
+    # This would also allow us to allow the users to be informed of new versions
+    #
+    try:
+        global HOST
+        # HOST = "https://healthcareio.the-phi.com" if 'PARSER_HOST_URL' not in os.environ else os.environ['PARSER_HOST']
+        url = f"{HOST}/api/users/signup"
+        _body = {"email":email,"version":meta.__version__}
+        _headers = {"content-type":"application/json"}
+        resp  = requests.post(url,headers=_headers,data=json.dumps(_body))
+        if resp.ok :
+            _config['system'] = dict(_config['system'],**resp.json())
+    except Exception as e:
+        print (e)
+        pass
+    # store this on disk
+    f = open(os.sep.join([CONFIG_FOLDER,'config.json']),'w')
+    f.write(json.dumps(_config))
+    f.close()
+    _msg = f"""
+        Thank you for considering using our {{x12}} parser verion {meta.__version__}
+        The generatted configuration file found at {CONFIG_FOLDER}
+        The database provider is {provider}
+
+        visit {HOST} to learn more about the features,
+    
+    """
+
+    print (_msg)
+@app.command(name='about')
+def copyright():
+    f"""
+    This function will return information about the {meta.__name__}
+    """
+    for note in [meta.__name__,meta.__author__,meta.__license__]:
+        print (note)
+
+    pass
+@app.command()
+def parse (claim_folder:str,plugin_folder:str = None,config_path:str = None):
+    """
+    This function will parse 837 and or 835 claims given a location of  parsing given claim folder and/or plugin folder.
+    
+    plugin_folder   folder containing user defined plugins (default are loaded)
+    
+    config_path     default configuration path
+    """
+    
+    _plugins,_parents   = x12.plugins.instance(path=plugin_folder)
+    _files              = x12.util.file.Location.get(path=claim_folder,chunks=10)
+    _path = config_path if config_path else os.sep.join([CONFIG_FOLDER,'config.json'])
+    
+    if os.path.exists(_path) :
+        f = open(_path)
+        _config  = json.loads(f.read())
+        f.close()
+        _store = _config['store']
+    #     # print (len(_files))
+        jobs = []
+        for _chunks in _files:
+            pthread = X12Parser(plugins=_plugins,parents=_parents,files=_chunks, store=_store)
+            pthread.start()
+            jobs.append(pthread)
+        while jobs :
+            jobs = [pthread for pthread in jobs if pthread.is_alive()]
+            time.sleep(1)
+    #     pass
+    # else:
+    #     pass
+    print ()
+    print ("                        PARSED                           ")
+    print ("...................... FINISHED .........................")
+    #
+    #
+@app.command()
+def check():
+    """
+    This function checks for the version running against the current version    
+    """
+    _info = [meta.__version__,None]
+    url = f'{HOST}/api/store/version'
+    try:
+        resp= requests.post(url)
+        _info[1] = resp.text if resp.status_code == 200 else "NA"
+    except Exception as e:
+        _info[1] = "NA"
+        pass
+    if _info[1] == "NA" :
+        _msg = "Unavailable server (unreachable)"
+    else:
+        _msg = ""
+
+    print ()
+    _info =pd.DataFrame(_info,columns=["versions"],index=["Yours","Current"])
+    print (_info)
+
+    print (_msg)
+
+@app.command(name="export-schema")
+def export_schema (file_type:str):
+    """
+    This function will display the schema in JSON format of a given file/type
+    """
+    _plugins,_parents = x12.plugins.instance()
+    if file_type not in ['835','837'] and file_type in ['claims','remits']:
+        file_type = '835' if file_type == 'remits' else '837'
+    _template = x12.publish.build(x12=file_type,plugins=_plugins)
+    print ( json.dumps(_template))
+
+
+@app.command(name="export")    
+def publish (file_type:str,path:str):
+    """
+    This function will export to a different database
+    file_type       values are either claims or remits 
+    
+    path            path to export configuration (data transport file)
+
+    file_type       claims or remits (835 or 837)
+    """
+    _type = None
+    if file_type.strip() in ['837','claims'] :
+        _type = 'claims'
+        _x12 = '837'
+    elif file_type.strip() in ['835','remits']:
+        _type = 'remits'
+        _x12 = '835'
+    if _type :
+        _store = {'source':os.sep.join([CONFIG_FOLDER,'config.json']),'target':path}
+        for _key in _store :
+            f = open(_store[_key])
+            _store[_key] = json.loads(f.read())
+            f.close()
+        _store['source'] = _store['source']['store']
+        
+        _plugins,_parents = x12.plugins.instance()
+        x12.publish.init(plugins=_plugins,x12=_x12,store=_store)
+    else:
+        print ("Can not determine type, (837 or 835)")
+
+    print ()
+    print ("                        EXPORT                           ")
+    print ("...................... FINISHED .........................")
+    
+        
+if __name__ == '__main__' :
+    
+    app()

+ 0 - 37
healthcareio/docker/Dockerfile

@@ -1,37 +0,0 @@
-#
-# 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"]
-# 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 - 10
healthcareio/docker/bootup.sh

@@ -1,10 +0,0 @@
-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 - 8
healthcareio/export/__init__.py

@@ -1,8 +0,0 @@
-"""
-
-This module is outside of the scope of the parser but it's a good to have because it allows to move data outside of mongodb into another data-store
-dependencies:
-    - data-transport https://healthcareio.the-phi.com/git/code/transport
-"""
-from healthcareio.export import export
-from healthcareio.export import workers

+ 0 - 388
healthcareio/export/export.py

@@ -1,388 +0,0 @@
-"""
-This file implements exporting data from a mongodb database to another data-store in relational format (csv). Any other use case will have to be performed with mongodb native tools
-    target:
-        File/SQLite
-        PostgreSQL
-        MySQL
-@TODO:
-    - Insure to support both schemas and table prefixes
-
-Usage :
-
-License:
-
-Copyright 2019, The Phi Technology LLC
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-"""
-import transport
-import numpy as np
-import os
-import json
-import jsonmerge
-import sys
-# from airflow import DAG
-from datetime import timedelta
-# import healthcareio.export.workers as workers
-from healthcareio.export import workers
-import platform
-from datetime import datetime
-import copy
-import requests
-import time
-from healthcareio.x12 import Parser
-PATH    = os.sep.join([os.environ.get('HOME'),'.healthcareio','config.json'])
-
-STORE_URI = 'http://healthcareio.the-phi.com/store/healthcareio'
-#
-# let us see if we have any custom configurations ...
-
-PATH = os.sep.join([os.environ.get('HOME'),'.healthcareio','custom'])
-CONFIG = {}
-CUSTOM_CONFIG = {} 
-# if os.path.exists(PATH) and os.listdir(PATH) :
-#     CONFIG  = json.loads((open(PATH)).read())
-#     PATH = os.sep.join([PATH,os.listdir(PATH)[0]]) 
-#     CUSTOM_CONFIG = json.loads((open(PATH)).read())
-    
-# _args   = dict(CONFIG['store'],**{'type':'mongo.MongoReader'})
-
-def get_field_names (_map,label):
-    fields = list(_map.keys())
-    
-    return fields if not label else [{label:fields}]
-def get_field (entry):
-    label = list(set(['label','field']) & set(entry.keys()))
-    label = None if not label else entry[label[0]]
-    if 'map' not in entry :
-        return None
-    _map = entry['map']
-    return get_field_names(_map,label)
-
-    pass    
-#
-#-- Get the fields to export that will go in the the unwind ;
-#
-def meta(config) :
-    """
-        This function will return the metadata associated with a given configuraiton 835 or 838
-        :params config  configuration section
-    """
-    _info = []
-    table_count = 1
-    cached = {}
-    for prefix in config :
-        if 'cache' in config[prefix] :
-            _cache  = config[prefix]['cache']
-            field   = _cache['field']
-            key     = _cache['key']
-            if 'map' in config[key]:
-                config[key]['map'][field] = -100
-    add_index  = {} #-- tells if we should add _index attribute or not
-    for prefix in config :
-       #
-       # Running through every element that we can parse (from the configuration)
-       # This allows us to create ER-Like structures from the data we have
-       #
-        
-        if type(config[prefix]) != dict :
-            continue
-       
-        if '@ref' in config[prefix] : #and set(['label','field','map']) & set(config[prefix]['@ref'].keys()):
-            for subprefix in config[prefix]['@ref'] :
-                
-                _entry = config[prefix]['@ref'][subprefix]
-                _id = list(set(['label','field']) & set(config[prefix]['@ref'][subprefix].keys()))
-                if _id :
-                    #
-                    # In case a reference item has a parent field/label
-                    _id = _id[0]
-                    table = config[prefix]['@ref'][subprefix][_id]
-                    add_index[table] = 1 if _id == 'label' else 0
-                if 'map' in _entry :
-                    
-                    _info += get_field(_entry)
-                else:
-                    _info += list(_entry.keys())
-        if set(['label','field','map']) & set(config[prefix].keys()):
-            _entry = config[prefix]
-            _id = list(set(['label','field']) & set(config[prefix].keys()))
-            if _id :
-                _id = _id[0]
-                table = config[prefix][_id]
-                add_index[table] = 1 if _id == 'label' else 0
-            
-                
-            if 'map' in _entry :
-                _info += get_field(_entry)
-        
-
-            
-    #
-    # We need to organize the fields appropriately here
-    #
-    # print (_info)
-    fields = {"main":[],"rel":{}}
-    for row in _info :
-        if type(row) == str :
-            fields['main'] += [row]
-            fields['main'] = list(set(fields['main']))
-            fields['main'].sort()
-        else :
-            _id =  list(set(add_index.keys()) & set(row.keys()))
-            
-            if _id :
-                
-                _id = _id[0]
-                
-                if add_index[_id] == 1 :
-                    row[_id]+= ['_index']
-            
-                if _id not in fields['rel']:
-                    
-                    fields['rel'][_id] = row[_id]
-                else:
-                    fields['rel'][_id] += row[_id]
-            else:
-                print ( _entry)
-                
-                _id = list(row.keys())[0]
-                fields['rel'][_id] = row[_id] if _id not in fields['rel'] else fields['rel'][_id] + row[_id]
-                
-            
-    
-    return fields
-def create (**_args) :
-    skip = [] if 'skip' not in _args else _args['skip']
-    fields =  ([_args['key']] if 'key' in _args else []) + _args['fields']
-    fields = ['_id'] + list(set(fields))
-    fields.sort()
-    table = _args['table'] 
-    sql = ['CREATE TABLE :table ',"(",",\n".join(["\t".join(["\t",name,"VARCHAR(125)"]) for name in fields]),")" ]
-    return " ".join(sql)
-def read (**_args) :
-    """
-    This function will read rows with a set number of files and store them into a data-store
-    """
-    files   = _args['files']
-    fields  = _args ['fields']
-    name     = _args['id']
-    pipeline= {"find":name,"filter":{"name":{"$in":files}}}
-    #
-    # @TODO: Find a way to write the data into a data-store
-    #   - use dbi interface with pandas or stream it in
-    #
-def init_sql(**_args):
-    """
-    This function expresses how we can generically read data stored in JSON format from a relational table
-    :param type 835,837
-    :param skip list of fields to be skipped
-    """
-    #
-    # we should acknowledge global variables CONFIG,CUSTOM_CONFIG
-    TYPE = _args['type']    
-    _config = CONFIG['parser'][TYPE][0]
-    TABLE_NAME = 'claims' if TYPE== '837' else 'remits'
-    if TYPE in CUSTOM_CONFIG :
-        _config  = jsonmerge.merge(_config,CUSTOM_CONFIG[TYPE])
-    #  
-    _info = meta(_config)
-    
-    _projectSQLite = [] #-- sqlite projection
-    for field_name in _info['main'] :
-        _projectSQLite += ["json_extract(data,'$."+field_name+"') "+field_name]    
-    _projectSQLite = ",".join(_projectSQLite) #-- Wrapping up SQLITE projection on main table
-    SQL = "SELECT DISTINCT claims.id _id,:fields FROM :table, json_each(data)".replace(":fields",_projectSQLite).replace(":table",TABLE_NAME)
-    r = [{"table":TABLE_NAME,"read":{"sql":SQL},"sql":create(table=TABLE_NAME,fields=_info['main'])}]    
-    for table in _info['rel'] :
-        #
-        # NOTE: Adding _index to the fields
-        fields = _info['rel'][table] #+["_index"]
-        
-        
-        project = [TABLE_NAME+".id _id","json_extract(data,'$.claim_id') as claim_id"]
-        fn_prefix = "json_extract(x.value,'$." if '_index' not in _info['rel'][table] else "json_extract(i.value,'$."
-        for field_name in fields :
-            # project += ["json_extract(x.value,'$."+field_name+"') "+field_name]
-            project += [fn_prefix+field_name+"') "+field_name]
-        SQL = "SELECT DISTINCT :fields FROM "+TABLE_NAME+", json_each(data) x, json_each(x.value) i where x.key = ':table'"
-        SQL = SQL.replace(":table",table).replace(":fields",",".join(project))
-        r += [{"table":table,"read":{"sql":SQL},"sql":create(table=table,key='claim_id',fields=fields)}]
-    
-    return r
-def init(**_args):
-    # if 'provider' in CONFIG['store'] and CONFIG['store']['provider'] == 'sqlite' :
-    #     return init_sql(**_args)
-    # else:
-    #     return init_mongo(**_args)
-    if ('provider' in CONFIG['store'] and CONFIG['store']['provider'] == 'mongo') or ('type' in CONFIG['store'] and 'mongo' in CONFIG['store']['type']):
-        return init_mongo(**_args)
-    else:
-        return init_sql(**_args)
-def init_mongo (**_args) :   
-    """
-    This function is intended to determine the number of tables to be created, as well as their type.
-    :param type         {835,837}
-    :param skip         list of fields to be skipped
-    """ 
-    TYPE = _args['type']
-    # SKIP = _args['skip'] if 'skip' in _args else []
-    _config = CONFIG['parser'][TYPE][0]
-    if TYPE in CUSTOM_CONFIG :
-        _config  = jsonmerge.merge(_config,CUSTOM_CONFIG[TYPE])
-    _info = meta(_config)
-    #
-    # @TODO: implement fields to be skipped ...
-    #
-    TABLE_NAME = 'claims' if TYPE== '837' else 'remits'
-    
-    # project = dict.fromkeys(["_id","claim_id"]+_info['main'],1)
-    project = {}
-    
-    for field_name in _info['main'] :
-        
-        _name = "".join(["$",field_name])
-        project[field_name] = {"$ifNull":[_name,""]}
-
-    project["_id"] = 1
-    project = {"$project":project}
-    
-    # _projectSQLite = ",".join(_projectSQLite) #-- Wrapping up SQLITE projection on main table
-
-    r = [{"table":TABLE_NAME,"read":{"mongo":{"aggregate":TABLE_NAME,"pipeline":[project],"cursor":{},"allowDiskUse":True}},"sql":create(table=TABLE_NAME,fields=_info['main'])}] 
-    
-    
-    for table in _info['rel'] :
-        #
-        # NOTE: Adding _index to the fields
-        fields = _info['rel'][table] +["_index"]
-        
-        project = {"_id":1,"claim_id":1,"_index":1} #dict.fromkeys(["_id","claim_id"]+fields,[ ".".join([table,field_name]) for field_name in fields])
-        for field_name in fields :
-            # project[field_name] = "$"+".".join([table,field_name])
-            _name = "$"+".".join([table,field_name])
-            project[field_name] = {"$ifNull":[_name,""]} #{"$cond":[{"$eq":[_name,None]},"",_name]}
-        project["_id"] = 1
-        # pipeline = [{"$match":{"procedures":{"$nin":[None,'']}}},{"$unwind":"$"+table},{"$project":project}]
-        pipeline = [{"$match": {table: {"$nin": [None, ""]}}},{"$unwind":"$"+table},{"$project":project}]
-        cmd = {"mongo":{"aggregate":TABLE_NAME,"cursor":{},"pipeline":pipeline,"allowDiskUse":True}}
-        r += [{"table":table,"read":cmd,"sql":create(table=table,key='claim_id',fields=fields)}]
-    
-    return r
-
-class Factory:
-    @staticmethod
-    def license(**_args):
-        body = {}
-        body['email'] = _args['email']
-        body['host'] = platform.node()
-        body['date'] = {"month":datetime.now().month,"year":datetime.now().year,"day":datetime.now().day}
-        headers = {'uid': body['email'],'content-type':'application/json'}
-        uri = STORE_URI+'/init'
-        http  = requests.session()
-        r = http.post(uri,headers=headers,data=body)
-        
-        return r.json() if r.status_code == 200 else {}
-    @staticmethod
-    def instance(**_args):
-        """
-        The creation process will only require a target store and a type (385,837)
-        :param type     EDI type to be processed i.e 835 or 837
-        :param write_store    target data-store (redshift, mariadb,mongodb ...)
-        """
-        global PATH
-        global CONFIG
-        global CUSTOM_CONFIG
-
-        PATH  = _args['config']
-        
-        # if 'config' in _args  :
-        #     PATH  = _args['config']
-        # else:
-        #     PATH = os.sep.join([os.environ.get('HOME'),'.healthcareio','config.json'])
-        CONFIG = Parser.setup(PATH)
-        CUSTOM_PATH = os.sep.join([os.environ.get('HOME'),'.healthcareio','custom'])
-        if os.path.exists(CUSTOM_PATH) and os.listdir(CUSTOM_PATH) :
-            
-            CUSTOM_PATH = os.sep.join([CUSTOM_PATH,os.listdir(CUSTOM_PATH)[0]]) 
-            CUSTOM_CONFIG = json.loads((open(CUSTOM_PATH)).read())
-        
-        _features = Factory.license(email=CONFIG['owner'])
-        X12_TYPE = _args['type']
-        store = copy.deepcopy(CONFIG['store']) #-- reading the original data
-        #
-        # Formatting accordingly just in case
-        if 'provider' in store :
-            if 'table' in store:
-                store['table'] = 'claims' if X12_TYPE == '837' else 'remits'
-            store['context'] ='read'
-        else:
-            pass
-        # store['type']='mongo.MongoReader'
-       
-        wstore = _args['write_store'] #-- output data store
-        
-        PREFIX = 'clm_' if X12_TYPE == '837' else 'era_'
-        # SCHEMA = '' if 'schema' not in wstore['args'] else wstore['args']['schema']
-        SCHEMA = '' if 'schema' not in wstore else wstore['schema']
-        _config = CONFIG['parser'][X12_TYPE][0]
-        if X12_TYPE in CUSTOM_CONFIG :
-            _config = jsonmerge.merge(_config,CUSTOM_CONFIG[X12_TYPE])        
-        # _info = meta(_config)     
-        job_args = init(type=X12_TYPE)  #-- getting the queries that will generate the objects we are interested in
-        # print (json.dumps(job_args))
-        _jobs = []
-        
-        for row in job_args:
-            # _store = json.loads(json.dumps(wstore))
-            _store = copy.deepcopy(wstore)
-            
-            # _store['args']['table'] = row['table']
-            if 'type' in _store :
-                _store['args']['table'] = row['table']
-            else:
-                
-                _store['table'] = row['table']
-            _pipe =  [
-                workers.CreateSQL(prefix=PREFIX,schema=SCHEMA,store=_store,sql=row['sql']),
-                # workers.Reader(prefix=PREFIX,schema=SCHEMA,store=store,mongo=row['mongo'],max_rows=250000,features=_features,table=row['table']),
-                workers.Reader(prefix=PREFIX,schema=SCHEMA,store=store,read=row['read'],max_rows=250000,features=_features,table=row['table']),
-                
-                workers.Writer(prefix=PREFIX,schema=SCHEMA,store=_store)
-            ]
-            _jobs += [workers.Subject(observers=_pipe,name=row['table'])]
-            
-        return _jobs    
-
-# if __name__ == '__main__' :
-    # pass
-# pipes = Factory.instance(type='835',write_store={"type":"sql.SQLWriter","args":{"provider":"postgresql","db":"sample",}}) #"inspect":0,"cast":0}})  
-# # pipes[0].run()
-# for thread in pipes:
-#     thread.start()
-#     time.sleep(1)
-# while pipes :
-#     pipes = [thread for thread in pipes if thread.is_alive()]
-#     time.sleep(10)
-# print (Factory.license(email='steve@the-phi.com'))
-#
-# check account with basic inormation
-# 
- 
-# class Observerob:
-#     def __init__(**_args) :
-        
-# #-- Let us all flatten the table
-# #
-# TYPE = '835'
-# _config = jsonmerge.merge(CONFIG['parser'][TYPE][0],CUSTOM_CONFIG[TYPE])
-# # f = meta(CONFIG['parser'][TYPE][0])
-# # _f = meta(CUSTOM_CONFIG[TYPE])
-# f = meta(_config)
-# # print (json.dumps( (f)))
-# print (json.dumps(init(type='835')))
-# # print (create(fields=f['rel']['adjudicated'],table='adjudicated',key='claim_id'))

+ 0 - 288
healthcareio/export/workers.py

@@ -1,288 +0,0 @@
-"""
-    HealthcareIO - The Phi Technology LLC 2020
-
-    This file contains functionalities that implement elements of an ETL pipeline that will consist of various workers. 
-    The pipeline is built around an observer design pattern.
-    
-    @TODO: Integrate with airflow and other process monitoring tools
-"""
-import transport
-import os
-from multiprocessing import Process, Lock
-import numpy as np
-import json
-import pandas as pd
-from zmq import has
-
-class Subject (Process):
-    cache = pd.DataFrame()
-    lock = Lock()
-    @staticmethod
-    def log(_args):
-        Subject.lock.acquire()
-        try:
-            Subject.cache = Subject.cache.append(pd.DataFrame([_args]))
-        except Exception as e :
-            print (e)
-        finally:
-            Subject.lock.release()
-    def __init__(self,**_args):
-        super().__init__()
-        self.observers = _args['observers']
-        self.index = 0
-        self.name = _args['name']
-        self.table = self.observers[1].table
-        self.m = {}
-        
-        
-        pass
-    def run(self):        
-        self.notify()
-    def notify(self):
-        if self.index < len(self.observers) :
-            
-            observer = self.observers[self.index]             
-            _observer = None if self.index == 0 else self.observers[self.index -1]
-            _invalues = None if not _observer else _observer.get()
-            if _observer is None :
-                self.m['table'] = self.name
-                
-
-
-
-            observer.init(caller=self,invalues = _invalues)        
-            self.index += 1            
-            observer.execute()
-            print ({"table":self.table,"module":observer.name(),"status":observer.status})
-            # self.m[observer.name()] = observer.status
-            
-        else:
-            pass
-            
-            
-            
-        
-class Worker :
-    def __init__(self,**_args):
-        #PATH = os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])
-        #CONFIG = json.loads((open(PATH)).read())
-        self._info = _args['store']
-        self.logs = []
-        self.schema = _args['schema']
-        self.prefix = _args['prefix']
-        self.status = 0
-        
-    def name(self):
-        return self.__class__.__name__ 
-    def log (self,**_args):
-        """
-        This function is designed to log to either the console or a data-store
-        """
-        # print (_args)
-        pass
-    def init(self,**_args):
-        """
-        Initializing a worker with arguments needed for it to perform it's task basic information needed are
-        :param caller   caller to be notified
-        :param store    data-store information i.e (pgsql,couchdb, mongo ...)
-        """
-        self.caller = _args['caller']
-        #self._info      = _args['store']
-        self._invalues  = _args['invalues'] if 'invalues' in _args else None
-    def execute(self):
-        try:
-            self._apply()
-        except Exception as error:
-            pass
-        finally:
-            if hasattr(self,'caller') :
-                self.caller.notify() 
-    def _apply(self):        
-        pass
-    def get(self):
-        pass
-    def notify(self):
-        self.caller.notify()
-    def tablename(self,name) :
-        PREFIX_SEPARATOR = '_' if '_' not in self.prefix else ''
-        SCHEMA_SEPARATOR = '' if self.schema.strip() =='' else '.'
-        TABLE_NAME =  PREFIX_SEPARATOR.join([self.prefix,name]) 
-        return SCHEMA_SEPARATOR.join([self.schema,TABLE_NAME])
-        
-
-class CreateSQL(Worker) :
-    """
-    This class is intended to create an SQL Table given the 
-    """
-    def __init__(self,**_args):
-        super().__init__(**_args)
-        self._sql = _args['sql']
-        
-    def init(self,**_args):
-        super().init(**_args)
-    
-    def _apply(self) :
-        sqltable = self._info['table'] if 'provider' in self._info else self._info['args']['table']
-        sqltable = self.tablename(sqltable)
-        # log = {"context":self.name(),"args":{"table":self._info['args']['table'],"sql":self._sql}}
-        log = {"context":self.name(),"args":{"table":sqltable,"sql":self._sql.replace(":table",sqltable)}}
-        try:
-            
-          
-            writer  = transport.factory.instance(**self._info)            
-            writer.apply(self._sql.replace(":table",sqltable))
-            writer.close()
-            log['status'] = 1
-            self.status = 1
-        except Exception as e:
-            log['status'] = 0
-            log['info']  = {"error":e.args[0]}
-            
-            # print (e)
-        finally:
-            self.log(**log)
-
-class Reader(Worker):
-    """
-    read from mongodb and and make the data available to a third party
-    :param pipeline mongodb command
-    :param max_rows maximum rows to be written in a single insert
-    """
-    def __init__(self,**_args):
-        super().__init__(**_args) 
-    
-        
-        # self.pipeline = _args['mongo']  #-- pipeline in the context of mongodb NOT ETL
-        
-        # self.pipeline = _args['mongo'] if 'mongo' in _args else _args['sql']
-        self.pipeline = _args['read'] ;
-        self.MAX_ROWS = _args['max_rows']
-        self.table = _args['table'] #-- target table
-     
-        
-        # is_demo = 'features' not in _args or ('features' in _args and ('export_etl' not in _args['features'] or _args['features']['export_etl'] == 0))        
-        #
-        # @TODO: Bundle the limits with the features so as to insure that it doesn't come across as a magic number
-        #
-        # LIMIT = -1
-        # if is_demo :
-        #     LIMIT = 10000
-        #     if set(['find','distinct']) & set(self.pipeline.keys()) :
-        #         self.pipeline['limit'] = LIMIT
-        #     elif 'aggregate' in self.pipeline :
-                
-        #         self.pipeline['pipeline'] = [{"$limit":LIMIT}] + self.pipeline['pipeline']
-        #     self.log(**{"context":self.name(),"demo":is_demo,"args":{"limit":LIMIT}})
-    
-    def init(self,**_args):
-        super().init(**_args)
-        self.rows = []
-              
-  
-    def _apply(self):
-        try:
-            if 'type' in self._info :
-                self._info['type'] = self._info['type'].replace('Writer','Reader')
-                if 'fields' in self._info['args'] :
-                    del self._info['args']['fields']
-            else:
-                self._info['context'] = 'read'
-            self.reader = transport.factory.instance(**self._info) ; 
-            
-            # self.rows = self.reader.read(mongo=self.pipeline)
-            self.rows = self.reader.read(**self.pipeline)
-            
-            if type(self.rows) == pd.DataFrame :
-                self.rows = self.rows.to_dict(orient='records')
-            # if 'provider' in self._info and self._info['provider'] == 'sqlite' :
-            #     self.rows = self.rows.apply(lambda row: json.loads(row.data),axis=1).tolist()
-            
-            N = len(self.rows) / self.MAX_ROWS  if len(self.rows) > self.MAX_ROWS else 1
-            N = int(N) 
-            # self.rows = rows
-            
-            _log = {"context":self.name(), "status":1,"info":{"rows":len(self.rows),"table":self.table,"segments":N}}
-            self.rows  = np.array_split(self.rows,N)
-            
-            
-            # self.get = lambda : rows #np.array_split(rows,N)
-            self.reader.close()
-            self.status = 1
-            #
-        except Exception as e :
-            _log['status'] = 0
-            _log['info']  = {"error":e.args[0]}
-            print ([e])
-            
-
-        self.log(**_log)
-        
-        # @TODO: Call the caller and notify it that this here is done
-    def get(self):       
-        return self.rows
-        
-
-class Writer(Worker):
-    def __init__(self,**_args):
-        super().__init__(**_args)
-        if 'provider' in self._info :
-            self._info['context'] = 'write'
-        
-    def init(self,**_args):
-        """
-        :param  store   output data-store needed for writing
-        :param invalues input values with to be written somewhere
-        """
-        
-        
-        self._invalues = _args['invalues']
-        
-        
-    def _apply(self):
-        
-        # table = self._info['args']['table'] if 'table' in self._info['args'] else 'N/A'
-        # table = self.tablename(self._info['args']['table'])
-        if 'provider' in self._info :
-            table = self.tablename(self._info['table'])
-            self._info['table']  = table
-        else:
-            table = self.tablename(self._info['args']['table'])
-            self._info['args']['table'] = table
-        
-        writer = transport.factory.instance(**self._info) 
-        
-        index = 0
-        
-        if self._invalues :
-            for rows in self._invalues :
-                # print (['segment # ',index,len(rows)])
-                
-                # self.log(**{"context":self.name(),"segment":(index+1),"args":{"rows":len(rows),"table":table}})                
-                
-                if len(rows) > 0:
-                    #
-                    # @TODO: Upgrade to mongodb 4.0+ and remove the line below 
-                    # Upon upgrade use the operator "$toString" in export.init function
-                    #
-                    rows = [dict(item,**{"_id":str(item["_id"])}) for item in rows]
-                    _df = pd.DataFrame(rows)                    
-                    writer.write(_df)
-                index += 1
-                # for _e in rows :
-                    # writer.write(_e) 
-
-        
-            self.status  = 1            
-        else:
-            print ("No data was passed")
-        
-            
-        writer.close()
-
-#_args = {"type":"mongo.MongoReader","args":{"db":"parserio","doc":"logs"}} 
-#reader = Reader()
-#reader.init(store = _args,pipeline={"distinct":"claims","key":"name"})
-#reader._apply()
-#print (reader.get())
-#for row in reader.get() :
-#    print (row)

+ 1 - 1
healthcareio/healthcare-io.py

@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+                                                                                                                                                        #!/usr/bin/env python3
 """
 (c) 2019 Claims Toolkit, 
 Health Information Privacy Lab, Vanderbilt University Medical Center

+ 38 - 0
healthcareio/logger.py

@@ -0,0 +1,38 @@
+from datetime import datetime
+import transport
+import copy
+import json
+import pandas as pd
+class X12Logger :
+    def __init__(self,**_args) :
+        self._store = copy.deepcopy(_args['store'])
+        self._store['table'] = 'logs'
+        self._store['context'] = 'write'
+
+    def log(self,**_args):
+        _date = datetime.now()
+        _info = {'date':'-'.join([str(_date.month),str(_date.day),str(_date.year)])}
+        for key in ['module','action','data'] :
+            value = 'NA' if key not in _args else _args[key]
+            value = value if type(value) not in [dict,list] else json.dumps(value)
+            _info[key] = value
+            
+            # print ([key, type(value) in [dict,list], type(value)])
+        #
+        # Storing the whole thing
+        try:
+            _xwriter = transport.factory.instance(**self._store)
+            # if not _xwriter.has(table=self._store['table']) :
+            #     table = self._store['table']
+            #     sql = f'''CREATE TABLE {table} (date date,module char(255), data JSON)'''
+            #     print (sql)
+            #     _xwriter.apply(sql)
+            _df = pd.DataFrame([_info])
+            
+            _xwriter.write(_df)
+            if hasattr(_xwriter,'close') :
+                _xwriter.close()
+        except Exception as e:
+            
+            print ([e])
+            pass

+ 0 - 239
healthcareio/server/__init__.py

@@ -1,239 +0,0 @@
-from flask import Flask, request,render_template, send_from_directory
-from healthcareio.params import SYS_ARGS
-import healthcareio.analytics
-import os
-import json
-import time
-# import smart
-import transport
-import pandas as pd
-import numpy as np
-from healthcareio import x12
-from healthcareio.export import export
-from multiprocessing import Process
-# from flask_socketio import SocketIO, emit, disconnect,send
-from healthcareio.server import proxy
-PATH = os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])
-app = Flask(__name__)
-# socket_ = SocketIO(app)
-
-def resume (files):
-    _args = SYS_ARGS['config']['store'].copy()
-    if 'mongo' in SYS_ARGS['config']['store']['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
-    
-    return list(set(files) - set(_files))
-        
-    
-
-def run ():
-    #
-    # let's get the files in the folder (perhaps recursively traverse them)
-    #
-    FILES = []
-    BATCH = int(SYS_ARGS['config']['args']['batch']) #-- number of processes (poorly named variable)
-
-    for root,_dir,f in os.walk(SYS_ARGS['config']['args']['folder']) :
-        if f :
-            FILES += [os.sep.join([root,name]) for name in f]
-    FILES = resume(FILES)
-    FILES = np.array_split(FILES,BATCH)
-    procs = []
-    for FILE_GROUP in FILES :
-        
-        FILE_GROUP = FILE_GROUP.tolist()
-        # logger.write({"process":index,"parse":SYS_ARGS['parse'],"file_count":len(row)})
-        # proc = Process(target=apply,args=(row,info['store'],_info,))
-        parser = x12.Parser(PATH) #os.sep.join([PATH,'config.json']))
-        parser.set.files(FILE_GROUP)
-        parser.start()
-        procs.append(parser)  
-    SYS_ARGS['procs']  = procs
-# @socket_.on('data',namespace='/stream')
-def push() :
-    _args = dict(SYS_ARGS['config']['store'].copy(),**{"type":"mongo.MongoReader"})
-    reader = transport.factory.instance(**_args)
-    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 = healthcareio.analytics.Apex.apply({"chart":{"type":"donut","axis":{"x":"count","y":"type"}},"data":r})
-    # emit("update",r,json=True)
-    return r
-# @socket_.on('connect')
-# def client_connect(**r):
-#     print ('Connection received')
-#     print (r)
-#     push()
-#     pass
-    
-@app.route("/favicon.ico")
-def _icon():
-    return send_from_directory(os.path.join([app.root_path, 'static','img','logo.svg']),
-                               'favicon.ico', mimetype='image/vnd.microsoft.icon')
-@app.route("/")
-def init():
-    e = SYS_ARGS['engine']
-    sections = {"remits":e.info['835'],"claims":e.info['837']}
-    _args = {"sections":sections,"store":SYS_ARGS["config"]["store"],"owner":SYS_ARGS['config']['owner'],"args":SYS_ARGS["config"]["args"]}
-    return render_template("index.html",**_args)
-@app.route("/format/<id>/<index>",methods=['POST'])
-def _format(id,index):
-    
-    e = SYS_ARGS['engine']
-    key = '837' if id == 'claims' else '835'
-    index = int(index)
-    # p = e.info[key][index]
-    p = e.filter(type=id,index=index)
-   
-    r = []
-    for item in p['pipeline'] :
-        _item= dict(item)       
-        _item = dict(_item,**healthcareio.analytics.Apex.apply(item))
-        del _item['data']
-        if 'apex' in _item or 'html' in _item:
-            r.append(_item)
-        
-        
-    r = {"id":p['id'],"pipeline":r}  
-    return json.dumps(r),200
-
-@app.route("/get/<id>/<index>",methods=['GET'])
-def get(id,index):
-    e = SYS_ARGS['engine']
-    key = '837' if id == 'claims' else '835'
-    index = int(index)
-    # p = e.info[key][index]
-    p = e.filter(type=id,index=index)
-    r = {}
-    for item in p[0]['pipeline'] :
-        
-        _item= [dict(item)]
-        
-        # r[item['label']] = item['data'].to_dict(orient='record')
-        r[item['label']] = item['data'].to_dict('record')
-    return json.dumps(r),200
-
-@app.route("/reset",methods=["POST"])
-def reset():
-    return "1",200
-@app.route("/data",methods=['GET'])
-def get_data ():
-    """
-    This function will return statistical data about the services i.e general statistics about what has/been processed
-    """
-    HEADER = {"Content-type":"application/json"}
-    _args = SYS_ARGS['config']
-    options = dict(proxy.get.files(_args),**proxy.get.processes(_args))
-    return json.dumps(options),HEADER
-@app.route("/log/<id>",methods=["POST","PUT","GET"])
-def log(id) :
-    HEADER = {"Content-Type":"application/json; charset=utf8"}
-    if id == 'params' and request.method in ['PUT', 'POST']:
-        info = request.json
-        _args = {"batch":info['batch'] if 'batch' in info else 1,"resume":True}
-        #
-        # We should update the configuration 
-        SYS_ARGS['config']['args'] = _args
-        PATH = os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])
-        write = lambda content: (open(PATH,'w')).write(json.dumps(content))
-        proc = Process(target=write,args=(SYS_ARGS['config'],))
-        proc.start()
-        return "1",HEADER
-    pass
-@app.route("/io/<id>",methods=['POST'])
-def io_data(id):
-    if id == 'params' :
-        _args = request.json
-        #
-        # Expecting batch,folder as parameters
-        _args = request.json
-        _args['resume'] = True
-        print (_args)
-        #
-        # We should update the configuration 
-        SYS_ARGS['config']['args'] = _args
-        # PATH = os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])
-        try:
-            write = lambda content: (open(PATH,'w')).write(json.dumps(content))
-            proc = Process(target=write,args=(SYS_ARGS['config'],))
-            proc.start()
-            # proc.join()
-            return "1",200
-        except Exception as e :
-            return "0",403
-            pass
-    elif id == 'stop' :
-        stop()
-        pass
-    elif id == 'run' :
-        # run()
-        _args = {"args":SYS_ARGS['config']['args'],"store":SYS_ARGS["config"]["store"]}
-        proxy.run(_args)
-        return "1",200
-        pass
-        
-@app.route("/export")
-def export_form():
-    _args = {"context":SYS_ARGS['context']}
-    return render_template("store.html",**_args)
-@app.route("/export",methods=['POST','PUT'])
-def apply_etl():
-    _info = request.json
-    m = {'s3':'s3.s3Writer','mongo':'mongo.MongoWriter'}
-    if _info :
-        dest_args = {'type':m[_info['type']],"args": _info['content'] }
-        src_args = SYS_ARGS['config']['store']
-        # print (_args)
-        # writer = transport.factory.instance(**_args)
-        proxy.publish(src_args,dest_args)
-        return "1",405
-        
-    else:
-        return "0",404
-@app.route("/update")
-def update():
-    pass
-    return "0",405
-@app.route("/reload",methods=['POST'])
-def reload():
-    # e = SYS_ARGS['engine']
-    # e.apply()
-    PATH= SYS_ARGS['config'] if 'config' in SYS_ARGS else os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])    
-    e = healthcareio.analytics.engine(PATH)
-    # e.apply()
-    SYS_ARGS['engine'] = e
-
-    return "1",200
-
-    
-if __name__ == '__main__' :
-    PORT = int(SYS_ARGS['port']) if 'port' in SYS_ARGS else 5500
-    DEBUG= int(SYS_ARGS['debug']) if 'debug' in SYS_ARGS else 0
-    SYS_ARGS['context'] = SYS_ARGS['context'] if 'context' in SYS_ARGS else ''
-
-    #
-    # 
-    PATH= SYS_ARGS['config'] if 'config' in SYS_ARGS else os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])
-    #
-    # Adjusting configuration with parameters (batch,folder,resume)
-    SYS_ARGS['config'] = json.loads(open(PATH).read())
-    if 'args' not in SYS_ARGS['config'] :
-        SYS_ARGS['config']["args"] = {"batch":1,"resume":True,"folder":"/data"}
-    
-    SYS_ARGS['procs'] = []
-    
-    
-    # SYS_ARGS['path'] = os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])
-    e = healthcareio.analytics.engine(PATH)
-    e.apply(type='claims',serialize=False)
-    SYS_ARGS['engine'] = e
-    app.run(host='0.0.0.0',port=PORT,debug=DEBUG,threaded=True)

+ 0 - 89
healthcareio/server/index.py

@@ -1,89 +0,0 @@
-from flask import Flask, request,render_template, send_from_directory
-from healthcareio.params import SYS_ARGS
-import healthcareio.analytics
-import os
-import json
-app = Flask(__name__)
-@app.route("/favicon.ico")
-def _icon():
-    return send_from_directory(os.path.join([app.root_path, 'static/img/logo.svg']),
-                               'favicon.ico', mimetype='image/vnd.microsoft.icon')
-@app.route("/")
-def init():
-    e = SYS_ARGS['engine']
-    sections = {"remits":e.info['835'],"claims":e.info['837']}
-    
-    _args = {"sections":sections,"store":SYS_ARGS['config']['store'],'args':{'batch':5}}
-    
-    return render_template("index.html",**_args)
-@app.route("/format/<id>/<index>",methods=['POST'])
-def _format(id,index):
-    
-    e = SYS_ARGS['engine']
-    key = '837' if id == 'claims' else '835'
-    index = int(index)
-    # p = e.info[key][index]
-    p = e.apply(type=id,index=index)
-    
-    #
-    r = []
-    for item in p[0]['pipeline'] :
-        _item= dict(item)
-        del _item['sql']
-        del _item ['data']
-        
-        _item = dict(_item,**healthcareio.analytics.Apex.apply(item))
-        if 'apex' in _item or 'html' in _item:
-            r.append(_item)
-        
-    r = {"id":p[0]['id'],"pipeline":r}
-    return json.dumps(r),200
-@app.route("/get/<id>/<index>",methods=['GET'])
-def get(id,index):
-    e = SYS_ARGS['engine']
-    key = '837' if id == 'claims' else '835'
-    index = int(index)
-    # p = e.info[key][index]
-    p = e.apply(type=id,index=index)
-    r = {}
-    for item in p[0]['pipeline'] :
-        
-        _item= [dict(item)]
-        
-        r[item['label']] = item['data'].to_dict(orient='record')
-        # del _item['sql']
-        # del _item ['data']
-    #     print (item['label'])
-    #     _item['apex'] = healthcareio.analytics.Apex.apply(item)
-    #     if _item['apex']:
-    #         r.append(_item)
-        
-    # r = {"id":p[0]['id'],"pipeline":r}
-    return json.dumps(r),200
-@app.route("/reload",methods=['POST'])
-def reload():
-    # e = SYS_ARGS['engine']
-    # e.apply()
-    PATH= SYS_ARGS['config'] if 'config' in SYS_ARGS else os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])    
-    e = healthcareio.analytics.engine(PATH)
-    # e.apply()
-    SYS_ARGS['engine'] = e
-
-    return "1",200
-
-    
-if __name__ == '__main__' :
-    PORT = int(SYS_ARGS['port']) if 'port' in SYS_ARGS else 5500
-    DEBUG= int(SYS_ARGS['debug']) if 'debug' in SYS_ARGS else 1
-    SYS_ARGS['context'] = SYS_ARGS['context'] if 'context' in SYS_ARGS else ''
-    #
-    # 
-    PATH= SYS_ARGS['config'] if 'config' in SYS_ARGS else os.sep.join([os.environ['HOME'],'.healthcareio','config.json'])
-    #
-    if os.path.exists(PATH) :
-        SYS_ARGS['config'] = json.loads(open(PATH).read())
-    e = healthcareio.analytics.engine(PATH)
-    # e.apply(type='claims',serialize=True)
-    SYS_ARGS['engine'] = e
-    
-    app.run(host='0.0.0.0',port=PORT,debug=DEBUG,threaded=True)

文件差異過大導致無法顯示
+ 0 - 469
healthcareio/server/out.html


+ 0 - 168
healthcareio/server/proxy.py

@@ -1,168 +0,0 @@
-"""
-    This file serves as proxy to healthcare-io, it will be embedded into the API
-"""
-import os
-import transport
-import numpy as np
-from healthcareio import x12
-import pandas as pd
-import smart
-from healthcareio.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 ( [len(list(set(files) - set(_files))),' files to be processed'])
-        return list(set(files) - set(_files))
-
-    @staticmethod
-    def processes(_args):
-        APP_NAME ='healthcare-io'
-        _info = smart.top.read(name=APP_NAME) #pd.DataFrame(smart.top.read(name='healthcare-io'))[['name','cpu','mem']]
-        
-        
-        if _info.shape[0] == 0 :
-            _info = pd.DataFrame({"name":[APP_NAME],"cpu":[0],"mem":[0]})
-        # _info = pd.DataFrame(_info.groupby(['name']).sum())
-        # _info['name'] = ['healthcare-io.py']
-        m = {'cpu':'CPU','mem':'RAM','name':'name'}
-        _info  = _info.rename(columns=m)
-        # _info.columns = [m[name] for name in _info.columns.tolist() if name in m]
-        _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']
-                )
-        
-        return {"process":{"chart":charts,"counts":_info.shape[0]}}
-    @staticmethod
-    def files (_args):
-        folder = _args['args']['folder']
-        _info = smart.folder.read(path=folder)
-        
-        N = _info.files.tolist()[0]
-        store_args = _args['store'].copy()
-        store_args['context'] = 'read'
-        
-        # if 'mongo' in _args['store']['type'] :
-        if _args['store']['provider'] in ['mongo', 'mongodb']:
-            # 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'
-            store_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
-        #
-        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 - 37
healthcareio/server/static/css/borders.css

@@ -1,37 +0,0 @@
-.border {
-    border:1px solid #CAD5E0 ;    
-}
-.border-round {
-    padding:6px;
-    border-radius:8px;
-}
-
-.border-round-top-left{
-    border-top-left-radius: 8px;    
-    padding:6px;
-}
-.border-round-top-right{
-    border-top-right-radius: 8px;
-    padding:6px;
-}
-.border-round-bottom-right{
-    border-bottom-right-radius: 8px;
-    padding:6px;
-}
-.border-round-bottom-left{
-    border-bottom-left-radius: 8px;
-    padding:6px;
-}
-
-
-.border-right{
-    border-right:1px solid #CAD5E0;
-}
-
-.border-left{
-    border-left:1px solid #CAD5E0;
-}
-
-
-.border-bottom { border-bottom:1px solid #CAD5E0}
-.border-top { border-top:1px solid #CAD5E0}

+ 0 - 57
healthcareio/server/static/css/default.css

@@ -1,57 +0,0 @@
-
-.active {
-    padding:4px;
-    cursor:pointer;
-    border-bottom:2px solid transparent ;
-}
-.active:hover{
-    border-bottom:2px solid #ff6500; 
-}
-input[type=text]{
-    border:1px solid transparent;
-    background-color:#f3f3f3;
-    outline: 0px;
-    padding:8px;
-    font-weight:normal;
-    font-family:sans-serif;
-    color:black;
-}
-.active-button {
-    display:grid;
-    grid-template-columns: 32px auto;
-    gap:2px;
-    align-items:center;
-    border:2px solid #CAD5E0;
-    cursor:pointer;
-    
-}
-.active-button i {padding:4px;;}
-.active-button:hover { border-color:#ff6500}
-
-.system {display:grid; grid-template-columns: 45% 1px auto; gap:20px;  margin-left:5%; width:90%;}
-.system .status .item {display:grid; grid-template-columns: 75px 8px auto; gap:2px;}
-.input-form {display:grid; gap:2px;}
-.input-form .item {display:grid; grid-template-columns: 125px auto; gap:2px; align-items:center;}
-.input-form .item .label { font-weight:bold; padding-left:10px}
-.fa-cog {color:#4682B4}
-.fa-check {color:#00c6b3}
-.fa-times {color:maroon}
-
-.code {
-    margin:4px;
-    background:#000000 ;
-    padding:8px;
-    font-family: 'Courier New', Courier, monospace;
-    color:#d3d3d3;
-    font-size:12px;
-    line-height: 2;
-}
-
-.tabs {display:grid; grid-template-columns: repeat(3,1fr) auto; gap:0px; align-items:center; text-align: center;}
-.tab {border:1px solid transparent; border-bottom-color:#D3D3D3; font-weight:bold; padding:4px}
-
-.tabs .selected {border-color:#CAD5E0; border-bottom-color:transparent; }
-.system iframe {width:100%; height:100%; border:1px solid transparent;}
-.data-info {height:90%; padding:8px;}
-.fa-cloud {color:#4682B4}
-.fa-database{color:#cc8c91}

+ 0 - 34
healthcareio/server/static/css/fa/LICENSE.txt

@@ -1,34 +0,0 @@
-Font Awesome Free License
--------------------------
-
-Font Awesome Free is free, open source, and GPL friendly. You can use it for
-commercial projects, open source projects, or really almost whatever you want.
-Full Font Awesome Free license: https://fontawesome.com/license/free.
-
-# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
-In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
-packaged as SVG and JS file types.
-
-# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
-In the Font Awesome Free download, the SIL OFL license applies to all icons
-packaged as web and desktop font files.
-
-# Code: MIT License (https://opensource.org/licenses/MIT)
-In the Font Awesome Free download, the MIT license applies to all non-font and
-non-icon files.
-
-# Attribution
-Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
-Awesome Free files already contain embedded comments with sufficient
-attribution, so you shouldn't need to do anything additional when using these
-files normally.
-
-We've kept attribution comments terse, so we ask that you do not actively work
-to remove them from files, especially code. They're a great way for folks to
-learn about Font Awesome.
-
-# Brand Icons
-All brand icons are trademarks of their respective owners. The use of these
-trademarks does not indicate endorsement of the trademark holder by Font
-Awesome, nor vice versa. **Please do not use brand logos for any purpose except
-to represent the company, product, or service to which they refer.**

文件差異過大導致無法顯示
+ 0 - 4556
healthcareio/server/static/css/fa/css/all.css


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/css/all.min.css


+ 0 - 15
healthcareio/server/static/css/fa/css/brands.css

@@ -1,15 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@font-face {
-  font-family: 'Font Awesome 5 Brands';
-  font-style: normal;
-  font-weight: 400;
-  font-display: block;
-  src: url("../webfonts/fa-brands-400.eot");
-  src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); }
-
-.fab {
-  font-family: 'Font Awesome 5 Brands';
-  font-weight: 400; }

+ 0 - 5
healthcareio/server/static/css/fa/css/brands.min.css

@@ -1,5 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands";font-weight:400}

文件差異過大導致無法顯示
+ 0 - 4522
healthcareio/server/static/css/fa/css/fontawesome.css


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/css/fontawesome.min.css


+ 0 - 15
healthcareio/server/static/css/fa/css/regular.css

@@ -1,15 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@font-face {
-  font-family: 'Font Awesome 5 Free';
-  font-style: normal;
-  font-weight: 400;
-  font-display: block;
-  src: url("../webfonts/fa-regular-400.eot");
-  src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); }
-
-.far {
-  font-family: 'Font Awesome 5 Free';
-  font-weight: 400; }

+ 0 - 5
healthcareio/server/static/css/fa/css/regular.min.css

@@ -1,5 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Free";font-weight:400}

+ 0 - 16
healthcareio/server/static/css/fa/css/solid.css

@@ -1,16 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@font-face {
-  font-family: 'Font Awesome 5 Free';
-  font-style: normal;
-  font-weight: 900;
-  font-display: block;
-  src: url("../webfonts/fa-solid-900.eot");
-  src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); }
-
-.fa,
-.fas {
-  font-family: 'Font Awesome 5 Free';
-  font-weight: 900; }

+ 0 - 5
healthcareio/server/static/css/fa/css/solid.min.css

@@ -1,5 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900}

+ 0 - 371
healthcareio/server/static/css/fa/css/svg-with-js.css

@@ -1,371 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-svg:not(:root).svg-inline--fa {
-  overflow: visible; }
-
-.svg-inline--fa {
-  display: inline-block;
-  font-size: inherit;
-  height: 1em;
-  overflow: visible;
-  vertical-align: -.125em; }
-  .svg-inline--fa.fa-lg {
-    vertical-align: -.225em; }
-  .svg-inline--fa.fa-w-1 {
-    width: 0.0625em; }
-  .svg-inline--fa.fa-w-2 {
-    width: 0.125em; }
-  .svg-inline--fa.fa-w-3 {
-    width: 0.1875em; }
-  .svg-inline--fa.fa-w-4 {
-    width: 0.25em; }
-  .svg-inline--fa.fa-w-5 {
-    width: 0.3125em; }
-  .svg-inline--fa.fa-w-6 {
-    width: 0.375em; }
-  .svg-inline--fa.fa-w-7 {
-    width: 0.4375em; }
-  .svg-inline--fa.fa-w-8 {
-    width: 0.5em; }
-  .svg-inline--fa.fa-w-9 {
-    width: 0.5625em; }
-  .svg-inline--fa.fa-w-10 {
-    width: 0.625em; }
-  .svg-inline--fa.fa-w-11 {
-    width: 0.6875em; }
-  .svg-inline--fa.fa-w-12 {
-    width: 0.75em; }
-  .svg-inline--fa.fa-w-13 {
-    width: 0.8125em; }
-  .svg-inline--fa.fa-w-14 {
-    width: 0.875em; }
-  .svg-inline--fa.fa-w-15 {
-    width: 0.9375em; }
-  .svg-inline--fa.fa-w-16 {
-    width: 1em; }
-  .svg-inline--fa.fa-w-17 {
-    width: 1.0625em; }
-  .svg-inline--fa.fa-w-18 {
-    width: 1.125em; }
-  .svg-inline--fa.fa-w-19 {
-    width: 1.1875em; }
-  .svg-inline--fa.fa-w-20 {
-    width: 1.25em; }
-  .svg-inline--fa.fa-pull-left {
-    margin-right: .3em;
-    width: auto; }
-  .svg-inline--fa.fa-pull-right {
-    margin-left: .3em;
-    width: auto; }
-  .svg-inline--fa.fa-border {
-    height: 1.5em; }
-  .svg-inline--fa.fa-li {
-    width: 2em; }
-  .svg-inline--fa.fa-fw {
-    width: 1.25em; }
-
-.fa-layers svg.svg-inline--fa {
-  bottom: 0;
-  left: 0;
-  margin: auto;
-  position: absolute;
-  right: 0;
-  top: 0; }
-
-.fa-layers {
-  display: inline-block;
-  height: 1em;
-  position: relative;
-  text-align: center;
-  vertical-align: -.125em;
-  width: 1em; }
-  .fa-layers svg.svg-inline--fa {
-    -webkit-transform-origin: center center;
-            transform-origin: center center; }
-
-.fa-layers-text, .fa-layers-counter {
-  display: inline-block;
-  position: absolute;
-  text-align: center; }
-
-.fa-layers-text {
-  left: 50%;
-  top: 50%;
-  -webkit-transform: translate(-50%, -50%);
-          transform: translate(-50%, -50%);
-  -webkit-transform-origin: center center;
-          transform-origin: center center; }
-
-.fa-layers-counter {
-  background-color: #ff253a;
-  border-radius: 1em;
-  -webkit-box-sizing: border-box;
-          box-sizing: border-box;
-  color: #fff;
-  height: 1.5em;
-  line-height: 1;
-  max-width: 5em;
-  min-width: 1.5em;
-  overflow: hidden;
-  padding: .25em;
-  right: 0;
-  text-overflow: ellipsis;
-  top: 0;
-  -webkit-transform: scale(0.25);
-          transform: scale(0.25);
-  -webkit-transform-origin: top right;
-          transform-origin: top right; }
-
-.fa-layers-bottom-right {
-  bottom: 0;
-  right: 0;
-  top: auto;
-  -webkit-transform: scale(0.25);
-          transform: scale(0.25);
-  -webkit-transform-origin: bottom right;
-          transform-origin: bottom right; }
-
-.fa-layers-bottom-left {
-  bottom: 0;
-  left: 0;
-  right: auto;
-  top: auto;
-  -webkit-transform: scale(0.25);
-          transform: scale(0.25);
-  -webkit-transform-origin: bottom left;
-          transform-origin: bottom left; }
-
-.fa-layers-top-right {
-  right: 0;
-  top: 0;
-  -webkit-transform: scale(0.25);
-          transform: scale(0.25);
-  -webkit-transform-origin: top right;
-          transform-origin: top right; }
-
-.fa-layers-top-left {
-  left: 0;
-  right: auto;
-  top: 0;
-  -webkit-transform: scale(0.25);
-          transform: scale(0.25);
-  -webkit-transform-origin: top left;
-          transform-origin: top left; }
-
-.fa-lg {
-  font-size: 1.33333em;
-  line-height: 0.75em;
-  vertical-align: -.0667em; }
-
-.fa-xs {
-  font-size: .75em; }
-
-.fa-sm {
-  font-size: .875em; }
-
-.fa-1x {
-  font-size: 1em; }
-
-.fa-2x {
-  font-size: 2em; }
-
-.fa-3x {
-  font-size: 3em; }
-
-.fa-4x {
-  font-size: 4em; }
-
-.fa-5x {
-  font-size: 5em; }
-
-.fa-6x {
-  font-size: 6em; }
-
-.fa-7x {
-  font-size: 7em; }
-
-.fa-8x {
-  font-size: 8em; }
-
-.fa-9x {
-  font-size: 9em; }
-
-.fa-10x {
-  font-size: 10em; }
-
-.fa-fw {
-  text-align: center;
-  width: 1.25em; }
-
-.fa-ul {
-  list-style-type: none;
-  margin-left: 2.5em;
-  padding-left: 0; }
-  .fa-ul > li {
-    position: relative; }
-
-.fa-li {
-  left: -2em;
-  position: absolute;
-  text-align: center;
-  width: 2em;
-  line-height: inherit; }
-
-.fa-border {
-  border: solid 0.08em #eee;
-  border-radius: .1em;
-  padding: .2em .25em .15em; }
-
-.fa-pull-left {
-  float: left; }
-
-.fa-pull-right {
-  float: right; }
-
-.fa.fa-pull-left,
-.fas.fa-pull-left,
-.far.fa-pull-left,
-.fal.fa-pull-left,
-.fab.fa-pull-left {
-  margin-right: .3em; }
-
-.fa.fa-pull-right,
-.fas.fa-pull-right,
-.far.fa-pull-right,
-.fal.fa-pull-right,
-.fab.fa-pull-right {
-  margin-left: .3em; }
-
-.fa-spin {
-  -webkit-animation: fa-spin 2s infinite linear;
-          animation: fa-spin 2s infinite linear; }
-
-.fa-pulse {
-  -webkit-animation: fa-spin 1s infinite steps(8);
-          animation: fa-spin 1s infinite steps(8); }
-
-@-webkit-keyframes fa-spin {
-  0% {
-    -webkit-transform: rotate(0deg);
-            transform: rotate(0deg); }
-  100% {
-    -webkit-transform: rotate(360deg);
-            transform: rotate(360deg); } }
-
-@keyframes fa-spin {
-  0% {
-    -webkit-transform: rotate(0deg);
-            transform: rotate(0deg); }
-  100% {
-    -webkit-transform: rotate(360deg);
-            transform: rotate(360deg); } }
-
-.fa-rotate-90 {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-  -webkit-transform: rotate(90deg);
-          transform: rotate(90deg); }
-
-.fa-rotate-180 {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-  -webkit-transform: rotate(180deg);
-          transform: rotate(180deg); }
-
-.fa-rotate-270 {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-  -webkit-transform: rotate(270deg);
-          transform: rotate(270deg); }
-
-.fa-flip-horizontal {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-  -webkit-transform: scale(-1, 1);
-          transform: scale(-1, 1); }
-
-.fa-flip-vertical {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-  -webkit-transform: scale(1, -1);
-          transform: scale(1, -1); }
-
-.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-  -webkit-transform: scale(-1, -1);
-          transform: scale(-1, -1); }
-
-:root .fa-rotate-90,
-:root .fa-rotate-180,
-:root .fa-rotate-270,
-:root .fa-flip-horizontal,
-:root .fa-flip-vertical,
-:root .fa-flip-both {
-  -webkit-filter: none;
-          filter: none; }
-
-.fa-stack {
-  display: inline-block;
-  height: 2em;
-  position: relative;
-  width: 2.5em; }
-
-.fa-stack-1x,
-.fa-stack-2x {
-  bottom: 0;
-  left: 0;
-  margin: auto;
-  position: absolute;
-  right: 0;
-  top: 0; }
-
-.svg-inline--fa.fa-stack-1x {
-  height: 1em;
-  width: 1.25em; }
-
-.svg-inline--fa.fa-stack-2x {
-  height: 2em;
-  width: 2.5em; }
-
-.fa-inverse {
-  color: #fff; }
-
-.sr-only {
-  border: 0;
-  clip: rect(0, 0, 0, 0);
-  height: 1px;
-  margin: -1px;
-  overflow: hidden;
-  padding: 0;
-  position: absolute;
-  width: 1px; }
-
-.sr-only-focusable:active, .sr-only-focusable:focus {
-  clip: auto;
-  height: auto;
-  margin: 0;
-  overflow: visible;
-  position: static;
-  width: auto; }
-
-.svg-inline--fa .fa-primary {
-  fill: var(--fa-primary-color, currentColor);
-  opacity: 1;
-  opacity: var(--fa-primary-opacity, 1); }
-
-.svg-inline--fa .fa-secondary {
-  fill: var(--fa-secondary-color, currentColor);
-  opacity: 0.4;
-  opacity: var(--fa-secondary-opacity, 0.4); }
-
-.svg-inline--fa.fa-swap-opacity .fa-primary {
-  opacity: 0.4;
-  opacity: var(--fa-secondary-opacity, 0.4); }
-
-.svg-inline--fa.fa-swap-opacity .fa-secondary {
-  opacity: 1;
-  opacity: var(--fa-primary-opacity, 1); }
-
-.svg-inline--fa mask .fa-primary,
-.svg-inline--fa mask .fa-secondary {
-  fill: black; }
-
-.fad.fa-inverse {
-  color: #fff; }

文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/css/svg-with-js.min.css


文件差異過大導致無法顯示
+ 0 - 2172
healthcareio/server/static/css/fa/css/v4-shims.css


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/css/v4-shims.min.css


文件差異過大導致無法顯示
+ 0 - 4441
healthcareio/server/static/css/fa/js/all.js


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/js/all.min.js


文件差異過大導致無法顯示
+ 0 - 571
healthcareio/server/static/css/fa/js/brands.js


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/js/brands.min.js


文件差異過大導致無法顯示
+ 0 - 998
healthcareio/server/static/css/fa/js/conflict-detection.js


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/js/conflict-detection.min.js


文件差異過大導致無法顯示
+ 0 - 2478
healthcareio/server/static/css/fa/js/fontawesome.js


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/js/fontawesome.min.js


文件差異過大導致無法顯示
+ 0 - 280
healthcareio/server/static/css/fa/js/regular.js


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/js/regular.min.js


文件差異過大導致無法顯示
+ 0 - 1124
healthcareio/server/static/css/fa/js/solid.js


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/js/solid.min.js


文件差異過大導致無法顯示
+ 0 - 68
healthcareio/server/static/css/fa/js/v4-shims.js


文件差異過大導致無法顯示
+ 0 - 5
healthcareio/server/static/css/fa/js/v4-shims.min.js


+ 0 - 19
healthcareio/server/static/css/fa/less/_animated.less

@@ -1,19 +0,0 @@
-// Animated Icons
-// --------------------------
-
-.@{fa-css-prefix}-spin {
-  animation: fa-spin 2s infinite linear;
-}
-
-.@{fa-css-prefix}-pulse {
-  animation: fa-spin 1s infinite steps(8);
-}
-
-@keyframes fa-spin {
-  0% {
-    transform: rotate(0deg);
-  }
-  100% {
-    transform: rotate(360deg);
-  }
-}

+ 0 - 16
healthcareio/server/static/css/fa/less/_bordered-pulled.less

@@ -1,16 +0,0 @@
-// Bordered & Pulled
-// -------------------------
-
-.@{fa-css-prefix}-border {
-  border-radius: .1em;
-  border: solid .08em @fa-border-color;
-  padding: .2em .25em .15em;
-}
-
-.@{fa-css-prefix}-pull-left { float: left; }
-.@{fa-css-prefix}-pull-right { float: right; }
-
-.@{fa-css-prefix}, .fas, .far, .fal, .fab {
-  &.@{fa-css-prefix}-pull-left { margin-right: .3em; }
-  &.@{fa-css-prefix}-pull-right { margin-left: .3em; }
-}

+ 0 - 12
healthcareio/server/static/css/fa/less/_core.less

@@ -1,12 +0,0 @@
-// Base Class Definition
-// -------------------------
-
-.@{fa-css-prefix}, .fas, .far, .fal, .fad, .fab {
-  -moz-osx-font-smoothing: grayscale;
-  -webkit-font-smoothing: antialiased;
-  display: inline-block;
-  font-style: normal;
-  font-variant: normal;
-  text-rendering: auto;
-  line-height: 1;
-}

+ 0 - 6
healthcareio/server/static/css/fa/less/_fixed-width.less

@@ -1,6 +0,0 @@
-// Fixed Width Icons
-// -------------------------
-.@{fa-css-prefix}-fw {
-  text-align: center;
-  width: (20em / 16);
-}

文件差異過大導致無法顯示
+ 0 - 1441
healthcareio/server/static/css/fa/less/_icons.less


+ 0 - 27
healthcareio/server/static/css/fa/less/_larger.less

@@ -1,27 +0,0 @@
-// Icon Sizes
-// -------------------------
-
-.larger(@factor) when (@factor > 0) {
-  .larger((@factor - 1));
-
-  .@{fa-css-prefix}-@{factor}x {
-    font-size: (@factor * 1em);
-  }
-}
-
-/* makes the font 33% larger relative to the icon container */
-.@{fa-css-prefix}-lg {
-  font-size: (4em / 3);
-  line-height: (3em / 4);
-  vertical-align: -.0667em;
-}
-
-.@{fa-css-prefix}-xs {
-  font-size: .75em;
-}
-
-.@{fa-css-prefix}-sm {
-  font-size: .875em;
-}
-
-.larger(10);

+ 0 - 18
healthcareio/server/static/css/fa/less/_list.less

@@ -1,18 +0,0 @@
-// List Icons
-// -------------------------
-
-.@{fa-css-prefix}-ul {
-  list-style-type: none;
-  margin-left: (@fa-li-width * 5/4);
-  padding-left: 0;
-
-  > li { position: relative; }
-}
-
-.@{fa-css-prefix}-li {
-  left: -@fa-li-width;
-  position: absolute;
-  text-align: center;
-  width: @fa-li-width;
-  line-height: inherit;
-}

+ 0 - 56
healthcareio/server/static/css/fa/less/_mixins.less

@@ -1,56 +0,0 @@
-// Mixins
-// --------------------------
-
-.fa-icon() {
-  -moz-osx-font-smoothing: grayscale;
-  -webkit-font-smoothing: antialiased;
-  display: inline-block;
-  font-style: normal;
-  font-variant: normal;
-  font-weight: normal;
-  line-height: 1;
-}
-
-.fa-icon-rotate(@degrees, @rotation) {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})";
-  transform: rotate(@degrees);
-}
-
-.fa-icon-flip(@horiz, @vert, @rotation) {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)";
-  transform: scale(@horiz, @vert);
-}
-
-
-// Only display content to screen readers. A la Bootstrap 4.
-//
-// See: http://a11yproject.com/posts/how-to-hide-content/
-
-.sr-only() {
-  border: 0;
-  clip: rect(0,0,0,0);
-  height: 1px;
-  margin: -1px;
-  overflow: hidden;
-  padding: 0;
-  position: absolute;
-  width: 1px;
-}
-
-// Use in conjunction with .sr-only to only display content when it's focused.
-//
-// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
-//
-// Credit: HTML5 Boilerplate
-
-.sr-only-focusable() {
-  &:active,
-  &:focus {
-    clip: auto;
-    height: auto;
-    margin: 0;
-    overflow: visible;
-    position: static;
-    width: auto;
-  }
-}

+ 0 - 24
healthcareio/server/static/css/fa/less/_rotated-flipped.less

@@ -1,24 +0,0 @@
-// Rotated & Flipped Icons
-// -------------------------
-
-.@{fa-css-prefix}-rotate-90  { .fa-icon-rotate(90deg, 1);  }
-.@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); }
-.@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); }
-
-.@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); }
-.@{fa-css-prefix}-flip-vertical   { .fa-icon-flip(1, -1, 2); }
-.@{fa-css-prefix}-flip-both, .@{fa-css-prefix}-flip-horizontal.@{fa-css-prefix}-flip-vertical { .fa-icon-flip(-1, -1, 2); }
-
-// Hook for IE8-9
-// -------------------------
-
-:root {
-  .@{fa-css-prefix}-rotate-90,
-  .@{fa-css-prefix}-rotate-180,
-  .@{fa-css-prefix}-rotate-270,
-  .@{fa-css-prefix}-flip-horizontal,
-  .@{fa-css-prefix}-flip-vertical,
-  .@{fa-css-prefix}-flip-both {
-    filter: none;
-  }
-}

+ 0 - 5
healthcareio/server/static/css/fa/less/_screen-reader.less

@@ -1,5 +0,0 @@
-// Screen Readers
-// -------------------------
-
-.sr-only { .sr-only(); }
-.sr-only-focusable { .sr-only-focusable(); }

文件差異過大導致無法顯示
+ 0 - 2066
healthcareio/server/static/css/fa/less/_shims.less


+ 0 - 22
healthcareio/server/static/css/fa/less/_stacked.less

@@ -1,22 +0,0 @@
-// Stacked Icons
-// -------------------------
-
-.@{fa-css-prefix}-stack {
-  display: inline-block;
-  height: 2em;
-  line-height: 2em;
-  position: relative;
-  vertical-align: middle;
-  width: 2em;
-}
-
-.@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x {
-  left: 0;
-  position: absolute;
-  text-align: center;
-  width: 100%;
-}
-
-.@{fa-css-prefix}-stack-1x { line-height: inherit; }
-.@{fa-css-prefix}-stack-2x { font-size: 2em; }
-.@{fa-css-prefix}-inverse { color: @fa-inverse; }

文件差異過大導致無法顯示
+ 0 - 1453
healthcareio/server/static/css/fa/less/_variables.less


+ 0 - 23
healthcareio/server/static/css/fa/less/brands.less

@@ -1,23 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import "_variables.less";
-
-@font-face {
-  font-family: 'Font Awesome 5 Brands';
-  font-style: normal;
-  font-weight: 400;
-  font-display: @fa-font-display;
-  src: url('@{fa-font-path}/fa-brands-400.eot');
-  src: url('@{fa-font-path}/fa-brands-400.eot?#iefix') format('embedded-opentype'),
-    url('@{fa-font-path}/fa-brands-400.woff2') format('woff2'),
-    url('@{fa-font-path}/fa-brands-400.woff') format('woff'),
-    url('@{fa-font-path}/fa-brands-400.ttf') format('truetype'),
-    url('@{fa-font-path}/fa-brands-400.svg#fontawesome') format('svg');
-}
-
-.fab {
-  font-family: 'Font Awesome 5 Brands';
-  font-weight: 400;
-}

+ 0 - 16
healthcareio/server/static/css/fa/less/fontawesome.less

@@ -1,16 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import "_variables.less";
-@import "_mixins.less";
-@import "_core.less";
-@import "_larger.less";
-@import "_fixed-width.less";
-@import "_list.less";
-@import "_bordered-pulled.less";
-@import "_animated.less";
-@import "_rotated-flipped.less";
-@import "_stacked.less";
-@import "_icons.less";
-@import "_screen-reader.less";

+ 0 - 23
healthcareio/server/static/css/fa/less/regular.less

@@ -1,23 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import "_variables.less";
-
-@font-face {
-  font-family: 'Font Awesome 5 Free';
-  font-style: normal;
-  font-weight: 400;
-  font-display: @fa-font-display;
-  src: url('@{fa-font-path}/fa-regular-400.eot');
-  src: url('@{fa-font-path}/fa-regular-400.eot?#iefix') format('embedded-opentype'),
-    url('@{fa-font-path}/fa-regular-400.woff2') format('woff2'),
-    url('@{fa-font-path}/fa-regular-400.woff') format('woff'),
-    url('@{fa-font-path}/fa-regular-400.ttf') format('truetype'),
-    url('@{fa-font-path}/fa-regular-400.svg#fontawesome') format('svg');
-}
-
-.far {
-  font-family: 'Font Awesome 5 Free';
-  font-weight: 400;
-}

+ 0 - 24
healthcareio/server/static/css/fa/less/solid.less

@@ -1,24 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import "_variables.less";
-
-@font-face {
-  font-family: 'Font Awesome 5 Free';
-  font-style: normal;
-  font-weight: 900;
-  font-display: @fa-font-display;
-  src: url('@{fa-font-path}/fa-solid-900.eot');
-  src: url('@{fa-font-path}/fa-solid-900.eot?#iefix') format('embedded-opentype'),
-    url('@{fa-font-path}/fa-solid-900.woff2') format('woff2'),
-    url('@{fa-font-path}/fa-solid-900.woff') format('woff'),
-    url('@{fa-font-path}/fa-solid-900.ttf') format('truetype'),
-    url('@{fa-font-path}/fa-solid-900.svg#fontawesome') format('svg');
-}
-
-.fa,
-.fas {
-  font-family: 'Font Awesome 5 Free';
-  font-weight: 900;
-}

+ 0 - 6
healthcareio/server/static/css/fa/less/v4-shims.less

@@ -1,6 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import '_variables.less';
-@import '_shims.less';

文件差異過大導致無法顯示
+ 0 - 2562
healthcareio/server/static/css/fa/metadata/categories.yml


文件差異過大導致無法顯示
+ 0 - 57762
healthcareio/server/static/css/fa/metadata/icons.json


文件差異過大導致無法顯示
+ 0 - 21485
healthcareio/server/static/css/fa/metadata/icons.yml


文件差異過大導致無法顯示
+ 0 - 2317
healthcareio/server/static/css/fa/metadata/shims.json


+ 0 - 298
healthcareio/server/static/css/fa/metadata/shims.yml

@@ -1,298 +0,0 @@
-area-chart:
-  name: chart-area
-arrow-circle-o-down:
-  name: arrow-alt-circle-down
-  prefix: far
-arrow-circle-o-left:
-  name: arrow-alt-circle-left
-  prefix: far
-arrow-circle-o-right:
-  name: arrow-alt-circle-right
-  prefix: far
-arrow-circle-o-up:
-  name: arrow-alt-circle-up
-  prefix: far
-arrows:
-  name: arrows-alt
-arrows-alt:
-  name: expand-arrows-alt
-arrows-h:
-  name: arrows-alt-h
-arrows-v:
-  name: arrows-alt-v
-bar-chart:
-  name: chart-bar
-  prefix: far
-bitbucket-square:
-  name: bitbucket
-  prefix: fab
-calendar:
-  name: calendar-alt
-calendar-o:
-  name: calendar
-  prefix: far
-caret-square-o-down:
-  name: caret-square-down
-  prefix: far
-caret-square-o-left:
-  name: caret-square-left
-  prefix: far
-caret-square-o-right:
-  name: caret-square-right
-  prefix: far
-caret-square-o-up:
-  name: caret-square-up
-  prefix: far
-cc:
-  name: closed-captioning
-  prefix: far
-chain-broken:
-  name: unlink
-circle-o-notch:
-  name: circle-notch
-circle-thin:
-  name: circle
-  prefix: far
-clipboard:
-  prefix: far
-clone:
-  prefix: far
-cloud-download:
-  name: cloud-download-alt
-cloud-upload:
-  name: cloud-upload-alt
-code-fork:
-  name: code-branch
-comment-alt:
-  name: comment-dots
-  prefix: far
-commenting:
-  name: comment-dots
-compass:
-  prefix: far
-compress:
-  name: compress-alt
-copyright:
-  prefix: far
-creative-commons:
-  prefix: fab
-credit-card:
-  prefix: far
-credit-card-alt:
-  name: credit-card
-cutlery:
-  name: utensils
-diamond:
-  name: gem
-  prefix: far
-eercast:
-  name: sellcast
-  prefix: fab
-eur:
-  name: euro-sign
-exchange:
-  name: exchange-alt
-expand:
-  name: expand-alt
-external-link:
-  name: external-link-alt
-external-link-square:
-  name: external-link-square-alt
-eye:
-  prefix: far
-eye-dropper:
-  name: eye-dropper
-  prefix: far
-eye-slash:
-  prefix: far
-eyedropper:
-  name: eye-dropper
-facebook:
-  name: facebook-f
-  prefix: fab
-facebook-official:
-  name: facebook
-  prefix: fab
-file-text:
-  name: file-alt
-files-o:
-  name: copy
-  prefix: far
-floppy-o:
-  name: save
-  prefix: far
-gbp:
-  name: pound-sign
-glass:
-  name: glass-martini
-google-plus:
-  name: google-plus-g
-  prefix: fab
-google-plus-circle:
-  name: google-plus
-  prefix: fab
-google-plus-official:
-  name: google-plus
-  prefix: fab
-hand-o-down:
-  name: hand-point-down
-  prefix: far
-hand-o-left:
-  name: hand-point-left
-  prefix: far
-hand-o-right:
-  name: hand-point-right
-  prefix: far
-hand-o-up:
-  name: hand-point-up
-  prefix: far
-header:
-  name: heading
-id-badge:
-  prefix: far
-ils:
-  name: shekel-sign
-inr:
-  name: rupee-sign
-intersex:
-  name: transgender
-jpy:
-  name: yen-sign
-krw:
-  name: won-sign
-level-down:
-  name: level-down-alt
-level-up:
-  name: level-up-alt
-life-ring:
-  prefix: far
-line-chart:
-  name: chart-line
-linkedin:
-  name: linkedin-in
-  prefix: fab
-linkedin-square:
-  name: linkedin
-  prefix: fab
-list-alt:
-  prefix: far
-long-arrow-down:
-  name: long-arrow-alt-down
-long-arrow-left:
-  name: long-arrow-alt-left
-long-arrow-right:
-  name: long-arrow-alt-right
-long-arrow-up:
-  name: long-arrow-alt-up
-map-marker:
-  name: map-marker-alt
-meanpath:
-  name: font-awesome
-  prefix: fab
-mobile:
-  name: mobile-alt
-money:
-  name: money-bill-alt
-  prefix: far
-object-group:
-  prefix: far
-object-ungroup:
-  prefix: far
-paste:
-  prefix: far
-pencil:
-  name: pencil-alt
-pencil-square:
-  name: pen-square
-pencil-square-o:
-  name: edit
-  prefix: far
-picture:
-  name: image
-pie-chart:
-  name: chart-pie
-refresh:
-  name: sync
-registered:
-  prefix: far
-repeat:
-  name: redo
-rub:
-  name: ruble-sign
-scissors:
-  name: cut
-shield:
-  name: shield-alt
-sign-in:
-  name: sign-in-alt
-sign-out:
-  name: sign-out-alt
-sliders:
-  name: sliders-h
-sort-alpha-asc:
-  name: sort-alpha-down
-sort-alpha-desc:
-  name: sort-alpha-down-alt
-sort-amount-asc:
-  name: sort-amount-down
-sort-amount-desc:
-  name: sort-amount-down-alt
-sort-asc:
-  name: sort-up
-sort-desc:
-  name: sort-down
-sort-numeric-asc:
-  name: sort-numeric-down
-sort-numeric-desc:
-  name: sort-numeric-down-alt
-spoon:
-  name: utensil-spoon
-star-half-empty:
-  name: star-half
-star-half-full:
-  name: star-half
-support:
-  name: life-ring
-  prefix: far
-tablet:
-  name: tablet-alt
-tachometer:
-  name: tachometer-alt
-television:
-  name: tv
-thumb-tack:
-  name: thumbtack
-thumbs-o-down:
-  name: thumbs-down
-  prefix: far
-thumbs-o-up:
-  name: thumbs-up
-  prefix: far
-ticket:
-  name: ticket-alt
-trash:
-  name: trash-alt
-trash-o:
-  name: trash-alt
-  prefix: far
-try:
-  name: lira-sign
-usd:
-  name: dollar-sign
-video-camera:
-  name: video
-vimeo:
-  name: vimeo-v
-  prefix: fab
-volume-control-phone:
-  name: phone-volume
-wheelchair-alt:
-  name: accessible-icon
-  prefix: fab
-window-maximize:
-  prefix: far
-window-restore:
-  prefix: far
-youtube-play:
-  name: youtube
-  prefix: fab

+ 0 - 688
healthcareio/server/static/css/fa/metadata/sponsors.yml

@@ -1,688 +0,0 @@
-accusoft:
-  icons:
-    - accusoft
-  label: Accusoft
-  url: 'https://www.accusoft.com'
-administrator-technology:
-  icons:
-    - stream
-  label: Administrator Technology
-  url: 'https://administrator.de'
-adversal:
-  icons:
-    - adversal
-  label: Adversal
-  url: 'https://www.adversal.com'
-affiliatetheme:
-  icons:
-    - affiliatetheme
-  label: affiliatetheme
-  url: 'https://affiliatetheme.io/en'
-algolia:
-  icons:
-    - algolia
-  label: Algolia
-  url: 'http://www.algolia.com'
-amazon-web-services:
-  icons:
-    - aws
-  label: Amazon Web Services
-  url: 'https://aws.amazon.com'
-amilia:
-  icons:
-    - amilia
-  label: Amilia
-  url: 'http://www.amilia.com'
-angry-creative:
-  icons:
-    - angrycreative
-  label: Angry Creative
-  url: 'https://angrycreative.se'
-app-signal:
-  icons:
-    - stroopwafel
-  label: AppSignal
-  url: 'https://appsignal.com'
-apper-systems-ab:
-  icons:
-    - apper
-  label: Apper Systems AB
-  url: 'http://www.apper.com'
-'asymmetrik,ltd':
-  icons:
-    - asymmetrik
-  label: 'Asymmetrik, Ltd.'
-  url: 'http://asymmetrik.com'
-ausmed-education:
-  icons:
-    - user-nurse
-  label: Ausmed Education
-  url: 'https://www.ausmed.com.au'
-avianex:
-  icons:
-    - avianex
-  label: avianex
-  url: 'https://www.avianex.de'
-bi-mobject:
-  icons:
-    - bimobject
-  label: BIMobject
-  url: 'http://bimobject.com'
-bity:
-  icons:
-    - bity
-  label: Bity
-  url: 'http://bity.com'
-blackpulp-designs:
-  icons:
-    - pray
-  label: Blackpulp Designs
-  url: 'https://www.blackpulp.com'
-blissbook:
-  icons:
-    - pen-fancy
-  label: Blissbook
-  url: 'https://blissbook.com'
-büromöbel-experte-gmb-h &co-kg:
-  icons:
-    - buromobelexperte
-  label: Büromöbel-Experte GmbH & Co. KG.
-  url: 'https://www.bueromoebel-experte.de'
-c-panel:
-  icons:
-    - cpanel
-  label: cPanel
-  url: 'http://cpanel.com'
-centercode:
-  icons:
-    - centercode
-  label: Centercode
-  url: 'https://www.centercode.com'
-cibltd:
-  icons:
-    - drum-steelpan
-  label: Comprehensive Insurance Brokers Limited
-  url: 'http://www.cibltd.com'
-clear-blue-technologies:
-  icons:
-    - solar-panel
-  label: Clear Blue Technologies
-  url: 'http://www.clearbluetechnologies.com'
-cloudscale-ch:
-  icons:
-    - cloudscale
-  label: cloudscale.ch
-  url: 'https://www.cloudscale.ch'
-cloudsmith:
-  icons:
-    - cloudsmith
-  label: Cloudsmith
-  url: 'https://cloudsmith.io'
-cloudversify:
-  icons:
-    - cloudversify
-  label: cloudversify
-  url: 'https://www.cloudversify.com'
-cuttlefish:
-  icons:
-    - cuttlefish
-  label: Cuttlefish
-  url: 'http://wearecuttlefish.com'
-cymedica:
-  icons:
-    - wave-square
-  label: CyMedica
-  url: 'https://www.cymedicaortho.com'
-darren-wiebe:
-  icons:
-    - church
-  label: Darren Wiebe
-deploy-dog:
-  icons:
-    - deploydog
-  label: deploy.dog
-  url: 'http://deploy.dog'
-deskpro:
-  icons:
-    - deskpro
-  label: Deskpro
-  url: 'http://www.deskpro.com'
-discourse:
-  icons:
-    - discourse
-  label: Discourse
-  url: 'https://discourse.org'
-doc-hub:
-  icons:
-    - dochub
-  label: DocHub
-  url: 'https://dochub.com'
-draft2-digital:
-  icons:
-    - draft2digital
-  label: Draft2Digital
-  url: 'http://draft2digital.com'
-dyalog-apl:
-  icons:
-    - dyalog
-  label: Dyalog APL
-  url: 'http://www.dyalog.com'
-econopublish:
-  icons:
-    - hat-cowboy-side
-  label: EconoPublish
-  url: 'https://www.econopublish.com'
-firstdraft:
-  icons:
-    - firstdraft
-  label: firstdraft
-  url: 'http://www.firstdraft.com'
-fleetplan:
-  icons:
-    - helicopter
-  label: FLEETPLAN
-  url: 'https://www.fleetplan.net'
-getaroom:
-  icons:
-    - archway
-    - dumbbell
-    - hotel
-    - map-marked
-    - map-marked-alt
-    - monument
-    - spa
-    - swimmer
-    - swimming-pool
-  label: getaroom
-  url: 'https://www.getaroom.com'
-git-kraken:
-  icons:
-    - gitkraken
-  label: GitKraken
-  url: 'https://www.gitkraken.com'
-gofore:
-  icons:
-    - gofore
-  label: Gofore
-  url: 'http://gofore.com'
-'gripfire,inc':
-  icons:
-    - gripfire
-  label: 'Gripfire, Inc.'
-  url: 'http://gripfire.io'
-harvard-medical-school:
-  icons:
-    - allergies
-    - ambulance
-    - band-aid
-    - briefcase-medical
-    - burn
-    - capsules
-    - diagnoses
-    - dna
-    - file-medical
-    - file-medical-alt
-    - first-aid
-    - heart
-    - heartbeat
-    - hospital
-    - hospital-alt
-    - hospital-symbol
-    - id-card-alt
-    - notes-medical
-    - pills
-    - plus
-    - prescription-bottle
-    - prescription-bottle-alt
-    - procedures
-    - smoking
-    - stethoscope
-    - syringe
-    - tablets
-    - thermometer
-    - user-md
-    - vial
-    - vials
-    - weight
-    - x-ray
-  label: Harvard Medical School
-  url: 'https://hms.harvard.edu'
-hips:
-  icons:
-    - hips
-  label: Hips
-  url: 'https://hips.com'
-hire-a-helper:
-  icons:
-    - archive
-    - box-open
-    - couch
-    - dolly
-    - people-carry
-    - route
-    - sign
-    - suitcase
-    - tape
-    - truck-loading
-    - truck-moving
-    - wine-glass
-  label: HireAHelper
-  url: 'https://www.hireahelper.com'
-hornbill:
-  icons:
-    - hornbill
-  label: Hornbill
-  url: 'https://www.hornbill.com'
-hotjar:
-  icons:
-    - hotjar
-  label: Hotjar
-  url: 'https://www.hotjar.com'
-hub-spot:
-  icons:
-    - hubspot
-  label: HubSpot
-  url: 'http://www.HubSpot.com'
-in-site-systems:
-  icons:
-    - toolbox
-  label: InSite Systems
-  url: 'https://www.insitesystems.com'
-inspira-bvba:
-  icons:
-    - chess-knight
-  label: Inspira bvba
-  url: 'https://www.inspira.be'
-joe-emison:
-  icons:
-    - blender-phone
-  label: Joe Emison
-joget:
-  icons:
-    - joget
-  label: Joget
-  url: 'http://www.joget.org'
-jon-galloway:
-  icons:
-    - crow
-  label: Jon Galloway
-kevin-barone:
-  icons:
-    - file-contract
-  label: Kevin Barone
-key-cdn:
-  icons:
-    - keycdn
-  label: KeyCDN
-  url: 'https://www.keycdn.com'
-korvue:
-  icons:
-    - korvue
-  label: Korvue
-  url: 'https://korvue.com'
-max-elman:
-  icons:
-    - frog
-  label: Max Elman
-med-apps:
-  icons:
-    - medapps
-  label: MedApps
-  url: 'http://medapps.com.au'
-medapps:
-  icons:
-    - book-medical
-    - clinic-medical
-    - comment-medical
-    - crutch
-    - disease
-    - hospital-user
-    - laptop-medical
-    - pager
-  label: MedApps
-  url: 'https://medapps.com.au'
-megaport:
-  icons:
-    - megaport
-  label: Megaport
-  url: 'https://www.megaport.com'
-mix:
-  icons:
-    - mix
-  label: Mix
-  url: 'http://mix.com'
-mizuni:
-  icons:
-    - mizuni
-  label: Mizuni
-  url: 'http://www.mizuni.com'
-mrt:
-  icons:
-    - medrt
-  label: MRT
-  url: 'https://medrt.co.jp'
-mylogin-info:
-  icons:
-    - user-shield
-  label: mylogin.info
-  url: 'https://www.mylogin.info'
-napster:
-  icons:
-    - napster
-  label: Napster
-  url: 'http://www.napster.com'
-nimblr:
-  icons:
-    - nimblr
-  label: Nimblr
-  url: 'https://nimblr.ai'
-nompse:
-  icons:
-    - chalkboard
-    - chalkboard-teacher
-  label: Nomp.se
-  url: 'https://nomp.se'
-ns8:
-  icons:
-    - ns8
-  label: NS8
-  url: 'https://www.ns8.com'
-nutritionix:
-  icons:
-    - nutritionix
-  label: Nutritionix
-  url: 'http://www.nutritionix.com'
-page4-corporation:
-  icons:
-    - page4
-  label: page4 Corporation
-  url: 'https://en.page4.com'
-pal-fed:
-  icons:
-    - palfed
-  label: PalFed
-  url: 'https://www.palfed.com'
-pcsg:
-  icons:
-    - horse-head
-  label: PCSG
-  url: 'https://www.pcsg.de'
-phabricator:
-  icons:
-    - phabricator
-  label: Phabricator
-  url: 'http://phacility.com'
-promo-wizard:
-  icons:
-    - hat-wizard
-  label: Promo Wizard
-  url: 'https://promowizard.co.uk'
-pulse-eight:
-  icons:
-    - volume-mute
-  label: Pulse-Eight
-  url: 'https://pulse-eight.com'
-purely-interactive:
-  icons:
-    - kiwi-bird
-  label: Purely Interactive
-  url: 'https://www.purelyinteractive.ca'
-pushed:
-  icons:
-    - pushed
-  label: Pushed
-  url: 'https://pushed.co'
-quin-scape:
-  icons:
-    - quinscape
-  label: QuinScape
-  url: 'https://www.quinscape.de'
-reacteurope:
-  icons:
-    - reacteurope
-  label: ReactEurope
-  url: 'https://www.react-europe.org'
-readme-io:
-  icons:
-    - readme
-  label: Readme.io
-  url: 'http://readme.io'
-red-river:
-  icons:
-    - red-river
-  label: red river
-  url: 'https://river.red'
-replyd:
-  icons:
-    - replyd
-  label: replyd
-resolving:
-  icons:
-    - resolving
-  label: Resolving
-  url: 'https://resolving.com'
-rev-io:
-  icons:
-    - rev
-  label: Rev.io
-  url: 'https://rev.io'
-rock-rms:
-  icons:
-    - rockrms
-  label: Rock RMS
-  url: 'http://rockrms.com'
-rocket-chat:
-  icons:
-    - comment
-    - comment-alt
-    - comment-dots
-    - comment-slash
-    - comments
-    - frown
-    - meh
-    - phone
-    - phone-slash
-    - poo
-    - quote-left
-    - quote-right
-    - rocketchat
-    - smile
-    - video
-    - video-slash
-  label: Rocket.Chat
-  url: 'https://rocket.chat'
-rodney-oliver:
-  icons:
-    - folder-minus
-    - folder-plus
-  label: Rodney Oliver
-schlix:
-  icons:
-    - schlix
-  label: SCHLIX
-  url: 'http://schlix.com'
-search-eng-in:
-  icons:
-    - searchengin
-  label: SearchEng.in
-  url: 'http://searcheng.in'
-service-stack:
-  icons:
-    - servicestack
-  label: ServiceStack
-  url: 'https://servicestack.net'
-shawn-storie:
-  icons:
-    - teeth
-    - teeth-open
-  label: Shawn Storie
-shopware:
-  icons:
-    - shopware
-  label: Shopware
-  url: 'https://shopware.de'
-shp:
-  icons:
-    - school
-  label: SHP
-  url: 'http://shp.com'
-silicon-barn-inc:
-  icons:
-    - project-diagram
-  label: Silicon Barn Inc
-  url: 'https://siliconbarn.com'
-sistrix:
-  icons:
-    - sistrix
-  label: SISTRIX
-  url: 'https://www.sistrix.de'
-smup:
-  icons:
-    - shoe-prints
-  label: Smup
-  url: 'https://www.atomsoftware.com.au'
-speakap:
-  icons:
-    - speakap
-  label: Speakap
-  url: 'https://speakap.com'
-stay-linked:
-  icons:
-    - box
-    - boxes
-    - clipboard-check
-    - clipboard-list
-    - dolly
-    - dolly-flatbed
-    - pallet
-    - shipping-fast
-    - truck
-    - warehouse
-  label: StayLinked
-  url: 'https://www.staylinked.com'
-sticker-mule:
-  icons:
-    - sticker-mule
-  label: Sticker Mule
-  url: 'https://stickermule.com'
-studio-vinari:
-  icons:
-    - studiovinari
-  label: Studio Vinari
-  url: 'https://studiovinari.com'
-supple:
-  icons:
-    - ad
-    - bullhorn
-    - bullseye
-    - comment-dollar
-    - comments-dollar
-    - envelope-open-text
-    - funnel-dollar
-    - mail-bulk
-    - poll
-    - poll-h
-    - search-dollar
-    - search-location
-    - supple
-  label: Supple
-  url: 'https://supple.com.au'
-the-red-yeti:
-  icons:
-    - the-red-yeti
-  label: The Red Yeti
-  url: 'http://theredyeti.com'
-the-us-sunnah-foundation:
-  icons:
-    - dollar-sign
-    - donate
-    - dove
-    - gift
-    - globe
-    - hand-holding-heart
-    - hand-holding-usd
-    - hand-holding-water
-    - hands-helping
-    - handshake
-    - heart
-    - leaf
-    - parachute-box
-    - piggy-bank
-    - ribbon
-    - seedling
-  label: The us-Sunnah Foundation
-  url: 'https://www.ussunnah.org'
-themeco:
-  icons:
-    - themeco
-  label: Themeco
-  url: 'https://theme.co'
-think-peaks:
-  icons:
-    - think-peaks
-  label: Think Peaks
-  url: 'https://thinkpeaks.com/'
-typo3:
-  icons:
-    - typo3
-  label: Typo3
-  url: 'https://typo3.org'
-uniregistry:
-  icons:
-    - uniregistry
-  label: Uniregistry
-  url: 'https://uniregistry.com'
-us-sunnah-foundation:
-  icons:
-    - ussunnah
-  label: us-Sunnah Foundation
-  url: 'https://www.ussunnah.org'
-vaadin:
-  icons:
-    - vaadin
-  label: Vaadin
-  url: 'http://vaadin.com'
-via:
-  icons:
-    - car-crash
-    - draw-polygon
-    - house-damage
-    - layer-group
-    - skull-crossbones
-    - user-injured
-  label: VIA Traffic Software Solutions
-  url: 'https://www.via.software'
-victor-costan:
-  icons:
-    - otter
-  label: Staphany Park and Victor Costan
-vnv:
-  icons:
-    - vnv
-  label: VNV
-  url: 'https://www.vnv.ch'
-weedable:
-  icons:
-    - bong
-    - cannabis
-    - hippo
-    - joint
-    - mortar-pestle
-    - prescription
-  label: Weedable
-  url: 'https://www.weedable.com'
-whmcs:
-  icons:
-    - whmcs
-  label: WHMCS
-  url: 'https://www.whmcs.com'
-workrails:
-  icons:
-    - briefcase
-  label: WorkRails
-  url: 'https://www.workrails.com'
-wpressr:
-  icons:
-    - wpressr
-  label: wpressr
-  url: 'https://wpressr.com'

+ 0 - 20
healthcareio/server/static/css/fa/scss/_animated.scss

@@ -1,20 +0,0 @@
-// Animated Icons
-// --------------------------
-
-.#{$fa-css-prefix}-spin {
-  animation: fa-spin 2s infinite linear;
-}
-
-.#{$fa-css-prefix}-pulse {
-  animation: fa-spin 1s infinite steps(8);
-}
-
-@keyframes fa-spin {
-  0% {
-    transform: rotate(0deg);
-  }
-
-  100% {
-    transform: rotate(360deg);
-  }
-}

+ 0 - 20
healthcareio/server/static/css/fa/scss/_bordered-pulled.scss

@@ -1,20 +0,0 @@
-// Bordered & Pulled
-// -------------------------
-
-.#{$fa-css-prefix}-border {
-  border: solid .08em $fa-border-color;
-  border-radius: .1em;
-  padding: .2em .25em .15em;
-}
-
-.#{$fa-css-prefix}-pull-left { float: left; }
-.#{$fa-css-prefix}-pull-right { float: right; }
-
-.#{$fa-css-prefix},
-.fas,
-.far,
-.fal,
-.fab {
-  &.#{$fa-css-prefix}-pull-left { margin-right: .3em; }
-  &.#{$fa-css-prefix}-pull-right { margin-left: .3em; }
-}

+ 0 - 21
healthcareio/server/static/css/fa/scss/_core.scss

@@ -1,21 +0,0 @@
-// Base Class Definition
-// -------------------------
-
-.#{$fa-css-prefix},
-.fas,
-.far,
-.fal,
-.fad,
-.fab {
-  -moz-osx-font-smoothing: grayscale;
-  -webkit-font-smoothing: antialiased;
-  display: inline-block;
-  font-style: normal;
-  font-variant: normal;
-  text-rendering: auto;
-  line-height: 1;
-}
-
-%fa-icon {
-  @include fa-icon;
-}

+ 0 - 6
healthcareio/server/static/css/fa/scss/_fixed-width.scss

@@ -1,6 +0,0 @@
-// Fixed Width Icons
-// -------------------------
-.#{$fa-css-prefix}-fw {
-  text-align: center;
-  width: $fa-fw-width;
-}

文件差異過大導致無法顯示
+ 0 - 1441
healthcareio/server/static/css/fa/scss/_icons.scss


+ 0 - 23
healthcareio/server/static/css/fa/scss/_larger.scss

@@ -1,23 +0,0 @@
-// Icon Sizes
-// -------------------------
-
-// makes the font 33% larger relative to the icon container
-.#{$fa-css-prefix}-lg {
-  font-size: (4em / 3);
-  line-height: (3em / 4);
-  vertical-align: -.0667em;
-}
-
-.#{$fa-css-prefix}-xs {
-  font-size: .75em;
-}
-
-.#{$fa-css-prefix}-sm {
-  font-size: .875em;
-}
-
-@for $i from 1 through 10 {
-  .#{$fa-css-prefix}-#{$i}x {
-    font-size: $i * 1em;
-  }
-}

+ 0 - 18
healthcareio/server/static/css/fa/scss/_list.scss

@@ -1,18 +0,0 @@
-// List Icons
-// -------------------------
-
-.#{$fa-css-prefix}-ul {
-  list-style-type: none;
-  margin-left: $fa-li-width * 5/4;
-  padding-left: 0;
-
-  > li { position: relative; }
-}
-
-.#{$fa-css-prefix}-li {
-  left: -$fa-li-width;
-  position: absolute;
-  text-align: center;
-  width: $fa-li-width;
-  line-height: inherit;
-}

+ 0 - 56
healthcareio/server/static/css/fa/scss/_mixins.scss

@@ -1,56 +0,0 @@
-// Mixins
-// --------------------------
-
-@mixin fa-icon {
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  display: inline-block;
-  font-style: normal;
-  font-variant: normal;
-  font-weight: normal;
-  line-height: 1;
-}
-
-@mixin fa-icon-rotate($degrees, $rotation) {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})";
-  transform: rotate($degrees);
-}
-
-@mixin fa-icon-flip($horiz, $vert, $rotation) {
-  -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)";
-  transform: scale($horiz, $vert);
-}
-
-
-// Only display content to screen readers. A la Bootstrap 4.
-//
-// See: http://a11yproject.com/posts/how-to-hide-content/
-
-@mixin sr-only {
-  border: 0;
-  clip: rect(0, 0, 0, 0);
-  height: 1px;
-  margin: -1px;
-  overflow: hidden;
-  padding: 0;
-  position: absolute;
-  width: 1px;
-}
-
-// Use in conjunction with .sr-only to only display content when it's focused.
-//
-// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
-//
-// Credit: HTML5 Boilerplate
-
-@mixin sr-only-focusable {
-  &:active,
-  &:focus {
-    clip: auto;
-    height: auto;
-    margin: 0;
-    overflow: visible;
-    position: static;
-    width: auto;
-  }
-}

+ 0 - 24
healthcareio/server/static/css/fa/scss/_rotated-flipped.scss

@@ -1,24 +0,0 @@
-// Rotated & Flipped Icons
-// -------------------------
-
-.#{$fa-css-prefix}-rotate-90  { @include fa-icon-rotate(90deg, 1);  }
-.#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
-.#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
-
-.#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
-.#{$fa-css-prefix}-flip-vertical   { @include fa-icon-flip(1, -1, 2); }
-.#{$fa-css-prefix}-flip-both, .#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(-1, -1, 2); }
-
-// Hook for IE8-9
-// -------------------------
-
-:root {
-  .#{$fa-css-prefix}-rotate-90,
-  .#{$fa-css-prefix}-rotate-180,
-  .#{$fa-css-prefix}-rotate-270,
-  .#{$fa-css-prefix}-flip-horizontal,
-  .#{$fa-css-prefix}-flip-vertical,
-  .#{$fa-css-prefix}-flip-both {
-    filter: none;
-  }
-}

+ 0 - 5
healthcareio/server/static/css/fa/scss/_screen-reader.scss

@@ -1,5 +0,0 @@
-// Screen Readers
-// -------------------------
-
-.sr-only { @include sr-only; }
-.sr-only-focusable { @include sr-only-focusable; }

文件差異過大導致無法顯示
+ 0 - 2066
healthcareio/server/static/css/fa/scss/_shims.scss


+ 0 - 31
healthcareio/server/static/css/fa/scss/_stacked.scss

@@ -1,31 +0,0 @@
-// Stacked Icons
-// -------------------------
-
-.#{$fa-css-prefix}-stack {
-  display: inline-block;
-  height: 2em;
-  line-height: 2em;
-  position: relative;
-  vertical-align: middle;
-  width: ($fa-fw-width*2);
-}
-
-.#{$fa-css-prefix}-stack-1x,
-.#{$fa-css-prefix}-stack-2x {
-  left: 0;
-  position: absolute;
-  text-align: center;
-  width: 100%;
-}
-
-.#{$fa-css-prefix}-stack-1x {
-  line-height: inherit;
-}
-
-.#{$fa-css-prefix}-stack-2x {
-  font-size: 2em;
-}
-
-.#{$fa-css-prefix}-inverse {
-  color: $fa-inverse;
-}

文件差異過大導致無法顯示
+ 0 - 1458
healthcareio/server/static/css/fa/scss/_variables.scss


+ 0 - 23
healthcareio/server/static/css/fa/scss/brands.scss

@@ -1,23 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import 'variables';
-
-@font-face {
-  font-family: 'Font Awesome 5 Brands';
-  font-style: normal;
-  font-weight: 400;
-  font-display: $fa-font-display;
-  src: url('#{$fa-font-path}/fa-brands-400.eot');
-  src: url('#{$fa-font-path}/fa-brands-400.eot?#iefix') format('embedded-opentype'),
-  url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'),
-  url('#{$fa-font-path}/fa-brands-400.woff') format('woff'),
-  url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'),
-  url('#{$fa-font-path}/fa-brands-400.svg#fontawesome') format('svg');
-}
-
-.fab {
-  font-family: 'Font Awesome 5 Brands';
-  font-weight: 400;
-}

+ 0 - 16
healthcareio/server/static/css/fa/scss/fontawesome.scss

@@ -1,16 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import 'variables';
-@import 'mixins';
-@import 'core';
-@import 'larger';
-@import 'fixed-width';
-@import 'list';
-@import 'bordered-pulled';
-@import 'animated';
-@import 'rotated-flipped';
-@import 'stacked';
-@import 'icons';
-@import 'screen-reader';

+ 0 - 23
healthcareio/server/static/css/fa/scss/regular.scss

@@ -1,23 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import 'variables';
-
-@font-face {
-  font-family: 'Font Awesome 5 Free';
-  font-style: normal;
-  font-weight: 400;
-  font-display: $fa-font-display;
-  src: url('#{$fa-font-path}/fa-regular-400.eot');
-  src: url('#{$fa-font-path}/fa-regular-400.eot?#iefix') format('embedded-opentype'),
-  url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'),
-  url('#{$fa-font-path}/fa-regular-400.woff') format('woff'),
-  url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'),
-  url('#{$fa-font-path}/fa-regular-400.svg#fontawesome') format('svg');
-}
-
-.far {
-  font-family: 'Font Awesome 5 Free';
-  font-weight: 400;
-}

+ 0 - 24
healthcareio/server/static/css/fa/scss/solid.scss

@@ -1,24 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import 'variables';
-
-@font-face {
-  font-family: 'Font Awesome 5 Free';
-  font-style: normal;
-  font-weight: 900;
-  font-display: $fa-font-display;
-  src: url('#{$fa-font-path}/fa-solid-900.eot');
-  src: url('#{$fa-font-path}/fa-solid-900.eot?#iefix') format('embedded-opentype'),
-  url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'),
-  url('#{$fa-font-path}/fa-solid-900.woff') format('woff'),
-  url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'),
-  url('#{$fa-font-path}/fa-solid-900.svg#fontawesome') format('svg');
-}
-
-.fa,
-.fas {
-  font-family: 'Font Awesome 5 Free';
-  font-weight: 900;
-}

+ 0 - 6
healthcareio/server/static/css/fa/scss/v4-shims.scss

@@ -1,6 +0,0 @@
-/*!
- * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@import 'variables';
-@import 'shims';

文件差異過大導致無法顯示
+ 0 - 1336
healthcareio/server/static/css/fa/sprites/brands.svg


文件差異過大導致無法顯示
+ 0 - 463
healthcareio/server/static/css/fa/sprites/regular.svg


文件差異過大導致無法顯示
+ 0 - 2995
healthcareio/server/static/css/fa/sprites/solid.svg


文件差異過大導致無法顯示
+ 0 - 1
healthcareio/server/static/css/fa/svgs/brands/500px.svg


文件差異過大導致無法顯示
+ 0 - 1
healthcareio/server/static/css/fa/svgs/brands/accessible-icon.svg


文件差異過大導致無法顯示
+ 0 - 1
healthcareio/server/static/css/fa/svgs/brands/accusoft.svg


文件差異過大導致無法顯示
+ 0 - 1
healthcareio/server/static/css/fa/svgs/brands/acquisitions-incorporated.svg


+ 0 - 1
healthcareio/server/static/css/fa/svgs/brands/adn.svg

@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M248 167.5l64.9 98.8H183.1l64.9-98.8zM496 256c0 136.9-111.1 248-248 248S0 392.9 0 256 111.1 8 248 8s248 111.1 248 248zm-99.8 82.7L248 115.5 99.8 338.7h30.4l33.6-51.7h168.6l33.6 51.7h30.2z"/></svg>

+ 0 - 1
healthcareio/server/static/css/fa/svgs/brands/adobe.svg

@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M315.5 64h170.9v384L315.5 64zm-119 0H25.6v384L196.5 64zM256 206.1L363.5 448h-73l-30.7-76.8h-78.7L256 206.1z"/></svg>

文件差異過大導致無法顯示
+ 0 - 1
healthcareio/server/static/css/fa/svgs/brands/adversal.svg


+ 0 - 0
healthcareio/server/static/css/fa/svgs/brands/affiliatetheme.svg


部分文件因文件數量過多而無法顯示