├── .gitignore ├── LICENSE ├── README.md ├── example-health-api ├── .gitignore ├── Dockerfile ├── create-secrets.sh ├── kubernetes-openshift.yaml ├── liberty-mysql │ └── mysql-driver.xml ├── liberty │ ├── jvm.options │ └── server.xml ├── pom.xml ├── samples │ ├── data.zip │ └── health_schema.sql └── src │ └── main │ ├── java │ └── com │ │ └── ibm │ │ └── examplehealth │ │ ├── Allergy.java │ │ ├── AllergyList.java │ │ ├── Appointment.java │ │ ├── AppointmentList.java │ │ ├── CityCounts.java │ │ ├── Credentials.java │ │ ├── ExampleResource.java │ │ ├── JAXRSConfiguration.java │ │ ├── Message.java │ │ ├── Observation.java │ │ ├── Organization.java │ │ ├── Patient.java │ │ ├── Prescription.java │ │ ├── Provider.java │ │ └── SynthData.java │ ├── resources │ └── META-INF │ │ └── persistence.xml │ └── webapp │ └── WEB-INF │ └── beans.xml ├── generate ├── README.md ├── generate.sh └── package.json ├── readme_images ├── architecture.png ├── new_architecture.png └── original_architecture.png ├── screenshots ├── Screen Shot 2019-07-11 at 1.47.00 PM.png ├── Screen Shot 2019-07-11 at 2.00.29 PM.png ├── mysql.png ├── oom.png ├── s1.png ├── s2.png ├── s3.png ├── s4.png └── s5.png └── terraform ├── main.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | 8 | *.iml 9 | .idea/ 10 | .~ 11 | .classpath 12 | .project 13 | .settings/ 14 | .nfs* 15 | 16 | .terraform 17 | *.tfstate.backup -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example Health JEE Application on Openshift 2 | 3 | This project is a conceptual Java EE application running on Open Liberty for a health records system, designed to showcase best in class integration of modern cloud technology running on OpenShift. 4 | 5 | ## Example Health Context 6 | 7 | Example Health is a conceptual healthcare/insurance type company. It has been around a long time, and has 100s of thousands of patient records. Example's health records look very similar to the health records of most insurance companies. 8 | 9 | Originally, Example Health used a monolithic application structure for their application. Their application structure was a full stack Java application running on WebSphere connected to a DB2 database on System z. Here's what the original architecture for Example Health looked like: 10 | 11 | ![](readme_images/original_architecture.png) 12 | 13 | Recently, Example Health decided to modernize their application and break it up into microservices. They decided to move to a SQL database connected to a Java EE application running on Open Liberty for the business logic and a Node.js application for the Patient UI. In addition, Example Health also decided to bring these applications to Openshift in the Cloud. The new current architecture for Example Health looks like this: 14 | 15 | ![](readme_images/new_architecture.png) 16 | 17 | Since moving to Openshift, Example Health has expanded to include new microservices that include an Admin application and an Analytics application. These along with the Patient UI can be found in seperate code patterns: 18 | 19 | 20 | 1. [Creating a Health Data Analytics App](https://developer.ibm.com/patterns/creating-a-health-data-analytics-app-with-legacy-mainframe-code-and-cloud/) 21 | 22 | 2. [Application modernization with the Source-to-Image toolkit and OpenShift](https://github.com/IBM/node-s2i-openshift) 23 | 24 | 3. [PHP Admin Front-end](https://github.com/IBM/summit-health-admin) 25 | 26 | 27 | # Architecture 28 | 29 | ![](readme_images/architecture.png) 30 | 31 | 1. User makes a call to one of the APIs for the Java EE application which is located in Openshift's application load balancer. 32 | 2. The API in Openshift's application load balancer triggers the API endpoint code in the Java EE application that is running on an Open Liberty server in a Docker container on Openshift. 33 | 3. The Java EE application queries the MySQL database to get the desired data. 34 | 4. The MySQL database sends back the data to the Java EE application where it gets handled accordingly. 35 | 5. The data gets configured into JSON format that gets returned to the API and User. 36 | 37 | # Steps 38 | 39 | 1. Install the following prerequisite tools. 40 | * A Java 8 (or higher) JDK such as [OpenJDK](https://openjdk.java.net/install/index.html) 41 | * [Maven](https://maven.apache.org/download.cgi) 42 | * [Docker](https://www.docker.com/get-started) 43 | * [IBM Cloud CLI](https://cloud.ibm.com/docs/cli?topic=cloud-cli-getting-started) 44 | * [OpenShift (oc) CLI](https://www.okd.io/download.html) 45 | * [MySQL client](https://dev.mysql.com/downloads/) 46 | 47 | 2. [Sign up for an IBM Cloud account](https://cloud.ibm.com/docs/account?topic=account-signup) if you do not have one. 48 | You must have a Pay-As-You-Go or Subscription account to deploy this code pattern. See https://cloud.ibm.com/docs/account?topic=account-upgrading-account to upgrade your account. 49 | 50 | 3. Create a [IBM Cloud Red Hat OpenShift Container Platform cluster](https://cloud.ibm.com/docs/containers?topic=containers-openshift_tutorial) 51 | 52 | 4. Create a [Compose for MySQL database](https://cloud.ibm.com/catalog/services/compose-for-mysql). After the database is provisioned, make note of its URL, port, user and password. 53 | 54 | 5. Clone this project. 55 | ``` 56 | git clone https://github.com/IBM/example-health-jee-openshift.git 57 | ``` 58 | 59 | 6. Create the database and tables using a MySQL client. Import the SQL schema for the the [Synthea](https://github.com/synthetichealth/synthea) simulated patient record data using the SQL file at: `example-health-api/samples/health_schema.sql`. 60 | 61 | 7. Build the Java EE application. 62 | ``` 63 | cd example-health-api 64 | mvn package 65 | ``` 66 | 67 | 8. Build the Java EE docker image. 68 | ``` 69 | docker build -t ol-example-health:1 . 70 | ``` 71 | 72 | 9. Create a repository in your dockerhub account and push the Java EE docker image to it. (Substitute your account name into the commands.) 73 | 74 | ``` 75 | docker tag ol-example-health:1 YOURACCOUNT/ol-example-health:1 76 | docker login -u YOURACCOUNT 77 | docker push YOURACCOUNT/ol-example-health:1 78 | ``` 79 | 80 | 10. Create a project (like a namespace) in OpenShift. This will create a new project and set it as the working project where pods and services will get deployed. 81 | 82 | ``` 83 | oc new-project health 84 | ``` 85 | 86 | 11. Edit the file `example-health-api/kubernetes-openshift.yaml` to change the `image` key to your docker image. 87 | 88 | 12. Set the secret values for your MySQL cloud deployment in the `example-health-api/create-secrets.sh` script. All the necesssary values can be found in the IBM Cloud MySQL service credentials page: 89 | 90 | ![memory](screenshots/mysql.png) 91 | 92 | > NOTE: The connection URL would resemble the following. Substitute the server name and port from the Cloud service page above. The value of `DB_NAME` use the database created and populated with the tables in step 6 above. 93 | ``` 94 | --from-literal=db_host="jdbc:mysql://$HOST:$PORT/$DB_NAME?sessionVariables=sql_mode=''" 95 | ``` 96 | 97 | Run the script to load the secrets into the OpenShift project. 98 | 99 | ``` 100 | $ ./create-secrets.sh 101 | secret/db-secrets created 102 | ``` 103 | 104 | The Open Liberty DataSource configuration in `server.xml` uses environment variables injected into the 105 | container by the deployment yaml via Kubernetes secrets to set database access parameters: 106 | 107 | ```xml 108 | 110 | 111 | 114 | ``` 115 | 116 | 13. Deploy the application to your cluster. 117 | ```` 118 | oc apply -f kubernetes-openshift.yaml 119 | ```` 120 | 121 | 14. Create a route to expose the application to the internet. 122 | ```` 123 | oc expose svc example-health-api 124 | ```` 125 | 126 | 15. Verify that the application is working. First obtain the hostname assigned to the route. 127 | ```` 128 | oc get route example-health-api 129 | 130 | NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD 131 | example-health-api example-health-api-health.****.appdomain.cloud example-health-api http None 132 | 133 | ```` 134 | 135 | In a browser window, navigate to `/openapi/ui/`. An OpenAPI specification of the endpoints and operations supported by the Java EE application appears. 136 | 137 | 138 | 16. Generate synthentic patient health records and populate the MySQL database by running the `generate.sh` script in `generate/`. Refer to the script's [README](generate/README.md) for instructions on how to run the script. 139 | 140 | > NOTE: In our testing, the script populates the MySQL database at about 125 patients per hour. 141 | 142 | > NOTE: This directive in `liberty.xml` is necessary to allow long-running patient load operations via REST: ``. The default timeout of 120 seconds is too 143 | short to batch patients at 50 per call, as the current script is configured. 144 | 145 | 146 | Once the application is up and running, the OpenAPI UI will allow you to browse the available APIs: 147 | 148 | ![memory](screenshots/s3.png) 149 | 150 | # SQL Schema 151 | 152 | The SQL schema in for `Synthea` derived data imported into Example Health uses this logical pattern and maps tables to Java classes under `src/main/java/com/ibm/examplehealth` mapped using JPA annotation. 153 | 154 | ```java 155 | Allergy.java :@Table(name="Allergies") 156 | Appointment.java :@Table(name="Appointments") 157 | Provider.java :@Table(name="Providers") 158 | Organization.java:@Table(name="Organizations") 159 | Prescription.java:@Table(name="Prescriptions") 160 | Observation.java: @Table(name="Observations") 161 | Patient.java: @Table(name="Patients") 162 | ``` 163 | 164 | ![tables](screenshots/s5.png) 165 | 166 | 167 | # Open Liberty in OpenShift 168 | 169 | ## Build a Liberty container for using JPA with a JDBC driver 170 | 171 | This project builds its OpenLiberty container based on the RedHat UBI, Universal Base Image: 172 | https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image 173 | 174 | To ensure that your Docker container works in the more security conscious environment of OpenShift, use Libert 19.0.0.5 or higher 175 | see: https://openliberty.io/blog/2019/03/28/microprofile22-liberty-19003.html#docker. In addition, to install the JDBC driver, note 176 | that `chown` option to `ADD` and the `chmod` needed to give the JVM permission to read the JDBC driver. 177 | 178 | 179 | Part of the `Dockerfile` used to build the image downloads the MySQL JDBC driver to connect to our cloud MySQL instance. 180 | 181 | ``` 182 | ADD --chown=default:root https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.16/mysql-connector-java-8.0.16.jar ${INSTALL_DIR}lib/mysql-connector-java-8.0.16.jar 183 | RUN chmod 644 ${INSTALL_DIR}lib/mysql-connector-java-8.0.16.jar 184 | COPY liberty-mysql/mysql-driver.xml ${CONFIG_DIR}configDropins/defaults/ 185 | ``` 186 | 187 | The `persistence.xml` specifies the driver details that is injected as the default persistence context via 188 | CDI: 189 | 190 | ```xml 191 | 195 | 196 | 197 | 199 | 200 | ``` 201 | 202 | We don't need to specify any persistence context in this annotation because only one is defined: 203 | 204 | ```java 205 | @PersistenceContext 206 | EntityManager entityManager; 207 | ``` 208 | 209 | # Memory management during bulk loading 210 | 211 | ## Clear EntityManager during bulk load 212 | 213 | Because by default transactions are handled on a per call basis, when loading many records (100s of MBs) via `generate`, we noticed memory usage rose dramatically as the EntityManager instantiated Java objects representing each database table. Running `clear()` during batch 214 | processing allowed memory to be reclaimed after entites were pushed to MySQL. 215 | 216 | ```java 217 | private void flushBatch(int size, int cnt, String type) { 218 | if ( (cnt % batchSize == 0) || (size == cnt) ) { 219 | entityManager.flush(); 220 | entityManager.clear(); 221 | } 222 | } 223 | ``` 224 | 225 | ## Update the gateway timeout settings to allow long running APIs. 226 | 227 | The default OpenShift timeout for the gateway is 30 seconds, too short for long running REST calls like the `generate` endpoint to load health data. It's necessary to set the route timeout to a longer value for the route defined for the health API: 228 | 229 | ``` 230 | # oc annotate route example-api --overwrite haproxy.router.openshift.io/timeout=60m 231 | route.route.openshift.io/example-api annotated 232 | ``` 233 | 234 | # Setting up JPA for OpenLiberty 235 | 236 | We set up a Data Source to allow Open Liberty to manage our connections to the MySQL database via 237 | the MySQL JDBC driver. For more details, see this Open Liberty guide: https://openliberty.io/guides/jpa-intro.html 238 | 239 | The `persistence.xml` specifies the driver details that is injected as the default persistence context via 240 | CDI: 241 | 242 | ```xml 243 | 247 | 248 | 249 | 251 | 252 | ``` 253 | 254 | We don't need to specify any persistence context in this annotation because only one is defined: 255 | 256 | ```java 257 | @PersistenceContext 258 | EntityManager entityManager; 259 | ``` 260 | 261 | 262 | # Memory management during bulk loading 263 | 264 | ## Clear EntityManager during bulk load 265 | 266 | Because by default transactions are handled on a per call basis, when loading many records (100s of MBs) via `generate`, we noticed memory usage rose dramatically as the EntityManager instantiated Java objects representing each database table. Running `clear()` during batch 267 | processing allowed memory to be reclaimed after entites were pushed to MySQL. 268 | 269 | ```java 270 | private void flushBatch(int size, int cnt, String type) { 271 | if ( (cnt % batchSize == 0) || (size == cnt) ) { 272 | entityManager.flush(); 273 | entityManager.clear(); 274 | } 275 | } 276 | ``` 277 | 278 | ## Increase JVM Heap size 279 | 280 | 281 | The OpenShift dashboard is helpful in problem determination, in our case memory exhaustion due to the default 1GB JVM heap size during bulk loading via JPA: 282 | 283 | Memory exhastion (hard limit at 1GB and rapid drop off as the call fails and cleanup occurs): 284 | 285 | ![memory](screenshots/oom.png) 286 | 287 | 288 | To solve this problem, we an option in `jvm.options` to increase the amount of memory available to the JVM above the default 1GB heap size: 289 | 290 | ``` 291 | -Xmx4096m 292 | ``` 293 | 294 | And added this to the image by adding this line in the `Dockerfile`: 295 | 296 | 297 | ``` 298 | COPY liberty/jvm.options $CONFIG_DIR 299 | ``` 300 | 301 | 302 | The OpenShift monitoring dashboard shows how container memory is able to exceed the 1GB threshold when necessary (in this case, the peak is caused by JSON-B parsing JSON inpout to the REST call that generates simulated health records): 303 | 304 | ![memory](screenshots/s1.png) 305 | 306 | 307 | 308 | 309 | # License 310 | 311 | This code pattern is licensed under the Apache License, Version 2. Separate third-party code objects invoked within this code pattern are licensed by their respective providers pursuant to their own separate licenses. Contributions are subject to the [Developer Certificate of Origin, Version 1.1](https://developercertificate.org/) and the [Apache License, Version 2](https://www.apache.org/licenses/LICENSE-2.0.txt). 312 | 313 | [Apache License FAQ](https://www.apache.org/foundation/license-faq.html#WhatDoesItMEAN) 314 | -------------------------------------------------------------------------------- /example-health-api/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | 8 | *.iml 9 | .idea/ 10 | .~ 11 | .classpath 12 | .project 13 | .settings/ 14 | 15 | -------------------------------------------------------------------------------- /example-health-api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:javaee8-ubi-min 2 | 3 | ENV INSTALL_DIR /opt/ol/wlp/ 4 | ENV CONFIG_DIR /opt/ol/wlp/usr/servers/defaultServer/ 5 | 6 | ADD --chown=default:root https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.16/mysql-connector-java-8.0.16.jar ${INSTALL_DIR}lib/mysql-connector-java-8.0.16.jar 7 | RUN chmod 644 ${INSTALL_DIR}lib/mysql-connector-java-8.0.16.jar 8 | COPY liberty-mysql/mysql-driver.xml ${CONFIG_DIR}configDropins/defaults/ 9 | 10 | COPY liberty/server.xml $CONFIG_DIR 11 | COPY liberty/jvm.options $CONFIG_DIR 12 | 13 | COPY target/health-api.war /opt/ol/wlp/usr/servers/defaultServer/apps 14 | EXPOSE 9080 15 | -------------------------------------------------------------------------------- /example-health-api/create-secrets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | oc create secret generic db-secrets \ 3 | --from-literal=db_username= \ 4 | --from-literal=db_password= \ 5 | --from-literal=db_host="jdbc:mysql:////?sessionVariables=sql_mode=''" 6 | -------------------------------------------------------------------------------- /example-health-api/kubernetes-openshift.yaml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: example-health-api 5 | labels: 6 | app: example-health-api 7 | spec: 8 | selector: 9 | app: example-health-api 10 | ports: 11 | - port: 9080 12 | name: http 13 | type: NodePort 14 | --- 15 | 16 | kind: Deployment 17 | apiVersion: apps/v1beta1 18 | metadata: 19 | name: example-health-api 20 | spec: 21 | replicas: 1 22 | template: 23 | metadata: 24 | annotations: 25 | sidecar.istio.io/inject: "true" 26 | labels: 27 | app: example-health-api 28 | version: v1 29 | spec: 30 | containers: 31 | - name: example-health-api 32 | image: ykoyfman/ol-example-health:latest 33 | ports: 34 | - containerPort: 9080 35 | imagePullPolicy: Always 36 | env: 37 | - name: ENV_MYSQL_URL 38 | valueFrom: 39 | secretKeyRef: 40 | name: db-secrets 41 | key: db_host 42 | - name: ENV_MYSQL_USER 43 | valueFrom: 44 | secretKeyRef: 45 | name: db-secrets 46 | key: db_username 47 | - name: ENV_MYSQL_PWD 48 | valueFrom: 49 | secretKeyRef: 50 | name: db-secrets 51 | key: db_password 52 | restartPolicy: Always 53 | --- 54 | -------------------------------------------------------------------------------- /example-health-api/liberty-mysql/mysql-driver.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example-health-api/liberty/jvm.options: -------------------------------------------------------------------------------- 1 | -Xmx4096m 2 | 3 | -------------------------------------------------------------------------------- /example-health-api/liberty/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | javaee-8.0 6 | mpMetrics-1.1 7 | monitor-1.0 8 | microProfile-2.1 9 | 10 | 11 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | admin 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /example-health-api/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.ibm.examplehealth 5 | health-api 6 | 1.0-SNAPSHOT 7 | war 8 | 9 | 10 | 11 | javax 12 | javaee-api 13 | 8.0 14 | provided 15 | 16 | 17 | 18 | 19 | health-api 20 | 21 | 22 | 23 | 1.8 24 | 1.8 25 | false 26 | UTF-8 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /example-health-api/samples/data.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/example-health-api/samples/data.zip -------------------------------------------------------------------------------- /example-health-api/samples/health_schema.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.26, for Linux (x86_64) 2 | -- 3 | -- Host: sl-us-south-1-portal.51.dblayer.com Database: ex 4 | -- ------------------------------------------------------ 5 | -- Server version 5.7.22-log 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- GTID state at the beginning of the backup 20 | -- 21 | 22 | 23 | -- 24 | -- Table structure for table `Allergies` 25 | -- 26 | 27 | DROP TABLE IF EXISTS `Allergies`; 28 | /*!40101 SET @saved_cs_client = @@character_set_client */; 29 | /*!40101 SET character_set_client = utf8 */; 30 | CREATE TABLE `Allergies` ( 31 | `id` int(11) NOT NULL AUTO_INCREMENT, 32 | `patient_id` varchar(36) DEFAULT NULL, 33 | `allergy_start` varchar(10) DEFAULT NULL, 34 | `allergy_stop` varchar(10) DEFAULT NULL, 35 | `description` varchar(50) DEFAULT NULL, 36 | PRIMARY KEY (`id`) 37 | ) ENGINE=InnoDB AUTO_INCREMENT=2542 DEFAULT CHARSET=latin1; 38 | /*!40101 SET character_set_client = @saved_cs_client */; 39 | 40 | -- 41 | -- Table structure for table `Appointments` 42 | -- 43 | 44 | DROP TABLE IF EXISTS `Appointments`; 45 | /*!40101 SET @saved_cs_client = @@character_set_client */; 46 | /*!40101 SET character_set_client = utf8 */; 47 | CREATE TABLE `Appointments` ( 48 | `id` int(11) NOT NULL AUTO_INCREMENT, 49 | `appointment_id` varchar(36) DEFAULT NULL, 50 | `date` varchar(10) DEFAULT NULL, 51 | `time` varchar(10) DEFAULT NULL, 52 | `patient_id` varchar(36) DEFAULT NULL, 53 | `provider_id` varchar(36) DEFAULT NULL, 54 | PRIMARY KEY (`id`) 55 | ) ENGINE=InnoDB AUTO_INCREMENT=452763 DEFAULT CHARSET=latin1; 56 | /*!40101 SET character_set_client = @saved_cs_client */; 57 | 58 | -- 59 | -- Table structure for table `MESSAGE` 60 | -- 61 | 62 | DROP TABLE IF EXISTS `MESSAGE`; 63 | /*!40101 SET @saved_cs_client = @@character_set_client */; 64 | /*!40101 SET character_set_client = utf8 */; 65 | CREATE TABLE `MESSAGE` ( 66 | `ID` bigint(20) NOT NULL, 67 | `MESSAGE` varchar(255) DEFAULT NULL, 68 | PRIMARY KEY (`ID`) 69 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 70 | /*!40101 SET character_set_client = @saved_cs_client */; 71 | 72 | -- 73 | -- Table structure for table `Observations` 74 | -- 75 | 76 | DROP TABLE IF EXISTS `Observations`; 77 | /*!40101 SET @saved_cs_client = @@character_set_client */; 78 | /*!40101 SET character_set_client = utf8 */; 79 | CREATE TABLE `Observations` ( 80 | `id` int(11) NOT NULL AUTO_INCREMENT, 81 | `date` varchar(10) DEFAULT NULL, 82 | `code` varchar(20) DEFAULT NULL, 83 | `description` varchar(100) DEFAULT NULL, 84 | `numeric_value` varchar(10) DEFAULT NULL, 85 | `character_value` varchar(60) DEFAULT NULL, 86 | `units` varchar(22) DEFAULT NULL, 87 | `patient_id` varchar(36) DEFAULT NULL, 88 | PRIMARY KEY (`id`) 89 | ) ENGINE=InnoDB AUTO_INCREMENT=2008943 DEFAULT CHARSET=latin1; 90 | /*!40101 SET character_set_client = @saved_cs_client */; 91 | 92 | -- 93 | -- Table structure for table `Organizations` 94 | -- 95 | 96 | DROP TABLE IF EXISTS `Organizations`; 97 | /*!40101 SET @saved_cs_client = @@character_set_client */; 98 | /*!40101 SET character_set_client = utf8 */; 99 | CREATE TABLE `Organizations` ( 100 | `id` int(11) NOT NULL AUTO_INCREMENT, 101 | `organization_id` varchar(36) DEFAULT NULL, 102 | `name` varchar(400) DEFAULT NULL, 103 | `address` varchar(50) DEFAULT NULL, 104 | `city` varchar(30) DEFAULT NULL, 105 | `state` varchar(3) DEFAULT NULL, 106 | `postcode` varchar(10) DEFAULT NULL, 107 | PRIMARY KEY (`id`) 108 | ) ENGINE=InnoDB AUTO_INCREMENT=7127 DEFAULT CHARSET=latin1; 109 | /*!40101 SET character_set_client = @saved_cs_client */; 110 | 111 | -- 112 | -- Table structure for table `Patients` 113 | -- 114 | 115 | DROP TABLE IF EXISTS `Patients`; 116 | /*!40101 SET @saved_cs_client = @@character_set_client */; 117 | /*!40101 SET character_set_client = utf8 */; 118 | CREATE TABLE `Patients` ( 119 | `id` int(11) NOT NULL AUTO_INCREMENT, 120 | `patient_id` varchar(36) DEFAULT NULL, 121 | `first_name` varchar(30) DEFAULT NULL, 122 | `last_name` varchar(30) DEFAULT NULL, 123 | `birthdate` varchar(10) DEFAULT NULL, 124 | `gender` varchar(2) DEFAULT NULL, 125 | `address` varchar(50) DEFAULT NULL, 126 | `city` varchar(60) DEFAULT NULL, 127 | `postcode` varchar(10) DEFAULT NULL, 128 | `user_id` varchar(50) DEFAULT NULL, 129 | `password` varchar(50) DEFAULT NULL, 130 | PRIMARY KEY (`id`) 131 | ) ENGINE=InnoDB AUTO_INCREMENT=4735 DEFAULT CHARSET=latin1; 132 | /*!40101 SET character_set_client = @saved_cs_client */; 133 | 134 | -- 135 | -- Table structure for table `Prescriptions` 136 | -- 137 | 138 | DROP TABLE IF EXISTS `Prescriptions`; 139 | /*!40101 SET @saved_cs_client = @@character_set_client */; 140 | /*!40101 SET character_set_client = utf8 */; 141 | CREATE TABLE `Prescriptions` ( 142 | `id` int(11) NOT NULL AUTO_INCREMENT, 143 | `medication_id` varchar(10) DEFAULT NULL, 144 | `drug_name` varchar(50) DEFAULT NULL, 145 | `patient_id` varchar(36) DEFAULT NULL, 146 | `reason` varchar(100) DEFAULT NULL, 147 | PRIMARY KEY (`id`) 148 | ) ENGINE=InnoDB AUTO_INCREMENT=88189 DEFAULT CHARSET=latin1; 149 | /*!40101 SET character_set_client = @saved_cs_client */; 150 | 151 | -- 152 | -- Table structure for table `Providers` 153 | -- 154 | 155 | DROP TABLE IF EXISTS `Providers`; 156 | /*!40101 SET @saved_cs_client = @@character_set_client */; 157 | /*!40101 SET character_set_client = utf8 */; 158 | CREATE TABLE `Providers` ( 159 | `id` int(11) NOT NULL AUTO_INCREMENT, 160 | `provider_id` varchar(36) DEFAULT NULL, 161 | `organization_id` varchar(36) DEFAULT NULL, 162 | `name` varchar(60) DEFAULT NULL, 163 | `speciality` varchar(30) DEFAULT NULL, 164 | PRIMARY KEY (`id`) 165 | ) ENGINE=InnoDB AUTO_INCREMENT=7269 DEFAULT CHARSET=latin1; 166 | /*!40101 SET character_set_client = @saved_cs_client */; 167 | 168 | -- 169 | -- Table structure for table `SEQUENCE` 170 | -- 171 | 172 | DROP TABLE IF EXISTS `SEQUENCE`; 173 | /*!40101 SET @saved_cs_client = @@character_set_client */; 174 | /*!40101 SET character_set_client = utf8 */; 175 | CREATE TABLE `SEQUENCE` ( 176 | `SEQ_NAME` varchar(50) NOT NULL, 177 | `SEQ_COUNT` decimal(38,0) DEFAULT NULL, 178 | PRIMARY KEY (`SEQ_NAME`) 179 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 180 | /*!40101 SET character_set_client = @saved_cs_client */; 181 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 182 | 183 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 184 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 185 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 186 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 187 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 188 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 189 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 190 | 191 | -- Dump completed on 2019-07-17 9:59:13 192 | -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Allergy.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.*; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | 7 | /** 8 | * The persistent class for the Allergies database table. 9 | * 10 | */ 11 | @Entity 12 | @Table(name="Allergies") 13 | @NamedQuery(name="Allergy.findAll", query="SELECT a FROM Allergy a") 14 | @NamedQuery(name="Allergy.getAllergies", query="SELECT NEW com.ibm.examplehealth.AllergyList(p.patientId, p.birthdate, p.city, p.postcode, a.description, " + 15 | "a.allergyStart, a.allergyStop) FROM Patient p JOIN Allergy a ON p.patientId = a.patientId") 16 | 17 | public class Allergy implements Serializable { 18 | private static final long serialVersionUID = 1L; 19 | 20 | @Id 21 | private int id; 22 | 23 | @JsonbProperty("START") 24 | @Column(name="allergy_start") 25 | private String allergyStart; 26 | 27 | @JsonbProperty("STOP") 28 | @Column(name="allergy_stop") 29 | private String allergyStop; 30 | 31 | @JsonbProperty("DESCRIPTION") 32 | private String description; 33 | 34 | @JsonbProperty("PATIENT") 35 | @Column(name="patient_id") 36 | private String patientId; 37 | 38 | public Allergy() { 39 | } 40 | 41 | public int getId() { 42 | return this.id; 43 | } 44 | 45 | public void setId(int id) { 46 | this.id = id; 47 | } 48 | 49 | public String getAllergyStart() { 50 | return this.allergyStart; 51 | } 52 | 53 | public void setAllergyStart(String allergyStart) { 54 | this.allergyStart = allergyStart; 55 | } 56 | 57 | public String getAllergyStop() { 58 | return this.allergyStop; 59 | } 60 | 61 | public void setAllergyStop(String allergyStop) { 62 | this.allergyStop = allergyStop; 63 | } 64 | 65 | public String getDescription() { 66 | return this.description; 67 | } 68 | 69 | public void setDescription(String description) { 70 | this.description = description; 71 | } 72 | 73 | public String getPatientId() { 74 | return this.patientId; 75 | } 76 | 77 | public void setPatientId(String patientId) { 78 | this.patientId = patientId; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/AllergyList.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | import javax.json.bind.annotation.JsonbProperty; 3 | 4 | public class AllergyList { 5 | 6 | private String patient_id; 7 | private String birthdate; 8 | private String city; 9 | private String postcode; 10 | private String description; 11 | private String start_date; 12 | private String stop_date; 13 | 14 | public AllergyList(String patient_id, String birthdate, String city, String postcode, String description, 15 | String start_date, String stop_date) { 16 | this.patient_id = patient_id; 17 | this.birthdate = birthdate; 18 | this.city = city; 19 | this.postcode = postcode; 20 | this.description = description; 21 | this.start_date = start_date; 22 | this.stop_date = stop_date; 23 | } 24 | 25 | @JsonbProperty("PATIENT_NUM") 26 | public String getPatient_id() { 27 | return patient_id; 28 | } 29 | 30 | public void setPatient_id(String patient_id) { 31 | this.patient_id = patient_id; 32 | } 33 | 34 | @JsonbProperty("BIRTHDATE") 35 | public String getBirthdate() { 36 | return birthdate; 37 | } 38 | 39 | public void setBirthdate(String birthdate) { 40 | this.birthdate = birthdate; 41 | } 42 | 43 | @JsonbProperty("CITY") 44 | public String getCity() { 45 | return city; 46 | } 47 | 48 | public void setCity(String city) { 49 | this.city = city; 50 | } 51 | 52 | @JsonbProperty("POSTCODE") 53 | public String getPostcode() { 54 | return postcode; 55 | } 56 | 57 | public void setPostcode(String postcode) { 58 | this.postcode = postcode; 59 | } 60 | 61 | @JsonbProperty("DESCRIPTION") 62 | public String getDescription() { 63 | return description; 64 | } 65 | 66 | public void setDescription(String description) { 67 | this.description = description; 68 | } 69 | 70 | @JsonbProperty("ALLERGY_START") 71 | public String getStart_date() { 72 | return start_date; 73 | } 74 | 75 | public void setStart_date(String start_date) { 76 | this.start_date = start_date; 77 | } 78 | 79 | @JsonbProperty("ALLERGY_STOP") 80 | public String getStop_date() { 81 | return stop_date; 82 | } 83 | 84 | public void setStop_date(String stop_date) { 85 | this.stop_date = stop_date; 86 | } 87 | } -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Appointment.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.*; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | 7 | 8 | /** 9 | * The persistent class for the Appointments database table. 10 | * 11 | */ 12 | @Entity 13 | @Table(name="Appointments") 14 | @NamedQuery(name="Appointment.findAll", query="SELECT a FROM Appointment a") 15 | 16 | /* 17 | SELECT org.state, org.postcode, org.city, org.name, org.address, 18 | pat.first_name, pat.last_name, app.date, app.time, app.patient_id, prov.name, prov.speciality 19 | FROM Providers prov, Appointments app, Patients pat, Organizations org 20 | WHERE app.provider_id=prov.organization_id 21 | AND app.patient_id="bd2b67d0-f318-471d-83a6-1b130673d9f3" 22 | AND pat.patient_id="bd2b67d0-f318-471d-83a6-1b130673d9f3" 23 | AND org.organization_id=app.provider_id; 24 | 25 | Constructor field order for use in NamedQuery: 26 | 27 | public AppointmentList(String patient_id, String first_name, String last_name, String date, String time, 28 | String doc_name, String field, String office_name, String office_addr, String office_city, String office_state, 29 | String office_zip) { 30 | */ 31 | @NamedQuery(name="Appointment.getAppointments", 32 | query="SELECT NEW com.ibm.examplehealth.AppointmentList(p.patientId, p.firstName, p.lastName, app.date, app.time, " + 33 | "prov.name, prov.speciality, org.name, org.address, org.city, org.state, org.postcode) " + 34 | "FROM Patient p, Appointment app, Provider prov, Organization org " + 35 | "WHERE app.patientId=:pid AND p.patientId=:pid AND app.providerId=prov.organizationId AND org.organizationId=app.providerId") 36 | 37 | public class Appointment implements Serializable { 38 | private static final long serialVersionUID = 1L; 39 | 40 | @Id 41 | private int id; 42 | 43 | @JsonbProperty("Id") 44 | @Column(name="appointment_id") 45 | private String appointmentId; 46 | 47 | @JsonbProperty("START") 48 | private String date; 49 | 50 | private String time; 51 | 52 | @JsonbProperty("PATIENT") 53 | @Column(name="patient_id") 54 | private String patientId; 55 | 56 | @JsonbProperty("PROVIDER") 57 | @Column(name="provider_id") 58 | private String providerId; 59 | 60 | public Appointment() { 61 | } 62 | 63 | public int getId() { 64 | return this.id; 65 | } 66 | 67 | public void setId(int id) { 68 | this.id = id; 69 | } 70 | 71 | public String getAppointmentId() { 72 | return this.appointmentId; 73 | } 74 | 75 | public void setAppointmentId(String appointmentId) { 76 | this.appointmentId = appointmentId; 77 | } 78 | 79 | public String getDate() { 80 | return this.date; 81 | } 82 | 83 | public void setDate(String date) { 84 | this.date = date; 85 | } 86 | 87 | public String getTime() { 88 | return this.time; 89 | } 90 | 91 | public void setTime(String time) { 92 | this.time = time; 93 | } 94 | 95 | public String getPatientId() { 96 | return this.patientId; 97 | } 98 | 99 | public void setPatientId(String patientId) { 100 | this.patientId = patientId; 101 | } 102 | 103 | public String getProviderId() { 104 | return this.providerId; 105 | } 106 | 107 | public void setProviderId(String providerId) { 108 | this.providerId = providerId; 109 | } 110 | } -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/AppointmentList.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | import javax.json.bind.annotation.JsonbProperty; 3 | 4 | /* 5 | JSON response format: 6 | 7 | { 8 | "PATIENTID": 1, 9 | "FIRSTNAME": "Ralph ", 10 | "LASTNAME": "Dalmeida ", 11 | "APPT_DATE": "2019-02-21", 12 | "APPT_TIME": "02.00.00", 13 | "DR_NAME": "Aaron Stone ", 14 | "MED_FIELD": "Internal Medicine ", 15 | "OFF_NAME": "Toronto Medical ", 16 | "OFF_ADDR": "555 14th Street ", 17 | "OFF_CITY": "Toronto ", 18 | "OFF_STATE": "ON", 19 | "OFF_ZIP": "M5H 1T1 " 20 | }, 21 | */ 22 | 23 | public class AppointmentList { 24 | 25 | private String PATIENTID; 26 | private String FIRSTNAME; 27 | private String LASTNAME; 28 | private String APPT_DATE; 29 | private String APPT_TIME; 30 | private String DR_NAME; 31 | private String MED_FIELD; 32 | private String OFF_NAME; 33 | private String OFF_ADDR; 34 | private String OFF_CITY; 35 | private String OFF_STATE; 36 | private String OFF_ZIP; 37 | 38 | public AppointmentList(String patient_id, String first_name, String last_name, String date, String time, 39 | String doc_name, String field, String office_name, String office_addr, String office_city, String office_state, 40 | String office_zip) { 41 | PATIENTID = patient_id; 42 | FIRSTNAME = first_name; 43 | LASTNAME = last_name; 44 | APPT_DATE = date; 45 | APPT_TIME = time; 46 | DR_NAME = doc_name; 47 | MED_FIELD = field; 48 | OFF_NAME = office_name; 49 | OFF_ADDR = office_addr; 50 | OFF_CITY = office_city; 51 | OFF_STATE = office_state; 52 | OFF_ZIP = office_zip; 53 | } 54 | 55 | public String getPATIENTID() { 56 | return PATIENTID; 57 | } 58 | 59 | public void setPATIENTID(String pATIENTID) { 60 | PATIENTID = pATIENTID; 61 | } 62 | 63 | public String getFIRSTNAME() { 64 | return FIRSTNAME; 65 | } 66 | 67 | public void setFIRSTNAME(String fIRSTNAME) { 68 | FIRSTNAME = fIRSTNAME; 69 | } 70 | 71 | public String getLASTNAME() { 72 | return LASTNAME; 73 | } 74 | 75 | public void setLASTNAME(String lASTNAME) { 76 | LASTNAME = lASTNAME; 77 | } 78 | 79 | public String getAPPT_DATE() { 80 | return APPT_DATE; 81 | } 82 | 83 | public void setAPPT_DATE(String aPPT_DATE) { 84 | APPT_DATE = aPPT_DATE; 85 | } 86 | 87 | public String getAPPT_TIME() { 88 | return APPT_TIME; 89 | } 90 | 91 | public void setAPPT_TIME(String aPPT_TIME) { 92 | APPT_TIME = aPPT_TIME; 93 | } 94 | 95 | public String getDR_NAME() { 96 | return DR_NAME; 97 | } 98 | 99 | public void setDR_NAME(String dR_NAME) { 100 | DR_NAME = dR_NAME; 101 | } 102 | 103 | public String getMED_FIELD() { 104 | return MED_FIELD; 105 | } 106 | 107 | public void setMED_FIELD(String mED_FIELD) { 108 | MED_FIELD = mED_FIELD; 109 | } 110 | 111 | public String getOFF_NAME() { 112 | return OFF_NAME; 113 | } 114 | 115 | public void setOFF_NAME(String oFF_NAME) { 116 | OFF_NAME = oFF_NAME; 117 | } 118 | 119 | public String getOFF_ADDR() { 120 | return OFF_ADDR; 121 | } 122 | 123 | public void setOFF_ADDR(String oFF_ADDR) { 124 | OFF_ADDR = oFF_ADDR; 125 | } 126 | 127 | public String getOFF_CITY() { 128 | return OFF_CITY; 129 | } 130 | 131 | public void setOFF_CITY(String oFF_CITY) { 132 | OFF_CITY = oFF_CITY; 133 | } 134 | 135 | public String getOFF_STATE() { 136 | return OFF_STATE; 137 | } 138 | 139 | public void setOFF_STATE(String oFF_STATE) { 140 | OFF_STATE = oFF_STATE; 141 | } 142 | 143 | public String getOFF_ZIP() { 144 | return OFF_ZIP; 145 | } 146 | 147 | public void setOFF_ZIP(String oFF_ZIP) { 148 | OFF_ZIP = oFF_ZIP; 149 | } 150 | } -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/CityCounts.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | import javax.json.bind.annotation.JsonbProperty; 3 | 4 | public class CityCounts { 5 | 6 | private String city; 7 | private String postcode; 8 | private Long population; 9 | 10 | public CityCounts(String city, String postcode, Long population) { 11 | this.city = city; 12 | this.postcode = postcode; 13 | this.population = population; 14 | } 15 | 16 | @JsonbProperty("CITY") 17 | public String getCity() { 18 | return city; 19 | } 20 | 21 | public void setCity(String city) { 22 | this.city = city; 23 | } 24 | 25 | @JsonbProperty("POSTCODE") 26 | public String getPostcode() { 27 | return postcode; 28 | } 29 | 30 | public void setPostcode(String postcode) { 31 | this.postcode = postcode; 32 | } 33 | 34 | @JsonbProperty("NUM_IN_CITY") 35 | public Long getPopulation() { 36 | return population; 37 | } 38 | 39 | public void setPopulation(Long population) { 40 | this.population = population; 41 | } 42 | } -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Credentials.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | public class Credentials { 4 | public Credentials() { 5 | } 6 | public String UID; 7 | public String PASS; 8 | 9 | public String getUID() { 10 | return UID; 11 | } 12 | 13 | public void setUID(String uID) { 14 | UID = uID; 15 | } 16 | 17 | public String getPASS() { 18 | return PASS; 19 | } 20 | 21 | public void setPASS(String pASS) { 22 | PASS = pASS; 23 | } 24 | } -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/ExampleResource.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import javax.ejb.Stateless; 4 | import javax.json.Json; 5 | import javax.json.bind.Jsonb; 6 | import javax.json.bind.JsonbBuilder; 7 | import javax.json.JsonArray; 8 | import javax.json.JsonObject; 9 | import javax.json.JsonReader; 10 | import javax.json.stream.JsonCollectors; 11 | import javax.persistence.EntityManager; 12 | import javax.persistence.PersistenceContext; 13 | import javax.ws.rs.GET; 14 | import javax.ws.rs.POST; 15 | import javax.ws.rs.Path; 16 | import javax.ws.rs.Produces; 17 | import javax.ws.rs.Consumes; 18 | import javax.ws.rs.core.MediaType; 19 | import javax.ws.rs.core.Response; 20 | import javax.ws.rs.core.Response.Status; 21 | import javax.ws.rs.PathParam; 22 | import java.io.StringReader; 23 | import java.lang.StringBuffer; 24 | import java.sql.ResultSet; 25 | import java.util.List; 26 | import java.util.logging.*; 27 | 28 | // [x] GET /getInfo/patients/{patID} - gets patient’s details 29 | // [x] GET /getInfo/prescription/{patID} - gets patient’s prescriptions 30 | // POST /healthInfo/addPatient - adds patient to db. Needed? 31 | // [x] GET /listObs/{patID} - gets patient’s observations 32 | // [x] POST /login/user - patient login 33 | // GET /login/userID/{uID}/pwd/{pass} - patient login. Needed? 34 | // POST /appointments/create - adds appt for patient Needed? 35 | // GET /appointments/list/{patID} - gets patient’s appointments 36 | // [x] GET /countCities - gets population of cities in db 37 | // [x] GET /showAllergies - gets city allergy data 38 | // [x] PUT /generate - creates and populates database 39 | // GET /getInfo/patients - gets all patients in db 40 | // GET /listDiseases - gets diseases of patients in db 41 | // GET /getInfo/prescription - gets counts of patient prescriptions 42 | 43 | @Path("/") 44 | @Stateless 45 | public class ExampleResource { 46 | 47 | @PersistenceContext 48 | EntityManager entityManager; 49 | int batchSize = 100; 50 | 51 | static Logger logger = Logger.getLogger("ExampleHealthAPI"); 52 | static { 53 | logger.setLevel(Level.ALL); 54 | logger.addHandler(new ConsoleHandler()); 55 | } 56 | 57 | @GET 58 | @Path("/v1/countCities") 59 | @Produces(MediaType.APPLICATION_JSON) 60 | public Response countCities() { 61 | 62 | List results = entityManager.createNamedQuery("Patient.getPop", CityCounts.class).getResultList(); 63 | /* Output format 64 | "ResultSet Output": [ 65 | { 66 | "CITY": "Akron", 67 | "POSTCODE": "44223", 68 | "NUM_IN_CITY": 13 69 | }], 70 | "StatusCode": 200, 71 | "StatusDescription": "Execution Successful” 72 | } 73 | */ 74 | 75 | Jsonb jsonb = JsonbBuilder.create(); 76 | String cityBlob = "{\"ResultSet Output\": " + jsonb.toJson(results) 77 | + ", \"StatusCode\": 200, \n \"StatusDescription\": \"Execution Successful\"}"; 78 | 79 | JsonReader jsonReader = Json.createReader(new StringReader(cityBlob)); 80 | 81 | JsonObject jresponse = jsonReader.readObject(); 82 | jsonReader.close(); 83 | 84 | return Response.ok(jresponse).build(); 85 | } 86 | 87 | @GET 88 | @Path("/v1/showAllergies") 89 | @Produces(MediaType.APPLICATION_JSON) 90 | public Response showAllergies() { 91 | /* 92 | "ResultSet Output": [ 93 | { 94 | "CITY": "Albany ", 95 | "POSTCODE": "12202 ", 96 | "PATIENT_NUM": 1437, 97 | "BIRTHDATE": "1961-11-26", 98 | "ALLERGY_START": "1991-10-08", 99 | "ALLERGY_STOP": null, 100 | "DESCRIPTION": "Allergy to fish" 101 | } ], 102 | "StatusCode": 200, 103 | "StatusDescription": "Execution Successful" 104 | } 105 | */ 106 | 107 | 108 | // select Patients.patient_id, Patients.birthdate, Patients.city, Patients.postcode, Allergies.description, Allergies.allergy_start, Allergies.allergy_stop from Patients JOIN Allergies ON Patients.patient_id = Allergies.patient_id; 109 | 110 | List results = entityManager.createNamedQuery("Allergy.getAllergies", AllergyList.class).getResultList(); 111 | 112 | Jsonb jsonb = JsonbBuilder.create(); 113 | String allergyBlob = "{\"ResultSet Output\": " + jsonb.toJson(results) 114 | + ", \"StatusCode\": 200, \n \"StatusDescription\": \"Execution Successful\"}"; 115 | 116 | JsonReader jsonReader = Json.createReader(new StringReader(allergyBlob)); 117 | 118 | JsonObject jresponse = jsonReader.readObject(); 119 | jsonReader.close(); 120 | 121 | return Response.ok(jresponse).build(); 122 | } 123 | 124 | @GET 125 | @Path("/v1/getInfo/patients") 126 | @Produces(MediaType.APPLICATION_JSON) 127 | public Response getPatients() { 128 | List results = entityManager.createNamedQuery("Patient.getPatients", Patient.class).getResultList(); 129 | Jsonb jsonb = JsonbBuilder.create(); 130 | String patientBlob = "{\"ResultSet Output\": " + jsonb.toJson(results) 131 | + ", \"StatusCode\": 200, \n \"StatusDescription\": \"Execution Successful\"}"; 132 | 133 | JsonReader jsonReader = Json.createReader(new StringReader(patientBlob)); 134 | 135 | JsonObject jresponse = jsonReader.readObject(); 136 | jsonReader.close(); 137 | return Response.ok(jresponse).build(); 138 | } 139 | 140 | @GET 141 | @Path("/v1/getInfo/patients/{patId}") 142 | @Produces(MediaType.APPLICATION_JSON) 143 | public Response getPatient(@PathParam("patId") String patId) { 144 | List results = entityManager.createNamedQuery("Patient.findPatient", Patient.class) 145 | .setParameter("pid", patId) 146 | .getResultList(); 147 | Jsonb jsonb = JsonbBuilder.create(); 148 | logger.info("Found this many patients with id " + patId + " = " + results.size()); 149 | int returnCode = 0; 150 | if (results.size() == 0) { 151 | returnCode=1; 152 | // return Response.ok("No patients found.").build(); 153 | } 154 | 155 | String patientBlob = "{\"HCCMAREA\": {" + 156 | " \"CA_REQUEST_ID\" : \"01IPAT\"," + 157 | " \"CA_RETURN_CODE\": " + returnCode + "," + 158 | " \"CA_PATIENT_ID\": \"" + patId + "\"," + 159 | " \"CA_PATIENT_REQUEST\": " + (returnCode==0 ? jsonb.toJson(results.get(0)) : "\"\"") + 160 | "}}"; 161 | 162 | logger.info("Patient blob: " + patientBlob); 163 | 164 | JsonReader jsonReader = Json.createReader(new StringReader(patientBlob)); 165 | 166 | JsonObject jresponse = jsonReader.readObject(); 167 | jsonReader.close(); 168 | return Response.ok(jresponse).build(); 169 | } 170 | 171 | @GET 172 | @Path("/v1/appointments/list/{patId}") 173 | @Produces(MediaType.APPLICATION_JSON) 174 | public Response appointments(@PathParam("patId") String patId) { 175 | List results = entityManager.createNamedQuery("Appointment.getAppointments", AppointmentList.class) 176 | .setParameter("pid", patId) 177 | .getResultList(); 178 | Jsonb jsonb = JsonbBuilder.create(); 179 | 180 | 181 | String appointmentBlob = "{\"ResultSet Output\": " + jsonb.toJson(results) 182 | + ", \"StatusCode\": 200, \n \"StatusDescription\": \"Execution Successful\"}"; 183 | 184 | int returnCode = 0; 185 | if (results.size() == 0) { 186 | returnCode=1; 187 | } 188 | 189 | logger.info("Appointment blob: " + appointmentBlob); 190 | 191 | JsonReader jsonReader = Json.createReader(new StringReader(appointmentBlob)); 192 | 193 | JsonObject jresponse = jsonReader.readObject(); 194 | jsonReader.close(); 195 | return Response.ok(jresponse).build(); 196 | } 197 | 198 | @GET 199 | @Path("/v1/getInfo/prescription/{patId}") 200 | @Produces(MediaType.APPLICATION_JSON) 201 | public Response getPrescriptions(@PathParam("patId") String patId) { 202 | List results = entityManager.createNamedQuery("Prescription.getPrescription", Prescription.class) 203 | .setParameter("pid", patId) 204 | .getResultList(); 205 | Jsonb jsonb = JsonbBuilder.create(); 206 | logger.info("Found this many prescriptions with id " + patId + " = " + results.size()); 207 | int returnCode = 0; 208 | if (results.size() == 0) { 209 | returnCode=1; 210 | // return Response.ok("No prescriptions found.").build(); 211 | } 212 | 213 | String prescriptionBlob = "{\"GETMEDO\": {" + 214 | " \"CA_REQUEST_ID\" : \"01IPAT\"," + 215 | " \"CA_RETURN_CODE\": " + returnCode + "," + 216 | " \"CA_PATIENT_ID\": \"" + patId + "\"," + 217 | " \"CA_LIST_MEDICATION_REQUEST\": { \"CA_MEDICATIONS\": " + jsonb.toJson(results) + "}}}"; 218 | 219 | logger.info("Prescription blob: " + prescriptionBlob); 220 | 221 | JsonReader jsonReader = Json.createReader(new StringReader(prescriptionBlob)); 222 | 223 | JsonObject jresponse = jsonReader.readObject(); 224 | jsonReader.close(); 225 | return Response.ok(jresponse).build(); 226 | } 227 | 228 | @GET 229 | @Path("/v1/getInfo/prescription") 230 | @Produces(MediaType.APPLICATION_JSON) 231 | public Response getPrescriptionsCount() { 232 | 233 | List results = entityManager.createNamedQuery("Prescription.countScripts").getResultList(); 234 | 235 | StringBuffer sb = new StringBuffer(); 236 | 237 | sb.append("["); 238 | for (Object[] o : results) { 239 | sb.append("{\"DRUG_NAME\":\"" + o[0] + "\", \"TOTAL_PATIENTS\":\"" + o[1] + "\"},"); 240 | } 241 | sb.deleteCharAt(sb.lastIndexOf(",")); 242 | sb.append("]"); 243 | 244 | return Response.ok(sb.toString()).build(); 245 | } 246 | 247 | @GET 248 | @Path("/v1/listObs/{patId}") 249 | @Produces(MediaType.APPLICATION_JSON) 250 | public Response listObs(@PathParam("patId") String patId) { 251 | List results = entityManager.createNamedQuery("Observation.getObservations", Observation.class) 252 | .setParameter("pid", patId) 253 | .getResultList(); 254 | Jsonb jsonb = JsonbBuilder.create(); 255 | logger.info("Found this many observations with id " + patId + " = " + results.size()); 256 | int returnCode = 0; 257 | if (results.size() == 0) { 258 | returnCode=1; 259 | } 260 | 261 | /* output 262 | Format of JSON output: 263 | 264 | "ResultSet Output": [ 265 | { 266 | "PATIENTID": 1, 267 | "DATEOFOBSERVATION": "2018-05-03", 268 | "CODE": "11111-0 ", 269 | "DESCRIPTION": "Tobacco smoking status NHIS", 270 | "NUMERICVALUE": null, 271 | "CHARACTERVALUE": "Former smoker", 272 | "UNITS": null 273 | }], 274 | "StatusCode": 200, 275 | "StatusDescription": "Execution Successful" 276 | */ 277 | String observationBlob = "{\"ResultSet Output\": " + jsonb.toJson(results) 278 | + ", \"StatusCode\": 200, \n \"StatusDescription\": \"Execution Successful\"}"; 279 | 280 | logger.info("Observation blob: " + observationBlob); 281 | 282 | JsonReader jsonReader = Json.createReader(new StringReader(observationBlob)); 283 | 284 | JsonObject jresponse = jsonReader.readObject(); 285 | jsonReader.close(); 286 | return Response.ok(jresponse).build(); 287 | 288 | } 289 | 290 | @GET 291 | @Path("/v1/listDiseases") 292 | @Produces(MediaType.APPLICATION_JSON) 293 | public Response listDiseases() { 294 | 295 | long asthma = entityManager.createNamedQuery("Prescription.countAsthma", Long.class).getSingleResult(); 296 | long diabetes = entityManager.createNamedQuery("Prescription.countDiabetes", Long.class).getSingleResult(); 297 | long ptnt_cnt = entityManager.createNamedQuery("Patient.countAll", Long.class).getSingleResult(); 298 | 299 | String returnBlob = "{" 300 | + "\"PATIENTS\": " + ptnt_cnt + "," 301 | + "\"DIABETES\": " + diabetes + "," 302 | + "\"ASTHMA\": " + asthma 303 | + "}"; 304 | 305 | return Response.ok(returnBlob).build(); 306 | } 307 | 308 | @POST 309 | @Path("/v1/login/user") 310 | @Produces(MediaType.APPLICATION_JSON) 311 | @Consumes(MediaType.APPLICATION_JSON) 312 | public Response login(String body) { 313 | // {"UID":username,"PASS":password} 314 | Jsonb jsonb = JsonbBuilder.create(); 315 | Credentials c = jsonb.fromJson(body, Credentials.class); 316 | List results = entityManager.createNamedQuery("Patient.login", Patient.class) 317 | .setParameter("userId", c.UID) 318 | .setParameter("password", c.PASS) 319 | .getResultList(); 320 | logger.info("Found this many patients: " + results.size()); 321 | int returnCode = 0; 322 | if (results.size() == 0) { 323 | returnCode=1; 324 | } 325 | 326 | /* 327 | "ResultSet Output": [ 328 | { 329 | "PATIENTID": 1 330 | } 331 | ], 332 | "StatusCode": 200, 333 | "StatusDescription": "Execution Successful" 334 | }*/ 335 | if (returnCode==1) { 336 | return Response.status(Status.NOT_FOUND).build(); 337 | } 338 | 339 | String loginBlob = "{\"ResultSet Output\":" + jsonb.toJson(results) 340 | + ", \"StatusCode\": 200, \n \"StatusDescription\": \"Execution Successful\"}"; 341 | 342 | logger.info("login blob: " + loginBlob); 343 | JsonReader jsonReader = Json.createReader(new StringReader(loginBlob)); 344 | JsonObject jresponse = jsonReader.readObject(); 345 | jsonReader.close(); 346 | return Response.ok(jresponse).build(); 347 | } 348 | 349 | @POST 350 | @Produces(MediaType.TEXT_PLAIN) 351 | @Consumes(MediaType.APPLICATION_JSON) 352 | @Path("/v1/generate") 353 | public String generate(String synthData) { 354 | Jsonb jsonb = JsonbBuilder.create(); 355 | SynthData synData = jsonb.fromJson(synthData, SynthData.class); 356 | int cnt=0; 357 | 358 | logger.info("Loading " + synData.patients.size() + " patient records."); 359 | for (Patient ptnt : synData.patients) { 360 | cnt++; 361 | String patientFirstName = ptnt.getFirstName().replaceAll("[0-9]", ""); 362 | String patientLastName = ptnt.getLastName().replaceAll("[0-9]", ""); 363 | ptnt.setFirstName(patientFirstName); 364 | ptnt.setLastName(patientLastName); 365 | // logger.info("First name: " + ptnt.getFirstName()); 366 | String patientCredential = ptnt.getFirstName().replaceAll("[0-9]", "").toLowerCase() + ptnt.getLastName().replaceAll("[0-9]", "").toLowerCase().charAt(0); 367 | ptnt.setPassword(patientCredential); 368 | ptnt.setUserId(patientCredential); 369 | entityManager.persist(ptnt); 370 | flushBatch(synData.patients.size(), cnt, "Patients"); 371 | } 372 | cnt = 0; 373 | 374 | logger.info("Loading " + synData.providers.size() + " provider records."); 375 | for (Provider p : synData.providers) { 376 | cnt++; 377 | entityManager.persist(p); 378 | flushBatch(synData.providers.size(), cnt, "Providers"); 379 | } 380 | cnt = 0; 381 | 382 | logger.info("Loading " + synData.organizations.size() + " organization records."); 383 | for (Organization o : synData.organizations) { 384 | cnt++; 385 | entityManager.persist(o); 386 | flushBatch(synData.organizations.size(), cnt, "Organizations"); 387 | } 388 | cnt=0; 389 | 390 | logger.info("Loading " + synData.allergies.size() + " allergy records."); 391 | for (Allergy a: synData.allergies) { 392 | cnt++; 393 | entityManager.persist(a); 394 | flushBatch(synData.allergies.size(), cnt, "Allergies"); 395 | } 396 | cnt=0; 397 | 398 | logger.info("Loading " + synData.medications.size() + " medication records."); 399 | for (Prescription p : synData.medications) { 400 | cnt++; 401 | String[] description = p.getDrugName().replaceAll("(NDA[0-9]+)|(\\.)","").split("[0-9]+"); 402 | p.setDrugName( description[0].length() > 0 ? description[0] : description[1].trim().substring(description[1].trim().indexOf(" ")) ); 403 | entityManager.persist(p); 404 | flushBatch(synData.medications.size(), cnt, "Prescriptions"); 405 | } 406 | cnt=0; 407 | 408 | logger.info("Loading " + synData.encounters.size() + " encounter records."); 409 | for (Appointment a : synData.encounters) { 410 | cnt++; 411 | String datetime = a.getDate(); 412 | a.setDate(datetime.substring(0,10)); 413 | a.setTime(datetime.substring(11,19)); 414 | entityManager.persist(a); 415 | flushBatch(synData.encounters.size(), cnt, "Encounters"); 416 | } 417 | cnt=0; 418 | 419 | logger.info("Loading " + synData.observations.size() + " observation records."); 420 | for (Observation o : synData.observations) { 421 | cnt++; 422 | if (o.type.equals("numeric")) { 423 | o.setNumericValue(o.jsonValue); 424 | } else { 425 | o.setCharacterValue(o.jsonValue); 426 | } 427 | entityManager.persist(o); 428 | flushBatch(synData.observations.size(), cnt, "Observations"); 429 | } 430 | 431 | return new String("Loaded : " + (synData.observations.size() + 432 | synData.organizations.size() + 433 | synData.providers.size() + 434 | synData.encounters.size() + 435 | synData.medications.size() + 436 | synData.patients.size() + 437 | synData.allergies.size()) + " records."); 438 | } 439 | 440 | @POST 441 | @Produces(MediaType.TEXT_PLAIN) 442 | @Consumes(MediaType.APPLICATION_JSON) 443 | @Path("/v1/testAddPatient") 444 | public void testAddPatient(Patient ptnt) { 445 | String patientFirstName = ptnt.getFirstName().replaceAll("[0-9]", ""); 446 | String patientLastName = ptnt.getLastName().replaceAll("[0-9]", ""); 447 | ptnt.setFirstName(patientFirstName); 448 | ptnt.setLastName(patientLastName); 449 | logger.info("First name: " + ptnt.getFirstName()); 450 | String patientCredential = ptnt.getFirstName().replaceAll("[0-9]", "").toLowerCase() + ptnt.getLastName().replaceAll("[0-9]", "").toLowerCase().charAt(0); 451 | ptnt.setPassword(patientCredential); 452 | ptnt.setUserId(patientCredential); 453 | entityManager.persist(ptnt); 454 | } 455 | 456 | private void flushBatch(int size, int cnt, String type) { 457 | if ( (cnt % batchSize == 0) || (size == cnt) ) { 458 | logger.info((size - cnt) + " " + type + " remaining."); 459 | entityManager.flush(); 460 | entityManager.clear(); 461 | } 462 | } 463 | 464 | } 465 | -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/JAXRSConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("resources") 7 | public class JAXRSConfiguration extends Application { 8 | 9 | // nothing to configure 10 | 11 | } 12 | -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Message.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class Message { 9 | 10 | @Id 11 | @GeneratedValue 12 | private long id; 13 | 14 | private String message; 15 | 16 | public Message() { 17 | } 18 | 19 | public Message(String message) { 20 | this.message = message; 21 | } 22 | 23 | public long getId() { 24 | return id; 25 | } 26 | 27 | public void setId(long id) { 28 | this.id = id; 29 | } 30 | 31 | public String getMessage() { 32 | return message; 33 | } 34 | 35 | public void setMessage(String message) { 36 | this.message = message; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Observation.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.*; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | 7 | /** 8 | * The persistent class for the Observations database table. 9 | * "ResultSet Output": [ 10 | { 11 | "PATIENTID": 1, 12 | "DATEOFOBSERVATION": "2018-05-03", 13 | "CODE": "11111-0 ", 14 | "DESCRIPTION": "Tobacco smoking status NHIS", 15 | "NUMERICVALUE": null, 16 | "CHARACTERVALUE": "Former smoker", 17 | "UNITS": null 18 | }], 19 | "StatusCode": 200, 20 | "StatusDescription": "Execution Successful" 21 | } 22 | */ 23 | @Entity 24 | @Table(name="Observations") 25 | @NamedQuery(name="Observation.findAll", query="SELECT o FROM Observation o") 26 | @NamedQuery(name = "Observation.getObservations", query = "SELECT o FROM Observation o WHERE " 27 | + "o.patientId = :pid") 28 | 29 | public class Observation implements Serializable { 30 | private static final long serialVersionUID = 1L; 31 | 32 | @Id 33 | private int id; 34 | 35 | @JsonbProperty("CODE") 36 | private String code; 37 | 38 | private String date; 39 | 40 | @JsonbProperty("DESCRIPTION") 41 | private String description; 42 | 43 | @Transient 44 | @JsonbProperty("VALUE") 45 | public String jsonValue; 46 | 47 | @Transient 48 | @JsonbProperty("TYPE") 49 | public String type; 50 | 51 | @Column(name="numeric_value") 52 | private String numericValue; 53 | 54 | @Column(name="character_value") 55 | private String characterValue; 56 | 57 | @JsonbProperty("PATIENT") 58 | @Column(name="patient_id") 59 | private String patientId; 60 | 61 | @JsonbProperty("UNITS") 62 | private String units; 63 | 64 | public Observation() { 65 | } 66 | 67 | public int getId() { 68 | return this.id; 69 | } 70 | 71 | public void setId(int id) { 72 | this.id = id; 73 | } 74 | 75 | @JsonbProperty("CHARACTERVALUE") 76 | public String getCharacterValue() { 77 | return this.characterValue; 78 | } 79 | 80 | public void setCharacterValue(String characterValue) { 81 | this.characterValue = characterValue; 82 | } 83 | 84 | public String getCode() { 85 | return this.code; 86 | } 87 | 88 | public void setCode(String code) { 89 | this.code = code; 90 | } 91 | 92 | @JsonbProperty("DATEOFOBSERVATION") 93 | public String getDate() { 94 | return this.date; 95 | } 96 | 97 | @JsonbProperty("DATE") 98 | public void setDate(String date) { 99 | this.date = date; 100 | } 101 | 102 | public String getDescription() { 103 | return this.description; 104 | } 105 | 106 | public void setDescription(String description) { 107 | this.description = description; 108 | } 109 | 110 | @JsonbProperty("NUMERICVALUE") 111 | public String getNumericValue() { 112 | return this.numericValue; 113 | } 114 | 115 | public void setNumericValue(String numericValue) { 116 | this.numericValue = numericValue; 117 | } 118 | 119 | public String getPatientId() { 120 | return this.patientId; 121 | } 122 | 123 | public void setPatientId(String patientId) { 124 | this.patientId = patientId; 125 | } 126 | 127 | public String getUnits() { 128 | return this.units; 129 | } 130 | 131 | public void setUnits(String units) { 132 | this.units = units; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Organization.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.*; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | 7 | 8 | /** 9 | * The persistent class for the Organizations database table. 10 | * 11 | */ 12 | @Entity 13 | @Table(name="Organizations") 14 | @NamedQuery(name="Organization.findAll", query="SELECT o FROM Organization o") 15 | public class Organization implements Serializable { 16 | private static final long serialVersionUID = 1L; 17 | 18 | @Id 19 | private int id; 20 | 21 | @JsonbProperty("Id") 22 | @Column(name="organization_id") 23 | private String organizationId; 24 | 25 | @JsonbProperty("NAME") 26 | private String name; 27 | 28 | @JsonbProperty("ADDRESS") 29 | private String address; 30 | 31 | @JsonbProperty("CITY") 32 | private String city; 33 | 34 | @JsonbProperty("STATE") 35 | private String state; 36 | 37 | @JsonbProperty("ZIP") 38 | @Column(name="postcode") 39 | private String postcode; 40 | 41 | public Organization() { 42 | } 43 | 44 | public int getId() { 45 | return this.id; 46 | } 47 | 48 | public void setId(int id) { 49 | this.id = id; 50 | } 51 | 52 | public String getOrganizationId() { 53 | return this.organizationId; 54 | } 55 | 56 | public void setOrganizationId(String organizationId) { 57 | this.organizationId = organizationId; 58 | } 59 | 60 | public String getName() { 61 | return this.name; 62 | } 63 | 64 | public void setName(String name) { 65 | this.name = name; 66 | } 67 | 68 | public String getAddress() { 69 | return this.address; 70 | } 71 | 72 | public void setAddress(String address) { 73 | this.address = address; 74 | } 75 | 76 | public String getCity() { 77 | return this.city; 78 | } 79 | 80 | public void setCity(String city) { 81 | this.city = city; 82 | } 83 | 84 | public String getState() { 85 | return this.state; 86 | } 87 | 88 | public void setState(String state) { 89 | this.state = state; 90 | } 91 | 92 | public String getPostcode() { 93 | return this.postcode; 94 | } 95 | 96 | public void setPostcode(String postcode) { 97 | this.postcode = postcode; 98 | } 99 | } -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Patient.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.*; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | import javax.json.bind.annotation.JsonbTransient; 7 | 8 | /** 9 | * The persistent class for the Patients database table. 10 | * 11 | * 12 | * 13 | {"Id":"20c47f5f-a24d-4efe-9eb9-5acf421d1958", 14 | * "BIRTHDATE":"1977-12-12", 15 | * "DEATHDATE":"", 16 | * "SSN":"999-44-8397", 17 | * "DRIVERS":"S99965159", 18 | * "PASSPORT":"X50372231X", 19 | * "PREFIX":"Mr.", 20 | * "FIRST":"Floyd420", 21 | * "LAST":"Jenkins714", 22 | * "SUFFIX":"", 23 | * "MAIDEN":"", 24 | * "MARITAL":"M", 25 | * "RACE":"white", 26 | * "ETHNICITY":"irish", 27 | * "GENDER":"M", 28 | * "BIRTHPLACE":"San Jose California US", 29 | * "ADDRESS":"1045 Dibbert Well", 30 | * "CITY":"Fresno", 31 | * "STATE":"California", 32 | * "ZIP":"93611"}, 33 | * 34 | * "HCCMAREA": { 35 | "CA_REQUEST_ID": "01IPAT", 36 | "CA_RETURN_CODE": 0, 37 | "CA_PATIENT_ID": 1, 38 | "CA_PATIENT_REQUEST": { 39 | "CA_INS_CARD_NUM": "9627811234", 40 | "CA_FIRST_NAME": "Ralph", 41 | "CA_LAST_NAME": "DAlmeida", 42 | "CA_DOB": "1980-07-11", 43 | "CA_ADDRESS": "34 Main Street", 44 | "CA_CITY": "Toronto", 45 | "CA_POSTCODE": "M5H 1T1", 46 | "CA_PHONE_MOBILE": "077-123-9987", 47 | "CA_EMAIL_ADDRESS": "RalphD@ibm.com", 48 | "CA_USERID": "ralphd", 49 | "CA_ADDITIONAL_DATA": "" 50 | } 51 | } 52 | } 53 | */ 54 | @Entity 55 | @Table(name="Patients") 56 | @NamedQuery(name="Patient.countAll", query="SELECT count(p) FROM Patient p") 57 | @NamedQuery(name="Patient.findAll", query="SELECT p FROM Patient p") 58 | @NamedQuery(name="Patient.getPatients", query="SELECT NEW com.ibm.examplehealth.Patient(p.firstName, p.lastName, p.gender, p.birthdate) FROM Patient p") 59 | @NamedQuery(name = "Patient.findPatient", query = "SELECT p FROM Patient p WHERE " 60 | + "p.patientId = :pid") 61 | @NamedQuery(name="Patient.login", query="SELECT p FROM Patient p WHERE p.userId=:userId AND p.password = :password") 62 | @NamedQuery(name="Patient.populations", query="select p.postcode, p.city, count(p) from Patient p group by p.city, p.postcode") 63 | @NamedQuery(name="Patient.getPop", query="SELECT NEW com.ibm.examplehealth.CityCounts(p.city, p.postcode, count(p)) FROM Patient p where not p.postcode='' group by p.city, p.postcode") 64 | 65 | public class Patient implements Serializable { 66 | private static final long serialVersionUID = 1L; 67 | 68 | @Id 69 | @JsonbTransient 70 | private int id; 71 | 72 | private String address; 73 | 74 | private String birthdate; 75 | 76 | private String city; 77 | 78 | @Column(name="first_name") 79 | private String firstName; 80 | 81 | @Column(name="last_name") 82 | private String lastName; 83 | 84 | private String password; 85 | 86 | 87 | @Column(name="patient_id") 88 | private String patientId; 89 | 90 | private String postcode; 91 | 92 | @Column(name="user_id") 93 | private String userId; 94 | 95 | private String gender; 96 | 97 | public Patient() { 98 | } 99 | 100 | public int getId() { 101 | return this.id; 102 | } 103 | 104 | public void setId(int id) { 105 | this.id = id; 106 | } 107 | 108 | @JsonbProperty("CA_ADDRESS") 109 | public String getAddress() { 110 | return this.address; 111 | } 112 | 113 | @JsonbProperty("ADDRESS") 114 | public void setAddress(String address) { 115 | this.address = address; 116 | } 117 | 118 | @JsonbProperty("CA_DOB") 119 | public String getBirthdate() { 120 | return this.birthdate; 121 | } 122 | 123 | @JsonbProperty("BIRTHDATE") 124 | public void setBirthdate(String birthdate) { 125 | this.birthdate = birthdate; 126 | } 127 | 128 | @JsonbProperty("CA_CITY") 129 | public String getCity() { 130 | return this.city; 131 | } 132 | 133 | @JsonbProperty("CITY") 134 | public void setCity(String city) { 135 | this.city = city; 136 | } 137 | 138 | @JsonbProperty("CA_FIRST_NAME") 139 | public String getFirstName() { 140 | return this.firstName; 141 | } 142 | 143 | @JsonbProperty("FIRST") 144 | public void setFirstName(String firstName) { 145 | this.firstName = firstName; 146 | } 147 | 148 | @JsonbProperty("CA_LAST_NAME") 149 | public String getLastName() { 150 | return this.lastName; 151 | } 152 | 153 | @JsonbProperty("LAST") 154 | public void setLastName(String lastName) { 155 | this.lastName = lastName; 156 | } 157 | 158 | public String getPassword() { 159 | return this.password; 160 | } 161 | 162 | public void setPassword(String password) { 163 | this.password = password; 164 | } 165 | 166 | @JsonbProperty("PATIENTID") 167 | public String getPatientId() { 168 | return this.patientId; 169 | } 170 | 171 | @JsonbProperty("Id") 172 | public void setPatientId(String patientId) { 173 | this.patientId = patientId; 174 | } 175 | 176 | @JsonbProperty("CA_POSTCODE") 177 | public String getPostcode() { 178 | return this.postcode; 179 | } 180 | 181 | @JsonbProperty("ZIP") 182 | public void setPostcode(String postcode) { 183 | this.postcode = postcode; 184 | } 185 | 186 | @JsonbProperty("CA_USERID") 187 | public String getUserId() { 188 | return this.userId; 189 | } 190 | 191 | public void setUserId(String userId) { 192 | this.userId = userId; 193 | } 194 | 195 | @JsonbProperty("CA_GENDER") 196 | public String getGender() { 197 | return this.gender; 198 | } 199 | 200 | @JsonbProperty("GENDER") 201 | public void setGender(String gender) { 202 | this.gender = gender; 203 | } 204 | 205 | public Patient(String firstName, String lastName, String gender, String birthdate) { 206 | this.birthdate = birthdate; 207 | this.firstName = firstName; 208 | this.lastName = lastName; 209 | this.gender = gender; 210 | } 211 | 212 | } 213 | -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Prescription.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.*; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | 7 | /** 8 | * The persistent class for the Prescriptions database table. 9 | * 10 | * patientPrescriptions(@PathParam(patID)) 11 | Path: GET /getInfo/prescription/{patID} 12 | Calls: dataService.getPatientPrescriptions(patID) 13 | Returns: { 14 | "GETMEDO": { 15 | "CA_RETURN_CODE": 0, 16 | "CA_PATIENT_ID": 1, 17 | "CA_LIST_MEDICATION_REQUEST": { 18 | "CA_MEDICATIONS": [ 19 | { 20 | "CA_MEDICATION_ID": 1000001, 21 | "CA_DRUG_NAME": "Metoprolol", 22 | "CA_STRENGTH": "100 mg", 23 | "CA_AMOUNT": 1, 24 | "CA_ROUTE": "oral route", 25 | "CA_FREQUENCY": "every 12 hours", 26 | "CA_IDENTIFIER": "redtablet", 27 | "CA_TYPE": "bp" 28 | } 29 | ] 30 | } 31 | } 32 | } 33 | */ 34 | @Entity 35 | @Table(name="Prescriptions") 36 | @NamedQuery(name="Prescription.findAll", query="SELECT p FROM Prescription p") 37 | @NamedQuery(name = "Prescription.getPrescription", query = "SELECT p FROM Prescription p WHERE " 38 | + "p.patientId = :pid") 39 | 40 | @NamedQuery(name = "Prescription.countAsthma", query = "select count(distinct pr.patientId) from Prescription pr where pr.reason like '%asthma%'") 41 | @NamedQuery(name = "Prescription.countDiabetes", query = "select count(distinct pr.patientId) from Prescription pr where pr.reason like '%diabetes%'") 42 | 43 | @NamedQuery(name = "Prescription.countScripts", query = "select Pr.drugName, count(distinct pr.patientId) from Prescription pr, Patient p where p.patientId = pr.patientId group by pr.drugName") 44 | 45 | 46 | public class Prescription implements Serializable { 47 | private static final long serialVersionUID = 1L; 48 | 49 | @Id 50 | private int id; 51 | 52 | 53 | @Column(name="drug_name") 54 | private String drugName; 55 | 56 | 57 | @Column(name="medication_id") 58 | private String medicationId; 59 | 60 | @JsonbProperty("PATIENT") 61 | @Column(name="patient_id") 62 | private String patientId; 63 | 64 | @JsonbProperty("REASONDESCRIPTION") 65 | @Column(name="reason") 66 | private String reason; 67 | 68 | public Prescription() { 69 | } 70 | 71 | public int getId() { 72 | return this.id; 73 | } 74 | 75 | public void setId(int id) { 76 | this.id = id; 77 | } 78 | 79 | @JsonbProperty("CA_DRUG_NAME") 80 | public String getDrugName() { 81 | return this.drugName; 82 | } 83 | 84 | @JsonbProperty("DESCRIPTION") 85 | public void setDrugName(String drugName) { 86 | this.drugName = drugName; 87 | } 88 | 89 | @JsonbProperty("CA_MEDICATION_ID") 90 | public String getMedicationId() { 91 | return this.medicationId; 92 | } 93 | 94 | @JsonbProperty("CODE") 95 | public void setMedicationId(String medicationId) { 96 | this.medicationId = medicationId; 97 | } 98 | 99 | public String getPatientId() { 100 | return this.patientId; 101 | } 102 | 103 | public void setPatientId(String patientId) { 104 | this.patientId = patientId; 105 | } 106 | 107 | public String getReason() { 108 | return this.reason; 109 | } 110 | 111 | public void setReason(String reason) { 112 | this.reason = reason; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/Provider.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.*; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | 7 | 8 | /** 9 | * The persistent class for the Providers database table. 10 | * 11 | */ 12 | @Entity 13 | @Table(name="Providers") 14 | @NamedQuery(name="Provider.findAll", query="SELECT p FROM Provider p") 15 | public class Provider implements Serializable { 16 | private static final long serialVersionUID = 1L; 17 | 18 | @Id 19 | private int id; 20 | 21 | @JsonbProperty("Id") 22 | @Column(name="provider_id") 23 | private String providerId; 24 | 25 | @JsonbProperty("ORGANIZATION") 26 | @Column(name="organization_id") 27 | private String organizationId; 28 | 29 | @JsonbProperty("NAME") 30 | private String name; 31 | 32 | @JsonbProperty("SPECIALITY") 33 | private String speciality; 34 | 35 | public Provider() { 36 | } 37 | 38 | public int getId() { 39 | return this.id; 40 | } 41 | 42 | public void setId(int id) { 43 | this.id = id; 44 | } 45 | 46 | public String getProviderId() { 47 | return this.providerId; 48 | } 49 | 50 | public void setProviderId(String providerId) { 51 | this.providerId = providerId; 52 | } 53 | 54 | public String getOrganizationId() { 55 | return this.organizationId; 56 | } 57 | 58 | public void setOrganizationId(String organizationId) { 59 | this.organizationId = organizationId; 60 | } 61 | 62 | public String getName() { 63 | return this.name; 64 | } 65 | 66 | public void setName(String name) { 67 | this.name = name; 68 | } 69 | 70 | public String getSpeciality() { 71 | return this.speciality; 72 | } 73 | 74 | public void setSpeciality(String speciality) { 75 | this.speciality = speciality; 76 | } 77 | } -------------------------------------------------------------------------------- /example-health-api/src/main/java/com/ibm/examplehealth/SynthData.java: -------------------------------------------------------------------------------- 1 | package com.ibm.examplehealth; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.*; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | import java.util.List; 7 | import java.util.ArrayList; 8 | 9 | public class SynthData { 10 | 11 | @JsonbProperty("allergies") 12 | public List allergies = new ArrayList(); 13 | 14 | public List patients = new ArrayList(); 15 | public List observations = new ArrayList(); 16 | public List medications = new ArrayList(); 17 | public List encounters = new ArrayList(); 18 | public List providers = new ArrayList(); 19 | public List organizations = new ArrayList(); 20 | } 21 | -------------------------------------------------------------------------------- /example-health-api/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example-health-api/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /generate/README.md: -------------------------------------------------------------------------------- 1 | # Generate Synthea Data 2 | 3 | ## About 4 | 5 | This is a script that is used to populate Example Health with realistic synthetic patient data. The bash script works by cloning [Synthea](https://github.com/synthetichealth/synthea), running Synthea to generate data, converting the Synthea data output to a JSON file, and sending the `apidata.json` JSON file to the Example Health API's generate endpoint where it gets processed and stored in the database. The script batches together around 50 patients in `apidata.json` per API call. 6 | 7 | ## Prerequisites 8 | 9 | * NPM: Install [here](https://www.npmjs.com/get-npm) 10 | * Install dependencies: 11 | 12 | ```bash 13 | npm install 14 | ``` 15 | 16 | * Java 1.8 or above: Install [here](https://www.oracle.com/technetwork/java/javase/downloads/index.html) 17 | 18 | ## Running 19 | 20 | ```bash 21 | ./generate.sh -p [population] -u [url] 22 | ``` 23 | 24 | **Flags**: 25 | * p 26 | * The population of the generated Synthea data. If flag is not used, defaults to 150. 27 | * u 28 | * **REQUIRED**: The Example Health API's base url. 29 | -------------------------------------------------------------------------------- /generate/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | population=150 3 | states=( "Alabama" "Alaska" "Arizona" "Arkansas" "California" "Colorado" "Connecticut" "Delaware" "Florida" "Georgia" "Hawaii" "Idaho" "Illinois" "Indiana" "Iowa" "Kansas" "Kentucky" "Louisiana" "Maine" "Maryland" "Massachusetts" "Michigan" "Minnesota" "Mississippi" "Missouri" "Montana" "Nebraska" "Nevada" "New Hampshire" "New Jersey" "New Mexico" "New York" "North Carolina" "North Dakota" "Ohio" "Oklahoma" "Oregon" "Pennsylvania" "Rhode Island" "South Carolina" "South Dakota" "Tennessee" "Texas" "Utah" "Vermont" "Virginia" "Washington" "West Virginia" "Wisconsin" "Wyoming" ) 4 | url="" 5 | max=50 6 | while getopts p:u: option 7 | do 8 | case "${option}" 9 | in 10 | p) population=${OPTARG};; 11 | u) url=${OPTARG};; 12 | esac 13 | done 14 | [ -z "$url" ] && echo "Missing required -u flag for API URL" && exit 1 15 | git clone https://github.com/synthetichealth/synthea.git 16 | cd synthea || exit 1 17 | sed -e 's/^\(exporter.years_of_history =\).*/\1 0/' -e 's/^\(exporter.csv.export =\).*/\1 true/' src/main/resources/synthea.properties > src/main/resources/synthea.properties.new 18 | mv src/main/resources/synthea.properties.new src/main/resources/synthea.properties 19 | seed=1 20 | for ((n=0;n<$population;n=$n+$max)) 21 | do 22 | populationChunk=$(($population - $n)) 23 | state=$(($RANDOM % 49)) 24 | currentState=${states[$state]} 25 | if test $populationChunk -lt $max; then 26 | ./run_synthea -s $seed -p "$populationChunk" "$currentState" 27 | else 28 | populationChunk=$max 29 | ./run_synthea -s $seed -p "$populationChunk" "$currentState" 30 | fi 31 | seed=$(($seed + 1)) 32 | mv output/csv/allergies.csv ../allergies.csv 33 | mv output/csv/patients.csv ../patients.csv 34 | mv output/csv/observations.csv ../observations.csv 35 | mv output/csv/medications.csv ../medications.csv 36 | mv output/csv/encounters.csv ../encounters.csv 37 | mv output/csv/providers.csv ../providers.csv 38 | mv output/csv/organizations.csv ../organizations.csv 39 | cd .. 40 | csvtojson allergies.csv > allergies.json 41 | csvtojson patients.csv > patients.json 42 | csvtojson observations.csv > observations.json 43 | csvtojson medications.csv > medications.json 44 | csvtojson encounters.csv > encounters.json 45 | csvtojson providers.csv > providers.json 46 | csvtojson organizations.csv > organizations.json 47 | sed -e '1s/^/{"allergies":/' allergies.json > apidata.json 48 | { 49 | echo ',"patients":' 50 | cat patients.json 51 | echo ',"observations":' 52 | cat observations.json 53 | echo ',"medications":' 54 | cat medications.json 55 | echo ',"encounters":' 56 | cat encounters.json 57 | echo ',"providers":' 58 | cat providers.json 59 | echo ',"organizations":' 60 | cat organizations.json 61 | echo "}" 62 | } >> apidata.json 63 | rm -rf allergies.csv 64 | rm -rf allergies.json 65 | rm -rf patients.csv 66 | rm -rf patients.json 67 | rm -rf observations.csv 68 | rm -rf observations.json 69 | rm -rf medications.csv 70 | rm -rf medications.json 71 | rm -rf encounters.csv 72 | rm -rf encounters.json 73 | rm -rf providers.csv 74 | rm -rf providers.json 75 | rm -rf organizations.csv 76 | rm -rf organizations.json 77 | statusCode=$(curl --write-out %{http_code} --silent --output /dev/null "$url/resources/v1/generate" -H "Content-Type: application/json" -X POST -d "@apidata.json") 78 | rm -rf apidata.json 79 | if (($statusCode >= 400)); then 80 | rm -rf synthea 81 | echo "Unable to finish generating $population patients. Error from $url/resources/v1/generate" 82 | echo "HTTP status code: $statusCode" 83 | exit 1 84 | fi 85 | cd synthea 86 | rm -rf output 87 | done 88 | cd .. 89 | rm -rf synthea 90 | -------------------------------------------------------------------------------- /generate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "synthea-generate", 3 | "version": "1.0.0", 4 | "description": "Generate mock data with Synthea", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/IBM/summit-jee-openshift.git" 8 | }, 9 | "author": "", 10 | "license": "Apache-2.0", 11 | "homepage": "https://github.com/IBM/summit-jee-openshift#readme", 12 | "devDependencies": { 13 | "csvtojson": "" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /readme_images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/readme_images/architecture.png -------------------------------------------------------------------------------- /readme_images/new_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/readme_images/new_architecture.png -------------------------------------------------------------------------------- /readme_images/original_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/readme_images/original_architecture.png -------------------------------------------------------------------------------- /screenshots/Screen Shot 2019-07-11 at 1.47.00 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/Screen Shot 2019-07-11 at 1.47.00 PM.png -------------------------------------------------------------------------------- /screenshots/Screen Shot 2019-07-11 at 2.00.29 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/Screen Shot 2019-07-11 at 2.00.29 PM.png -------------------------------------------------------------------------------- /screenshots/mysql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/mysql.png -------------------------------------------------------------------------------- /screenshots/oom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/oom.png -------------------------------------------------------------------------------- /screenshots/s1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/s1.png -------------------------------------------------------------------------------- /screenshots/s2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/s2.png -------------------------------------------------------------------------------- /screenshots/s3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/s3.png -------------------------------------------------------------------------------- /screenshots/s4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/s4.png -------------------------------------------------------------------------------- /screenshots/s5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/example-health-jee-openshift/0559192d5f3b239a3f47abb832a8072959c634cd/screenshots/s5.png -------------------------------------------------------------------------------- /terraform/main.tf: -------------------------------------------------------------------------------- 1 | variable "ibmcloud_api_key" {} 2 | 3 | provider "ibm" { 4 | ibmcloud_api_key = "${var.ibmcloud_api_key}" 5 | region = "${var.region}" 6 | } 7 | 8 | data "ibm_org" "org" { 9 | org = "${var.org}" 10 | } 11 | 12 | data "ibm_space" "space" { 13 | org = "${var.org}" 14 | space = "${var.space}" 15 | } 16 | 17 | resource "ibm_container_cluster" "cluster" { 18 | name = "${var.cluster_name}" 19 | datacenter = "${var.datacenter}" 20 | hardware = "${var.hardware}" 21 | default_pool_size = "${var.poolsize}" 22 | machine_type = "${var.machine_type}" 23 | public_vlan_id = "${var.public_vlan_id}" 24 | private_vlan_id = "${var.private_vlan_id}" 25 | kube_version = "${var.kube_version}" 26 | } 27 | 28 | resource "ibm_service_instance" "mysql_db" { 29 | name = "${var.service_name}" 30 | space_guid = "${data.ibm_space.space.id}" 31 | service = "compose-for-mysql" 32 | plan = "Standard" 33 | } 34 | 35 | resource "ibm_service_key" "mysql_db_key" { 36 | name = "db_key" 37 | service_instance_guid = "${ibm_service_instance.mysql_db.id}" 38 | } 39 | 40 | resource "ibm_container_bind_service" "bind_service" { 41 | cluster_name_id = "${ibm_container_cluster.cluster.id}" 42 | service_instance_id = "${ibm_service_instance.mysql_db.id}" 43 | namespace_id = "default" 44 | } 45 | -------------------------------------------------------------------------------- /terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "org" { 2 | description = "Your IBM Cloud organization which is gnerally your email address. Run `ibmcloud cf orgs` to see all your orgs." 3 | } 4 | 5 | variable "space" { 6 | description = "Your IBM Cloud space to provision the Compose for MySQL service. Run `ibmcloud cf spaces` to see all your spaces." 7 | } 8 | 9 | variable "region" { 10 | default = "us-south" 11 | description = "Region to deploy your Compose MySQL instance." 12 | } 13 | 14 | variable "datacenter" { 15 | default = "wdc04" 16 | description = "The datacenter to provision your Red Hat OpenShift cluster in IBM Cloud." 17 | } 18 | 19 | variable "machine_type" { 20 | default = "b2c.4x16" 21 | description = "The flavor of worker node in your cluster. Run `ibmcloud ks flavors --zone ` to see the different flavors." 22 | } 23 | 24 | variable "private_vlan_id" { 25 | description = "Your private VLAN ID. If you don't have one, set this value to 'null' and one will be created for you. Run `ic ks vlans --zone ` to see your VLANs." 26 | } 27 | 28 | variable "public_vlan_id" { 29 | description = "Your private VLAN ID. If you don't have one, set this value to 'null' and one will be created for you. Run `ic ks vlans --zone ` to see your VLANs." 30 | } 31 | 32 | variable "cluster_name" { 33 | default = "cluster1" 34 | description = "Name of your OpenShift cluster." 35 | } 36 | 37 | variable "poolsize" { 38 | default = "2" 39 | description = "Number of nodes in your cluster." 40 | } 41 | 42 | variable "service_name" { 43 | default = "composeformysql" 44 | description = "Service ID for the Compose for MySQL instance." 45 | } 46 | 47 | variable "kube_version" { 48 | default = "3.11_openshift" 49 | description = "Version of OpenShift." 50 | } 51 | variable "hardware" { 52 | default = "shared" 53 | description = "Type of hardware for OpenShift cluster." 54 | } 55 | --------------------------------------------------------------------------------