JS tools that use the MIE API for examples
A package designed to interact with MIE's (Medical Informatics Engineering) Webchart API seamlessly in NPM environments.
- Installation
- Methods and Features
- Setting Variables
- Retrieving Cookie
- Retrieving Records
- Updating Records
- Creating Records
- Patient Documents
- AI Capabilities
- Ledger
- Motivation
- Author
NPM
$ npm i @mieweb/mie-api-tools
Yarn
$ yarn add @mieweb/mie-api-tools
PNPM
$ pnpm add @maxklema/mie-api-tools
Once the package is installed, you can import it into your project using require
or import
.
const mie = require("@mieweb/mie-api-tools");
import mie from "@mieweb/mie-api-tools";
NOTE: Default import is highly reccomended. If you want to use a named import only, you must install the appropriate global variables that will be used to make your request.
Example: Importing the get method requires
const { get, URL, practice, username, password } = require('@maxklema/mie-api-tools');
. Simply importingconst { get } = require('@maxklema/mie-api-tools');
will not work.
As of version 1.0.7, importing @maxklema/mie-api-tools using import
does not include ledger or document capabilities.
- Retrieve Session Cookie
- Retrieve Records
- Search through queries
- Filter by fields
- Get all data related to a patient
- Update Records
- Update any record with JSON data
- Create Recrods
- Create any record with JSON data
- Patient Documents
- Downloading Documents
- Download multiple documents through querying
- Multi-Threading allows for extremely fast downloading
- Document Migration
- Upload multiple documents with through a CSV file
- Multi-Threading allows for extremely fast uploading
- Downloading Documents
- AI Capabilities (using Gemini)
- Summarize patients' entire medical history in any style
- Ask questions about patients' medical history
- Ledger
- Can be turned on or off
- Log all requests, responses, and errors
- Can customize your ledger and the location logs are written to
Before you are able to make requests and interact with the API, you need to set a few variables to help MIE API Tools create a valid session ID.
The following table shows all the global variables that can be set:
Name | Required? | Type | Description |
---|---|---|---|
URL |
Required | String | The URL of your WebChart. It should look something similar to "https://practice.webchartnow.com/webchart.cgi" . |
practice |
Required | String | The name of your practice. |
username |
Required | String | The username used to create a session ID. NOTE: Make sure the user you are choosing has valid permissions. |
password |
Required | String | The password of the user used to create a session ID. |
ledger |
Optional | String | A toggle to switch the ledger on or off. For more information, see Ledger. |
GeminiKey |
Optional | String | Your Gemini API Key to use MIE API Tools' AI features. For more information, see AI Capabilities. |
Setting the variables should be done right after the import statement. An example is shown below:
mie.URL.value = "https://practice.webchartnow.com/webchart.cgi";
mie.practice.value = "practice";
mie.username.value = process.env.USERNAME;
mie.password.value = process.env.PASSWORD;
NOTE: When setting the username and password values, it is recommended to use environment variables.
Sometimes, you may only want to receieve your WebChart Session Cookie. This can be achieved with the mie.getCookie()
method.
Here is an example:
async function getCookie() {
await mie.getCookie();
return mie.Cookie.value;
}
const cookie = getCookie();
NOTE: You must declare your login information first before calling this method. To do this, see Setting Variables.
Perhaps the most common use case of the package is getting database records. This can be achieved with the mie.get()
method.
The basic structure of the request is as follows:
mie
.get("patients", [], {})
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(err);
});
mie.get()
accepts the following parameters:
Name | Required? | Type | Description |
---|---|---|---|
endpoint |
Required | String | The specific API endpoint that you wish to query from. By default, WebChart has 191 endpoints which can be found in Control Panel → API of your WebChart. |
fields |
[] no fields |
String Array | Fields that you want to display (parse) in the response. Only these fields will be returned. |
queries |
{} no queries |
Object | Fields that you want to query by to help locate specific records. |
Response Format: JSON
NOTE: Only specifying the endpoint while leaving the other parameters empty will return ALL data for that endpoint. For example, when retrieving records for the
patients
endpoint, allpatients
data will be returned.
Although mie.get()
returns a promise, the method can also be invoked with await/async
.
async function myFunc() {
const data = await mie.get("documents", [], {});
console.log(data);
}
myFunc();
Example File: here.
In order to narrow down the records returned, you can specify queries to retrieve records by in the third parameter.
For example, in the patients
endpoint, if you want to only return records pertaining to a patient with pat_id: 18
:
mie.get("patients", [], { pat_id: 18 });
You are also able to chain queries together by placing multiple inside an object. This will narrow down a search even more.
mie.get("patients", [], { first_name: "Will", sex: "M" });
NOTE: You can only query by fields that are available for that endpoint specifically. For example,
last_name
exists in thepatients
endpoint, making it a legal field to query by. However, querying bydoc_id
in that same endpoint will throw an error.
Example File: here.
Sometimes a single record can have dozens of fields. If you are only looking for a certain field or fields, you can specify which one(s) in the second parameter.
For example, in the documents
endpoint, if you want to return only the storage_type
of each document:
mie.get("documents", ["storage_type"], { doc_type: "WCPHOTO" });
In the same example, if you only want to return the storage_type
and doc_id
:
mie.get("documents", ["storage_type", "doc_id"], {
doc_type: "WCPHOTO",
});
Thus, the output would look something similar to this for a single record:
'0': {
pat_id: '9',
storage_type: '0',
doc_id: '659'
}
NOTE: When filtering by fields, the
pat_id
(if applicable) is always included in the returned data.
Example File: here.
There may be cases where you want to gather all the available records for a patient. However, these records can be scattered across dozens of endpoints, making it hard to query, let alone concatenate, all of the data.
MIE API Tools provides a method called mie.getAllPatientRecords()
for doing this.
The basic structure of the request is as follows:
mie
.getAllPatientRecords(5, { length: "concise" })
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(err);
});
mie.getAllPatientRecords()
accepts the following parameters:
Name | Required? | Type | Description |
---|---|---|---|
pat_id |
Required | Integer | The patient ID of the patient for whom you want to retrieve their information. |
options (length) |
Optional | Object, with key being type string | The length, or level of detail, of data that is returned. Length accepts brief , concise , and detailed . Each one returns more Records. Why the difference? Some endpoints provide very important (and relevant) patient information, while others provide less important and relevant information. The more brief the synopsis, the less lesser-relevant records that are returned. THe default length is concise . |
Response Format: JSON
Example File: here.
Another common use of the package is updating records at various API endpoints. This can be achieved by using the mie.put()
method.
The basic structure of the request is as follows:
const json_data = {
first_name: "William",
address1: "1690 Broadway Bldg 19, Ste 500, Fort Wayne, IN 46802",
};
mie.put("patients", { pat_id: 18 }, json_data);
Updating records requires a JSON object that contains key-value pairs of fields to update.
mie.put()
accepts the following parameters:
Name | Required? | Type | Description |
---|---|---|---|
endpoint |
Required | String | The specific API endpoint that you wish to query from. By default, WebChart has 191 endpoints which can be found in Control Panel → API of your WebChart. |
identifier |
Required | Object | The unique ID of the specific record to update. |
json_options (new data) |
{} No data to update |
Object | The new updated Data formatted as a JSON object. |
Response Format: none
NOTE: Each endpoint has a specific identifier. For those that deal with patient data, it is most likely
pat_id
. Other endpoints, such asdocuments
, havedoc_id
as their identifier. A complete list can be found inside the API Docs of your WebChart or in src/Variables/endpointLists.js of this repository.
NOTE: By Default, not all endpoints support UPDATE functionality. Double check that the API endpoint you want to use supports UPDATE functionality.
Example File: here.
Creating new Records is another important capability of MIE API Tools. The methodology is very similar to mie.puts()
but without the identifier. This can be achieved by using the mie.post()
method.
The basic structure of the request is as follows:
const json_data = {
first_name: "William",
address1: "1690 Broadway Bldg 19, Ste 500, Fort Wayne, IN 46802",
};
mie.post("patients", json_data);
Updating records requires a JSON object that contains key-value pairs of fields to update.
mie.puts()
accepts the following parameters.
Name | Required? | Type | Description |
---|---|---|---|
endpoint |
Required | String | The specific API endpoint that you wish to query from. By default, WebChart has 191 endpoints which can be found in Control Panel → API of your WebChart. |
new_data |
{} No data to update |
Object | The new Data to post to an endpoint formatted as a JSON object. |
Response Format: none
NOTE: When creating a new record in a certain endpoint, it is not necessary to include all possible field-value pairs in the JSON object. For example, when creating a new patient, you do not have to fill in every field, such as
first_name
,ssn
,address3
, etc. A new patient will still be created with the fields you did include in the JSON object while the remaining fields will be left blank.
NOTE: By Default, not all endpoints support POST functionality. Double check that the API endpoint you want to use supports POST functionality.
Example File: here.
NOTE: As of version 1.0.7, installing @maxklema/mie-api-tools using
import
does not support patient documents.
An integral part of WebChart is being able to store patient documents. WebChart supports 28 different storage types and dozens of document types. For a complete chart explaining the storage types, see Custom Documents CSV API.
MIE API Tools allows you to download a collection of documents and upload a collection of documents.
MIE API Tools allows you to download as many documents as you want at once through querying. Download functionalities also allow for an optimization option, which allows for faster downloading. This can be achieved by using the mie.downloadDocs()
function.
The basic structure of the request is as follows:
mie.downloadDocs({ storage_type: 8 }, "downloads", 0);
mie.downloadDocs()
accepts the following parameters:
Name | Required? | Type | Description |
---|---|---|---|
queryString |
Required | Object | Fields that you want to query by to help locate specific documents for download. All documents that are returned by the query will be downloaded. |
directory |
Required | String | The directory you want to place your downloaded file in. |
optimization |
Optional | Integer | When left blank, optimization is turned off. When set to 1 , optimization is turned on. When optimization is on, filenames follow the format: doc_id.extension . When optimization is off, filenames follow the format: (pat_last_name)_(doc_id).extension . The difference is significant in that the patient's last name does not need to be queried before the document is downloaded, saving time. |
Response Format: For each file you attempt to download, the status of the upload will either be placed in /Download Status/success.csv
or in /Download Status/errors.csv
. Each file contains the headers FILE
, DOC_ID
, and STATUS
. Results are appended to the previous. If you delete statuses in either file, do not delete the headers, or the program may crash!
Similar to Retrieving Records by Querying, mie.downloadDocs()
also takes an object with queries. You can only filter by the fields that belong to the documents
endpoint. You can find a full list at Control Panel → API → Documents.
You are also able to chain queries together to download more specific documents. For example:\
mie.downloadDocs({ storage_type: 8, doc_type: "WCPHOTO" }, "downloads", 0);
Example Folder: here.
MIE API Tools allows you to upload any amount of documents at once by crafting a custom CSV file (see more below). With uploading capabilities, you are able to specify header fields.
The basic structure of the request is as follows:
mie.migrateData(jsonObject);
mie.migrateData()
accepts the following parameter:
Name | Required? | Type | Description |
---|---|---|---|
jsonObject |
Required | Object | The JSON Object with your migration configurations. |
To upload documents, you need a configuration JSON object. This object should include everything needed to make a data migration, including your WebChart credentials, output directory, mapping, threads, etc.
The following table describes each available property.
Property | Required? | Type | Description |
---|---|---|---|
username |
Variable | String | Your login username for your WebChart system. This field is required if mie.username.value is empty. |
password |
Variable | String | The login password for your WebChart system. This field is required if mie.password.value is empty. |
handle |
Variable | String | The name of your WebChart system. This field is required if mie.handle.value is empty. |
url |
Variable | String | The URL of your WebChart. It should look something similar to "https://practice.webchartnow.com/webchart.cgi" . This field is required if mie.URL.value is empty. |
input_data |
Required | Array Object | A list of filepaths to your input CSV file or files (you can have multiple data migrations). |
output_dir |
Optional | String | The root output directory where you want the results of your migrations to be stored. The default directory is Output/ . |
mapping |
Required | String / Object | The mapping translation for your input CSV headers. As of 1.0.0, pre-templated mappings one and two are available. To see these mappings, view mappings. To create custom mappings, see custom mappings. |
threads |
Optional | Integer | The number of worker threads you want working at once. The default is your number of CPU cores. |
csv_delimiter |
Variable | String | The character that separates data in your CSV files. The default delimiter is , . If you have a different delimiter, you need to include this header. |
Mappings translate your CSV headers to those that WebChart understands. This is necessary since different EHRs may have different headers and capitalizations.
For a detailed description of each header, see the 'Documents' API documentation on your WebChart.
Mapping One [one ] |
Mapping One [two ] |
Translation |
---|---|---|
filePath | filepath | file |
mrNumber | mrnumber | mrnumber |
docID | docid | doc_id |
revisionNumber | revisionnumber | revision_number |
userID | userid | user_id |
originID | originid | origin_id |
patID | patid | pat_id |
docType | doctype | doc_type |
storageType | storagetype | storage_type |
storageID | storageid | storage_id |
serviceLocation | servicelocation | service_location |
originDate | origindate | origin_date |
enterDate | enterdate | enter_date |
revisionDater | revisiondate | revision_date |
serviceDate | servicedate | service_date |
approxServiceDate | approxservicedate | approx_service_date |
interface | interface | interface |
inpatient | inpatient | inpatient |
allowForTeaching | allowforteaching | allow_for_teaching |
subject | subject | subject |
NOTE: Some EHRs may export data with headers not included in this list. In that case, you do not have to remove them from the CSV import file. They will be ignored by the program.
Custom mappings can also be created. In your config.json file, instead of assigning "mapping"
to a string, assign it to an object (which will be converted to a map).
As an example, for an input CSV file with the headers FILE_PATH
, PAT_ID
, MRNUMBER
, and DOC_TYPE
, a custom mapping can be created like this:
Config.json
"mapping": {
"FILE_PATH": "file",
"DOC_TYPE": "doc_type",
"MRNUMBER": "mrnumber",
"PAT_ID": "pat_id"
},
Here is an example of a CSV file containing 6 documents to be uploaded:
document_name,storage_type,doc_type,pat_id,subject,service_location,service_date
testing_files/29.jpg,8,WCPHOTO,18,"","",""
testing_files/Hart_109.txt,14,US,18,"this is my test lab!","OFFICE","2008-08-10 00:00:00"
testing_files/110.txt,14,US,18,"","OFFICE","2008-08-10 00:00:00"
testing_files/111.txt,14,US,18,"","OFFICE","2008-08-10 00:00:00"
testing_files/112.txt,14,US,18,"","OFFICE","2008-08-10 00:00:00"
testing_files/113.txt,14,US,18,"","OFFICE","2008-08-10 00:00:00"
For each input CSV file, a folder will be created with the name of that file for the job output. Inside, there will be a success.csv and an errors.csv file.
The headers in the sucess.csv file will be the same headers as the input CSV file.
If you re-run a migration job, do not delete your success.csv file. This file is used to keep track of files that have already been uploaded and will avoid uploading duplicate files in future jobs.
The headers in the errors.csv file will be the same headers as the input CSV file.
A new errors.csv file is generated for each migration job, so duplicate error messages will not appear.
Once you have created your CSV file containing all the files you want to upload, you can upload them by invoking the mie.uploadDocs()
function.
An input CSV file with mapping two
may look something like this
filepath,doctype,mrnumber
testing_files/29.jpg,WCPHOTO,10033
testing_files/110.txt,US,10033
testing_files/Hart_109.txt,US,10033
testing_files/Doe.html,ORDIM,10033
NOTE: While MIE API Tools supports most storage types, some are not supported (yet). However, there are work-arounds. For example,
storage_type: 13 (.htm)
files do not upload correctly to WebChart. Neither do.tif
or.tiff
files. As a result, all.htm
files are automatically converted to.html
files. Similarly, all.tif
and.tiff
files are automatically converted to.png
files.
Example Folder: here.
NOTE: AI capabilities are only supported in Node.js 18 and above.
If you simply want a overview of a patient's medical history and the ability to ask questions about them, MIE API Tools has built-in methods that allow you to do this. The package uses Google's Gemini LLM to help do this.
In order to use the AI capabilities, you must set mie.GeminiKey.value
to that of your Gemini API Key.
One way MIE API Tools utilizes AI is by providing summaries of patient medical history. This can be achieved by using the mie.summarizePatient()
method.
The basic structure of the request is as follows:
mie
.summarizePatient(18)
.then((result) => {
console.log(result);
})
.catch((err) => {
console.error(err);
});
mie.summarizePatient()
accepts the following parameters:
Name | Required? | Type | Description |
---|---|---|---|
patID |
Required | Integer | The ID of the patient for whom you want to summarize. |
adjective |
Optional | Object Key (String) | An adjective to specify the type of summary you would like to receive. Examples include brief , detailed , professional , etc. The default adjective is concise . |
model |
Optional | Object Key (String) | The Gemini AI Model you want to use to provide you a summary. The default model is gemini-1.5-flash . |
An example of using all three parameters in a request looks like this:
mie
.summarizePatient(18, { adjective: "brief", model: "gemini-1.5-flash" })
.then((result) => {
console.log(result);
});
Response Format: String.
An example response may look something like this:
William S. Hart is a 70-year-old male patient with a history of hypertension, congestive heart failure, atrial fibrillation, hyperlipidemia, coronary artery disease, peripheral vascular disease, and a history of smoking. He has also been diagnosed with asthma and COPD. Mr. Hart has a history of a right-sided stroke and anemia. He has a history of various procedures, including a CT scan of the head and an appendectomy. He has been prescribed a variety of medications throughout his medical history, including aspirin, lisinopril, lasix, and coumadin. Mr. Hart has had a variety of encounters with the healthcare system, including visits, consultations, and follow-up appointments. He has a history of an injury involving a fall while walking with boxes up a ramp. Mr. Hart is insured by Medicare and AARP.
Although mie.summarizePatient()
returns a promise, the method can also be invoked with await/async
.
async function myFunc() {
const summary = await mie.summarizePatient(18);
console.log(summary);
}
myFunc();
Example File: here.
Another way MIE API Tools utilizes AI is by allowing you to ask questions about a patient's medical history. This can be achieved by using the mie.queryPatient()
method.
The basic structure of the request is as follows:
mie
.queryPatient(18, "Does this patient have insurance?")
.then((response) => {
console.log(response);
})
.catch((err) => {
console.error(err);
});
mie.queryPatient()
accepts the following parameters:
Name | Required? | Type | Description |
---|---|---|---|
patID |
Required | Integer | The ID of the patient for whom you want to summarize. |
query (question) |
"" indicates no question |
String | The query, or question, you would like to ask about the patient. |
model |
Optional | Object Key (String) | The Gemini AI Model you want to use to provide you a summary. The default model is gemini-1.5-flash . |
An example of using all three parameters in a request looks like this:
mie
.queryPatient(18, "Does this patient have insurance?", {
model: "gemini-1.5-flash",
})
.then((response) => {
console.log(response);
});
Response Format: String.
An example response to the question Does this patient have insurance? can look like this:
Based on the provided JSON data, the patient, William S. Hart, has two insurance policies: "Medicare" and "AARP". Both policies have a relation insured as "Self", meaning the patient is the primary insured on both plans. This indicates that the patient has insurance coverage.
Although mie.queryPatient()
returns a promise, the method can also be invoked with await/async
.
async function myFunc() {
const query = await mie.queryPatient(18, "Does this patient have insurance?");
console.log(query);
}
myFunc();
Example File: here.
NOTE: As of version 1.0.7, installing @maxklema/mie-api-tools using
import
does not support ledger functionalities.
MIE API Tools provides a tool to log all requests, responses, and errors made. The package uses the Winston package for logging.
A Ledger can be toggled on or off. To toggle a ledger on, you must set mie.ledger.value
to "true"
. To toggle a ledger off, you must set mie.ledger.value
to "false"
.
Creating a new ledger can be achieved by invoking the mie.createLedger()
method.
A basic structure of the request is as follows:
const options = {
levels: ["info", "error"],
format: ["timestamps", "levels"],
storage: ["Logs/info.log", "Logs/errors.log"],
log_returned_data: "false",
};
mie.createLedger(options);
mie.createLedger()
accepts an object, which takes in the following parameters:
Name | Required? | Type | Description |
---|---|---|---|
levels |
Optional | String Array | The type of messages that you would like logged. info messages log all requests and responses made. error messages log all errors that were thrown. The default levels include both info and error messages. |
format |
Optional | String Array | Additional options you would like your ledger to include. timestamps will log the timestamp next to each message. levels will log the level (type of message) next to each message. This is useful if you are logging both info and error messages in the same file or in the console. The default formats enable both timestamps and levels . |
storage |
Optional | String or String Array | The location that the ledger will write to. If you want info and error messages to be written to two different files, set this option to an string array containing two different filepaths. NOTE: the first filepath will be where info messages are logged while the second filepath will be where error messages are logged. An error will be thrown if you specify two file paths but only have one level set. If you only want messages to be logged to one file, you must set storage to a String containing that filepath, not a String array. Lastly, if you want your messages to be logged to the console, set storage_type to "console" . The default storage location is "Logs/apiActivity.log" . |
log_returned_data |
Optional | String | A boolean, basically, that allows you to toggle loose-lipped data responses on or off. Setting this to true will log the data that is returned by each response. Setting this to false will not log the data is returned by each response. The default value is set to true . However, serious consideration should go into dictating this option as sensitive medical information will be logged. |
Response Format: Varies based on options specified.
Example One: All options enabled and level messages are seperated as such:
const options = {
levels: ["info", "error"],
format: ["timestamps", "levels"],
storage: ["Logs/info.log", "Logs/errors.log"],
log_returned_data: "true",
};
Logs/info.log:
2024-06-06T14:26:03.789Z [info]: Patient Query AI Request:
Patient ID: Sdasds
Gemini Model: "gemini-1.5-flash"
Query: "Does this patient have insurance?"
2024-06-06T14:26:03.793Z [info]: Patient Retrieval Request (Top Records Filtered):
Patient ID: Sdasds
2024-06-06T14:26:04.834Z [info]: Record Retrieval Request:
Request URL: "https://mieinternprac.webchartnow.com/webchart.cgi/json/R0VUL2RiL3BhdGllbnRzL3BhdF9pZD1TZGFzZHM="
Endpoint: "patients"
Query By: {"pat_id":"Sdasds"}
2024-06-06T14:26:04.933Z [info]: Record Retrieval Response
2024-06-06T14:26:04.933Z [info]: Patient Retrieval Response:
Failed to collect patient information with Patient ID Sdasds because no patient with that ID exists.
Errors/errors.log:
2024-06-06T14:26:04.936Z [error]: Bad Parameter: No patient exists with ID "Sdasds".
Example Two: All format options are toggled off and info
and error
messages are written to a single file.
const options = {
levels: ["info", "error"],
format: [],
storage: ["Logs/activity.log"],
log_returned_data: "true",
};
Logs/activity.log:
Patient Query AI Request:
Patient ID: Sdasds
Gemini Model: "gemini-1.5-flash"
Query: "Does this patient have insurance?"
Patient Retrieval Request (Top Records Filtered):
Patient ID: Sdasds
Record Retrieval Request:
Request URL: "https://mieinternprac.webchartnow.com/webchart.cgi/json/R0VUL2RiL3BhdGllbnRzL3BhdF9pZD1TZGFzZHM="
Endpoint: "patients"
Query By: {"pat_id":"Sdasds"}
Record Retrieval Response:
Data: {"request":"db/patients/pat_id=Sdasds","db":[],"meta":{"status":"204","msg":"No Content","success":["No data found"]}}
Patient Retrieval Response:
Failed to collect patient information with Patient ID Sdasds because no patient with that ID exists.
Bad Parameter: No patient exists with ID "Sdasds".
Example Folder: here. e
MIE has an API that integrates with WebChart (Electronic Health Record) Systems, allowing developers and healthcare professionals access to critical medical data from the WebChart server. However, to use the MIE API, it has to be run in a browser using HTTPS requests. This makes it hard for developers to use the API and can lead to problems including scalability issues, session management, and security concerns. Since browsers are not optimized for handling large numbers of connections and requests, with the API running in a browser, it may be harder to handle large volumes of requests. Browser sessions are not long, resulting in the API facing challenges in managing longer-term sessions. This can then affect the ability to maintain persistent connections across multiple API requests. Overall, integrating the MIE API with Webchart EHR systems through a browser is not the optimal method to utilize this important API.
This package allows for the API to be used seamlessly by being capable of performing both basic and advanced CRU operations, uploading and downloading documents, summarizing patient medical records and asking questions about patients, and logging all requests, responses, and errors as needed.