From b0c64cebc1433045a94684df8576e58fb8e57881 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 27 Oct 2023 11:47:14 +0200 Subject: [PATCH 01/46] update ip configs --- settings-sample.env | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/settings-sample.env b/settings-sample.env index d46fc85..f5a174f 100644 --- a/settings-sample.env +++ b/settings-sample.env @@ -2,14 +2,14 @@ # The base url of the OpenLMIS distribution. Will be used for communication between services. # In case of generated links pointing to the distribution, the PUBLIC_URL should be used instead. -BASE_URL=http://192.168.1.102 +BASE_URL=http://192.168.8.125 # The virtual host for the nginx server - nginx will make services available under this host. -VIRTUAL_HOST=192.168.1.102 +VIRTUAL_HOST=192.168.8.125 # The public url of the OpenLMIS distribution. Should be used in generated links pointing to the distribution. # If this variable is not set, the BASE_URL will be used for the generated links. -PUBLIC_URL=http://192.168.1.102 +PUBLIC_URL=http://192.168.8.125 ############################################################################################################ # Profile: use one of the desired deployment profiles below by uncommenting one (and only one) line below From 42211bf402f47459ea74273850c3a91f98d96ab3 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 27 Oct 2023 11:57:30 +0200 Subject: [PATCH 02/46] new compose file for an example extension --- ref-distro-example-extension-docker-compose.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ref-distro-example-extension-docker-compose.yml diff --git a/ref-distro-example-extension-docker-compose.yml b/ref-distro-example-extension-docker-compose.yml new file mode 100644 index 0000000..e69de29 From 097d4560bb075e8114b96ddc02ac9e672d1b87bc Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 27 Oct 2023 12:00:24 +0200 Subject: [PATCH 03/46] rename --- ...ml => docker-compose.openlmis-ref-distro-example-extension.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ref-distro-example-extension-docker-compose.yml => docker-compose.openlmis-ref-distro-example-extension.yml (100%) diff --git a/ref-distro-example-extension-docker-compose.yml b/docker-compose.openlmis-ref-distro-example-extension.yml similarity index 100% rename from ref-distro-example-extension-docker-compose.yml rename to docker-compose.openlmis-ref-distro-example-extension.yml From ed0d01dc1de054b9aaff7ca3ccb551dae27cb090 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 27 Oct 2023 12:20:15 +0200 Subject: [PATCH 04/46] include template-service during spin --- ...se.openlmis-stockmanagement-validator-extension.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docker-compose.openlmis-stockmanagement-validator-extension.yml b/docker-compose.openlmis-stockmanagement-validator-extension.yml index 246ed8d..5849e05 100644 --- a/docker-compose.openlmis-stockmanagement-validator-extension.yml +++ b/docker-compose.openlmis-stockmanagement-validator-extension.yml @@ -87,6 +87,16 @@ services: depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] + example: + image: openlmis/template-service:latest + env_file: .env + environment: + JAVA_OPTS: '-Dlogging.config=/logback.xml' + volumes: + - 'example-extensions:/extensions' + - './logback.xml:/logback.xml' + depends_on: [log, example-extensions] + example-extensions: image: openlmis/openlmis-example-extensions:0.0.1-SNAPSHOT volumes: From 9496389966c4cf2b4344b8006c20f5dad3001ffa Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 27 Oct 2023 12:36:24 +0200 Subject: [PATCH 05/46] remove template-service during spin --- ...mis-stockmanagement-validator-extension.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docker-compose.openlmis-stockmanagement-validator-extension.yml b/docker-compose.openlmis-stockmanagement-validator-extension.yml index 5849e05..8edbae8 100644 --- a/docker-compose.openlmis-stockmanagement-validator-extension.yml +++ b/docker-compose.openlmis-stockmanagement-validator-extension.yml @@ -87,15 +87,15 @@ services: depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] - example: - image: openlmis/template-service:latest - env_file: .env - environment: - JAVA_OPTS: '-Dlogging.config=/logback.xml' - volumes: - - 'example-extensions:/extensions' - - './logback.xml:/logback.xml' - depends_on: [log, example-extensions] + # example: + # image: openlmis/template-service:latest + # env_file: .env + # environment: + # JAVA_OPTS: '-Dlogging.config=/logback.xml' + # volumes: + # - 'example-extensions:/extensions' + # - './logback.xml:/logback.xml' + # depends_on: [log, example-extensions] example-extensions: image: openlmis/openlmis-example-extensions:0.0.1-SNAPSHOT From 9facda29246746bc06c0987e5690fe5d6ed4c732 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 27 Oct 2023 12:46:31 +0200 Subject: [PATCH 06/46] template-service --- ...is-stockmanagement-validator-extension.yml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docker-compose.openlmis-stockmanagement-validator-extension.yml b/docker-compose.openlmis-stockmanagement-validator-extension.yml index 8edbae8..c874070 100644 --- a/docker-compose.openlmis-stockmanagement-validator-extension.yml +++ b/docker-compose.openlmis-stockmanagement-validator-extension.yml @@ -87,20 +87,20 @@ services: depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] - # example: - # image: openlmis/template-service:latest - # env_file: .env - # environment: - # JAVA_OPTS: '-Dlogging.config=/logback.xml' - # volumes: - # - 'example-extensions:/extensions' - # - './logback.xml:/logback.xml' - # depends_on: [log, example-extensions] - - example-extensions: - image: openlmis/openlmis-example-extensions:0.0.1-SNAPSHOT + example: + image: openlmis/template-service:latest + env_file: .env + environment: + JAVA_OPTS: '-Dlogging.config=/logback.xml' volumes: - 'example-extensions:/extensions' + - './logback.xml:/logback.xml' + depends_on: [log, example-extensions] + + # example-extensions: + # image: openlmis/openlmis-example-extensions:0.0.1-SNAPSHOT + # volumes: + # - 'example-extensions:/extensions' stockmanagement: image: openlmis/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} From ece3ef5232598e016c176eb4c314c5ad7db2fff7 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 27 Oct 2023 12:48:31 +0200 Subject: [PATCH 07/46] include example-extensions --- ...mpose.openlmis-stockmanagement-validator-extension.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.openlmis-stockmanagement-validator-extension.yml b/docker-compose.openlmis-stockmanagement-validator-extension.yml index c874070..5849e05 100644 --- a/docker-compose.openlmis-stockmanagement-validator-extension.yml +++ b/docker-compose.openlmis-stockmanagement-validator-extension.yml @@ -97,10 +97,10 @@ services: - './logback.xml:/logback.xml' depends_on: [log, example-extensions] - # example-extensions: - # image: openlmis/openlmis-example-extensions:0.0.1-SNAPSHOT - # volumes: - # - 'example-extensions:/extensions' + example-extensions: + image: openlmis/openlmis-example-extensions:0.0.1-SNAPSHOT + volumes: + - 'example-extensions:/extensions' stockmanagement: image: openlmis/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} From c1b6878f8fb85326b4515f7ab303defb5c43df84 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Mon, 30 Oct 2023 12:26:55 +0200 Subject: [PATCH 08/46] new compose file for new service --- ....openlmis-ref-distro-example-extension.yml | 0 ...se.openlmis-ref-distro-example-service.yml | 193 ++++++++++++++++++ 2 files changed, 193 insertions(+) delete mode 100644 docker-compose.openlmis-ref-distro-example-extension.yml create mode 100644 docker-compose.openlmis-ref-distro-example-service.yml diff --git a/docker-compose.openlmis-ref-distro-example-extension.yml b/docker-compose.openlmis-ref-distro-example-extension.yml deleted file mode 100644 index e69de29..0000000 diff --git a/docker-compose.openlmis-ref-distro-example-service.yml b/docker-compose.openlmis-ref-distro-example-service.yml new file mode 100644 index 0000000..5849e05 --- /dev/null +++ b/docker-compose.openlmis-ref-distro-example-service.yml @@ -0,0 +1,193 @@ +version: "3.3" +services: + + consul: + command: -server -bootstrap + image: gliderlabs/consul-server + ports: + - "8300" + - "8400" + - "8500:8500" + - "53" + + nginx: + image: openlmis/nginx:${OL_NGINX_VERSION} + ports: + - "${OL_HTTP_PORT:-80}:80" + env_file: settings.env + environment: + NGINX_LOG_DIR: '/var/log/nginx/log' + volumes: + - 'nginx-log:/var/log/nginx/log' + - 'consul-template-log:/var/log/consul-template' + depends_on: [consul] + + reference-ui: + image: openlmis/reference-ui:${OL_REFERENCE_UI_VERSION} + env_file: settings.env + depends_on: [consul] + + requisition: + image: openlmis/requisition:${OL_REQUISITION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + referencedata: + image: openlmis/referencedata:${OL_REFERENCEDATA_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + auth: + image: openlmis/auth:${OL_AUTH_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + notification: + image: openlmis/notification:${OL_NOTIFICATION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + fulfillment: + image: openlmis/fulfillment:${OL_FULFILLMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + cce: + image: openlmis/cce:${OL_CCE_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + example: + image: openlmis/template-service:latest + env_file: .env + environment: + JAVA_OPTS: '-Dlogging.config=/logback.xml' + volumes: + - 'example-extensions:/extensions' + - './logback.xml:/logback.xml' + depends_on: [log, example-extensions] + + example-extensions: + image: openlmis/openlmis-example-extensions:0.0.1-SNAPSHOT + volumes: + - 'example-extensions:/extensions' + + stockmanagement: + image: openlmis/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'example-extensions:/extensions' + - 'service-config:/config' + depends_on: [log, db, example-extensions] + command: ["/wait-for-postgres.sh", "/run.sh"] + + report: + image: openlmis/report:${OL_REPORT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + hapifhir: + restart: always + image: openlmis/hapifhir:${OL_HAPIFHIR_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + diagnostics: + image: openlmis/diagnostics:${OL_DIAGNOSTICS_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log] + + db: + image: openlmis/postgres:${OL_POSTGRES_VERSION} + env_file: settings.env + networks: + default: + aliases: + - olmis-db + depends_on: [consul] + + log: + image: openlmis/rsyslog:${OL_RSYSLOG_VERSION} + volumes: + - 'syslog:/var/log' + depends_on: + - service-configuration + - consul + + service-configuration: + build: + context: ./config + volumes: + - service-config:/config + + ftp: + image: hauptmedia/proftpd + ports: + - "${OL_FTP_PORT_21:-21}:21" + - "${OL_FTP_PORT_20:-20}:20" + env_file: settings.env + depends_on: [consul] + + redis: + image: redis:3.2.12 + depends_on: [consul] + +volumes: + syslog: + external: false + nginx-log: + external: false + consul-template-log: + external: false + service-config: + external: false + example-extensions: + external: false From 091f89b32557770367a6683a3a8b6591d85b0f6a Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Mon, 30 Oct 2023 12:34:10 +0200 Subject: [PATCH 09/46] update env_file for example service --- docker-compose.openlmis-ref-distro-example-service.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.openlmis-ref-distro-example-service.yml b/docker-compose.openlmis-ref-distro-example-service.yml index 5849e05..c24dfd7 100644 --- a/docker-compose.openlmis-ref-distro-example-service.yml +++ b/docker-compose.openlmis-ref-distro-example-service.yml @@ -89,7 +89,7 @@ services: example: image: openlmis/template-service:latest - env_file: .env + env_file: settings.env environment: JAVA_OPTS: '-Dlogging.config=/logback.xml' volumes: From 24ea9286a10239335ac401818bd5b9034d75f041 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Mon, 30 Oct 2023 12:52:54 +0200 Subject: [PATCH 10/46] use latest example-extension service --- docker-compose.openlmis-ref-distro-example-service.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.openlmis-ref-distro-example-service.yml b/docker-compose.openlmis-ref-distro-example-service.yml index c24dfd7..884b64d 100644 --- a/docker-compose.openlmis-ref-distro-example-service.yml +++ b/docker-compose.openlmis-ref-distro-example-service.yml @@ -98,7 +98,7 @@ services: depends_on: [log, example-extensions] example-extensions: - image: openlmis/openlmis-example-extensions:0.0.1-SNAPSHOT + image: openlmis/openlmis-example-extensions:latest volumes: - 'example-extensions:/extensions' From a3927915d897750ab6f74d7e3ea80f51b4d95352 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Mon, 30 Oct 2023 14:51:47 +0200 Subject: [PATCH 11/46] recreate by replicating std compose file --- ...se.openlmis-ref-distro-example-service.yml | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/docker-compose.openlmis-ref-distro-example-service.yml b/docker-compose.openlmis-ref-distro-example-service.yml index 884b64d..a8cfda5 100644 --- a/docker-compose.openlmis-ref-distro-example-service.yml +++ b/docker-compose.openlmis-ref-distro-example-service.yml @@ -87,30 +87,14 @@ services: depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] - example: - image: openlmis/template-service:latest - env_file: settings.env - environment: - JAVA_OPTS: '-Dlogging.config=/logback.xml' - volumes: - - 'example-extensions:/extensions' - - './logback.xml:/logback.xml' - depends_on: [log, example-extensions] - - example-extensions: - image: openlmis/openlmis-example-extensions:latest - volumes: - - 'example-extensions:/extensions' - stockmanagement: image: openlmis/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} env_file: settings.env environment: JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' volumes: - - 'example-extensions:/extensions' - 'service-config:/config' - depends_on: [log, db, example-extensions] + depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] report: @@ -123,6 +107,16 @@ services: depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] + buq: + image: openlmis/buq:${OL_BUQ_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [ log, db ] + command: [ "/wait-for-postgres.sh", "/run.sh" ] + hapifhir: restart: always image: openlmis/hapifhir:${OL_HAPIFHIR_VERSION} @@ -134,7 +128,7 @@ services: - 'service-config:/config' depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] - + diagnostics: image: openlmis/diagnostics:${OL_DIAGNOSTICS_VERSION} env_file: settings.env @@ -180,6 +174,21 @@ services: image: redis:3.2.12 depends_on: [consul] + # example: + # image: openlmis/template-service:latest + # env_file: settings.env + # environment: + # JAVA_OPTS: '-Dlogging.config=/logback.xml' + # volumes: + # - 'example-extensions:/extensions' + # - './logback.xml:/logback.xml' + # depends_on: [log, example-extensions] + + # example-extensions: + # image: openlmis/openlmis-example-extensions:latest + # volumes: + # - 'example-extensions:/extensions' + volumes: syslog: external: false From eff4e19ca8874708b207b3284ab04e563388ef68 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Tue, 31 Oct 2023 14:58:06 +0200 Subject: [PATCH 12/46] create new compose file to test pod init service --- ...-compose.openlmis-pointofdelivery-init.yml | 202 ++++++++++++++++++ ...se.openlmis-ref-distro-example-service.yml | 2 +- 2 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 docker-compose.openlmis-pointofdelivery-init.yml diff --git a/docker-compose.openlmis-pointofdelivery-init.yml b/docker-compose.openlmis-pointofdelivery-init.yml new file mode 100644 index 0000000..23c49db --- /dev/null +++ b/docker-compose.openlmis-pointofdelivery-init.yml @@ -0,0 +1,202 @@ +version: "3.3" +services: + + consul: + command: -server -bootstrap + image: gliderlabs/consul-server + ports: + - "8300" + - "8400" + - "8500:8500" + - "53" + + nginx: + image: openlmis/nginx:${OL_NGINX_VERSION} + ports: + - "${OL_HTTP_PORT:-80}:80" + env_file: settings.env + environment: + NGINX_LOG_DIR: '/var/log/nginx/log' + volumes: + - 'nginx-log:/var/log/nginx/log' + - 'consul-template-log:/var/log/consul-template' + depends_on: [consul] + + reference-ui: + image: openlmis/reference-ui:${OL_REFERENCE_UI_VERSION} + env_file: settings.env + depends_on: [consul] + + requisition: + image: openlmis/requisition:${OL_REQUISITION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + referencedata: + image: openlmis/referencedata:${OL_REFERENCEDATA_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + auth: + image: openlmis/auth:${OL_AUTH_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + notification: + image: openlmis/notification:${OL_NOTIFICATION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + fulfillment: + image: openlmis/fulfillment:${OL_FULFILLMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + cce: + image: openlmis/cce:${OL_CCE_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + stockmanagement: + image: openlmis/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + report: + image: openlmis/report:${OL_REPORT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + buq: + image: openlmis/buq:${OL_BUQ_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [ log, db ] + command: [ "/wait-for-postgres.sh", "/run.sh" ] + + hapifhir: + restart: always + image: openlmis/hapifhir:${OL_HAPIFHIR_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + diagnostics: + image: openlmis/diagnostics:${OL_DIAGNOSTICS_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log] + + db: + image: openlmis/postgres:${OL_POSTGRES_VERSION} + env_file: settings.env + networks: + default: + aliases: + - olmis-db + depends_on: [consul] + + log: + image: openlmis/rsyslog:${OL_RSYSLOG_VERSION} + volumes: + - 'syslog:/var/log' + depends_on: + - service-configuration + - consul + + service-configuration: + build: + context: ./config + volumes: + - service-config:/config + + ftp: + image: hauptmedia/proftpd + ports: + - "${OL_FTP_PORT_21:-21}:21" + - "${OL_FTP_PORT_20:-20}:20" + env_file: settings.env + depends_on: [consul] + + redis: + image: redis:3.2.12 + depends_on: [consul] + + pointofdelivery: + image: openlmis/openlmis/pointofdelivery:latest + env_file: settings.env + environment: + JAVA_OPTS: '-Dlogging.config=/logback.xml' + volumes: + - 'pointofdelivery-extensions:/extensions' + - './logback.xml:/logback.xml' + depends_on: [log] + + # example-extensions: + # image: openlmis/openlmis-example-extensions:latest + # volumes: + # - 'example-extensions:/extensions' + +volumes: + syslog: + external: false + nginx-log: + external: false + consul-template-log: + external: false + service-config: + external: false + pointofdelivery-extensions: + external: false diff --git a/docker-compose.openlmis-ref-distro-example-service.yml b/docker-compose.openlmis-ref-distro-example-service.yml index a8cfda5..8df7e5c 100644 --- a/docker-compose.openlmis-ref-distro-example-service.yml +++ b/docker-compose.openlmis-ref-distro-example-service.yml @@ -182,7 +182,7 @@ services: # volumes: # - 'example-extensions:/extensions' # - './logback.xml:/logback.xml' - # depends_on: [log, example-extensions] + # depends_on: [log] # example-extensions: # image: openlmis/openlmis-example-extensions:latest From 2930a95587f61cb5a88c9c55b73249c8a1314cb1 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Tue, 31 Oct 2023 15:02:30 +0200 Subject: [PATCH 13/46] specify correct pod image name --- docker-compose.openlmis-pointofdelivery-init.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.openlmis-pointofdelivery-init.yml b/docker-compose.openlmis-pointofdelivery-init.yml index 23c49db..f519de5 100644 --- a/docker-compose.openlmis-pointofdelivery-init.yml +++ b/docker-compose.openlmis-pointofdelivery-init.yml @@ -175,7 +175,7 @@ services: depends_on: [consul] pointofdelivery: - image: openlmis/openlmis/pointofdelivery:latest + image: openlmis/pointofdelivery:latest env_file: settings.env environment: JAVA_OPTS: '-Dlogging.config=/logback.xml' From 981543f3817b86b1b79e70966fd66e0825a57f28 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 3 Nov 2023 04:32:50 +0200 Subject: [PATCH 14/46] update pod service java and volume configs --- docker-compose.openlmis-pointofdelivery-init.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.openlmis-pointofdelivery-init.yml b/docker-compose.openlmis-pointofdelivery-init.yml index f519de5..76292aa 100644 --- a/docker-compose.openlmis-pointofdelivery-init.yml +++ b/docker-compose.openlmis-pointofdelivery-init.yml @@ -178,10 +178,10 @@ services: image: openlmis/pointofdelivery:latest env_file: settings.env environment: - JAVA_OPTS: '-Dlogging.config=/logback.xml' + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' volumes: - 'pointofdelivery-extensions:/extensions' - - './logback.xml:/logback.xml' + - 'service-config:/config' depends_on: [log] # example-extensions: From fa2308fac9bc3b22fd7612e7bc63873031dfe650 Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 3 Nov 2023 07:54:51 +0200 Subject: [PATCH 15/46] use the raw template service --- docker-compose.openlmis-pointofdelivery-init.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.openlmis-pointofdelivery-init.yml b/docker-compose.openlmis-pointofdelivery-init.yml index 76292aa..e0c597a 100644 --- a/docker-compose.openlmis-pointofdelivery-init.yml +++ b/docker-compose.openlmis-pointofdelivery-init.yml @@ -175,7 +175,7 @@ services: depends_on: [consul] pointofdelivery: - image: openlmis/pointofdelivery:latest + image: openlmis/template-service:latest env_file: settings.env environment: JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' From fbdd375ad32530912c21ebfdedee2db89a97a61c Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Fri, 3 Nov 2023 08:41:48 +0200 Subject: [PATCH 16/46] rename pod to template-service --- docker-compose.openlmis-pointofdelivery-init.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.openlmis-pointofdelivery-init.yml b/docker-compose.openlmis-pointofdelivery-init.yml index e0c597a..4064ec3 100644 --- a/docker-compose.openlmis-pointofdelivery-init.yml +++ b/docker-compose.openlmis-pointofdelivery-init.yml @@ -174,7 +174,7 @@ services: image: redis:3.2.12 depends_on: [consul] - pointofdelivery: + template-service: image: openlmis/template-service:latest env_file: settings.env environment: From 0767647e92c8ef1078b5148fcc7dd99426d56ddd Mon Sep 17 00:00:00 2001 From: lmphatsi Date: Wed, 8 Nov 2023 12:02:04 +0200 Subject: [PATCH 17/46] new dev compose file --- .env | 5 +- docker-compose.openlmis-dev.yml | 202 ++++++++++++++++++ ...-compose.openlmis-pointofdelivery-init.yml | 10 +- start-local.sh | 5 +- 4 files changed, 213 insertions(+), 9 deletions(-) create mode 100644 docker-compose.openlmis-dev.yml diff --git a/.env b/.env index 6fd79cd..455fbea 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -OL_REFERENCE_UI_VERSION=5.2.7-SNAPSHOT +OL_REFERENCE_UI_VERSION=1.0.7 OL_REQUISITION_VERSION=8.3.7-SNAPSHOT OL_REFERENCEDATA_VERSION=15.2.6-SNAPSHOT @@ -6,7 +6,8 @@ OL_AUTH_VERSION=4.3.4-SNAPSHOT OL_NOTIFICATION_VERSION=4.3.4-SNAPSHOT OL_FULFILLMENT_VERSION=9.0.5-SNAPSHOT OL_CCE_VERSION=1.3.3-SNAPSHOT -OL_STOCKMANAGEMENT_VERSION=5.1.10-SNAPSHOT +OL_STOCKMANAGEMENT_VERSION=1.0.2 +OL_POINTOFDELIVERY_VERSION=1.0.0 OL_REPORT_VERSION=1.2.3-SNAPSHOT OL_BUQ_VERSION=1.0.0-SNAPSHOT OL_HAPIFHIR_VERSION=2.0.3-SNAPSHOT diff --git a/docker-compose.openlmis-dev.yml b/docker-compose.openlmis-dev.yml new file mode 100644 index 0000000..7e65254 --- /dev/null +++ b/docker-compose.openlmis-dev.yml @@ -0,0 +1,202 @@ +version: "3.3" +services: + + consul: + command: -server -bootstrap + image: gliderlabs/consul-server + ports: + - "8300" + - "8400" + - "8500:8500" + - "53" + + nginx: + image: openlmis/nginx:${OL_NGINX_VERSION} + ports: + - "${OL_HTTP_PORT:-80}:80" + env_file: settings.env + environment: + NGINX_LOG_DIR: '/var/log/nginx/log' + volumes: + - 'nginx-log:/var/log/nginx/log' + - 'consul-template-log:/var/log/consul-template' + depends_on: [consul] + + reference-ui: + image: elmislesotho/reference-ui:${OL_REFERENCE_UI_VERSION} + env_file: settings.env + depends_on: [consul] + + requisition: + image: openlmis/requisition:${OL_REQUISITION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + referencedata: + image: openlmis/referencedata:${OL_REFERENCEDATA_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + auth: + image: openlmis/auth:${OL_AUTH_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + notification: + image: openlmis/notification:${OL_NOTIFICATION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + fulfillment: + image: openlmis/fulfillment:${OL_FULFILLMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + cce: + image: openlmis/cce:${OL_CCE_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + stockmanagement: + image: elmislesotho/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + report: + image: openlmis/report:${OL_REPORT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + buq: + image: openlmis/buq:${OL_BUQ_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [ log, db ] + command: [ "/wait-for-postgres.sh", "/run.sh" ] + + hapifhir: + restart: always + image: openlmis/hapifhir:${OL_HAPIFHIR_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + diagnostics: + image: openlmis/diagnostics:${OL_DIAGNOSTICS_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log] + + db: + image: openlmis/postgres:${OL_POSTGRES_VERSION} + env_file: settings.env + networks: + default: + aliases: + - olmis-db + depends_on: [consul] + + log: + image: openlmis/rsyslog:${OL_RSYSLOG_VERSION} + volumes: + - 'syslog:/var/log' + depends_on: + - service-configuration + - consul + + service-configuration: + build: + context: ./config + volumes: + - service-config:/config + + ftp: + image: hauptmedia/proftpd + ports: + - "${OL_FTP_PORT_21:-21}:21" + - "${OL_FTP_PORT_20:-20}:20" + env_file: settings.env + depends_on: [consul] + + redis: + image: redis:3.2.12 + depends_on: [consul] + + pointofdelivery: + image: elmislesotho/pointofdelivery:${OL_POINTOFDELIVERY_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'pointofdelivery-extensions:/extensions' + - 'service-config:/config' + depends_on: [log, db] + + # example-extensions: + # image: openlmis/openlmis-example-extensions:latest + # volumes: + # - 'example-extensions:/extensions' + +volumes: + syslog: + external: false + nginx-log: + external: false + consul-template-log: + external: false + service-config: + external: false + pointofdelivery-extensions: + external: false diff --git a/docker-compose.openlmis-pointofdelivery-init.yml b/docker-compose.openlmis-pointofdelivery-init.yml index 4064ec3..7e65254 100644 --- a/docker-compose.openlmis-pointofdelivery-init.yml +++ b/docker-compose.openlmis-pointofdelivery-init.yml @@ -23,7 +23,7 @@ services: depends_on: [consul] reference-ui: - image: openlmis/reference-ui:${OL_REFERENCE_UI_VERSION} + image: elmislesotho/reference-ui:${OL_REFERENCE_UI_VERSION} env_file: settings.env depends_on: [consul] @@ -88,7 +88,7 @@ services: command: ["/wait-for-postgres.sh", "/run.sh"] stockmanagement: - image: openlmis/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} + image: elmislesotho/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} env_file: settings.env environment: JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' @@ -174,15 +174,15 @@ services: image: redis:3.2.12 depends_on: [consul] - template-service: - image: openlmis/template-service:latest + pointofdelivery: + image: elmislesotho/pointofdelivery:${OL_POINTOFDELIVERY_VERSION} env_file: settings.env environment: JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' volumes: - 'pointofdelivery-extensions:/extensions' - 'service-config:/config' - depends_on: [log] + depends_on: [log, db] # example-extensions: # image: openlmis/openlmis-example-extensions:latest diff --git a/start-local.sh b/start-local.sh index 1367812..9069a36 100755 --- a/start-local.sh +++ b/start-local.sh @@ -49,8 +49,9 @@ setEnvByIp BOLD=$(tput bold) echo "Starting OpenLMIS Ref-Distro on ${BOLD}${HOST_ADDR}" docker-compose \ - -f docker-compose.yml \ + -f docker-compose.openlmis-dev.yml \ up \ --build \ --remove-orphans \ - --force-recreate + --force-recreate \ + -d From c52b803a284d2720f8babf11764e70e5218f8285 Mon Sep 17 00:00:00 2001 From: lmphatsi Date: Thu, 21 Dec 2023 18:25:58 +0200 Subject: [PATCH 18/46] Production compose file --- .env | 9 +- docker-compose.openlmis-prod.yml | 205 +++++++++++++++++++++++++++++++ start-local.sh | 5 +- 3 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 docker-compose.openlmis-prod.yml diff --git a/.env b/.env index 455fbea..697b033 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -OL_REFERENCE_UI_VERSION=1.0.7 +OL_REFERENCE_UI_VERSION=1.0.8 OL_REQUISITION_VERSION=8.3.7-SNAPSHOT OL_REFERENCEDATA_VERSION=15.2.6-SNAPSHOT @@ -6,8 +6,11 @@ OL_AUTH_VERSION=4.3.4-SNAPSHOT OL_NOTIFICATION_VERSION=4.3.4-SNAPSHOT OL_FULFILLMENT_VERSION=9.0.5-SNAPSHOT OL_CCE_VERSION=1.3.3-SNAPSHOT -OL_STOCKMANAGEMENT_VERSION=1.0.2 -OL_POINTOFDELIVERY_VERSION=1.0.0 +OL_STOCKMANAGEMENT_VERSION=1.0.3 +#OL_STOCKMANAGEMENT_VERSION=latest +#OL_POINTOFDELIVERY_VERSION=1.0.1 +OL_POINTOFDELIVERY_VERSION=1.0.4 +OL_INVENTORYMANAGEMENT_VERSION=latest OL_REPORT_VERSION=1.2.3-SNAPSHOT OL_BUQ_VERSION=1.0.0-SNAPSHOT OL_HAPIFHIR_VERSION=2.0.3-SNAPSHOT diff --git a/docker-compose.openlmis-prod.yml b/docker-compose.openlmis-prod.yml new file mode 100644 index 0000000..2237d06 --- /dev/null +++ b/docker-compose.openlmis-prod.yml @@ -0,0 +1,205 @@ +version: "3.3" +services: + + consul: + command: -server -bootstrap + image: gliderlabs/consul-server + ports: + - "8300" + - "8400" + - "8500:8500" + - "53" + + nginx: + image: openlmis/nginx:${OL_NGINX_VERSION} + ports: + - "${OL_HTTP_PORT:-80}:80" + env_file: settings.env + environment: + NGINX_LOG_DIR: '/var/log/nginx/log' + volumes: + - 'nginx-log:/var/log/nginx/log' + - 'consul-template-log:/var/log/consul-template' + depends_on: [consul] + + reference-ui: + image: elmislesotho/reference-ui:${OL_REFERENCE_UI_VERSION} + env_file: settings.env + depends_on: [consul] + + requisition: + image: openlmis/requisition:${OL_REQUISITION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + referencedata: + image: openlmis/referencedata:${OL_REFERENCEDATA_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + auth: + image: openlmis/auth:${OL_AUTH_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + notification: + image: openlmis/notification:${OL_NOTIFICATION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + fulfillment: + image: openlmis/fulfillment:${OL_FULFILLMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + cce: + image: openlmis/cce:${OL_CCE_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + stockmanagement: + image: elmislesotho/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} + #image: openlmis/stockmanagement:latest + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + report: + image: openlmis/report:${OL_REPORT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + buq: + image: openlmis/buq:${OL_BUQ_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [ log] + command: [ "/wait-for-postgres.sh", "/run.sh" ] + + hapifhir: + restart: always + image: openlmis/hapifhir:${OL_HAPIFHIR_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log] + command: ["/wait-for-postgres.sh", "/run.sh"] + + diagnostics: + image: openlmis/diagnostics:${OL_DIAGNOSTICS_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log] + + # db: + # image: openlmis/postgres:${OL_POSTGRES_VERSION} + # env_file: settings.env + # networks: + # default: + # aliases: + # - olmis-db + # depends_on: [consul] + + log: + image: openlmis/rsyslog:${OL_RSYSLOG_VERSION} + volumes: + - 'syslog:/var/log' + depends_on: + - service-configuration + - consul + + service-configuration: + build: + context: ./config + volumes: + - service-config:/config + + ftp: + image: hauptmedia/proftpd + ports: + - "${OL_FTP_PORT_21:-21}:21" + - "${OL_FTP_PORT_20:-20}:20" + env_file: settings.env + depends_on: [consul] + + redis: + image: redis:3.2.12 + depends_on: [consul] + + pointofdelivery: + #image: elmislesotho/pointofdelivery:${OL_POINTOFDELIVERY_VERSION} + image: elmislesotho/pointofdelivery:${OL_POINTOFDELIVERY_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'pointofdelivery-extensions:/extensions' + - 'service-config:/config' + depends_on: [log] + command: [ "/wait-for-postgres.sh", "/run.sh" ] + + # example-extensions: + # image: openlmis/openlmis-example-extensions:latest + # volumes: + # - 'example-extensions:/extensions' + +volumes: + syslog: + external: false + nginx-log: + external: false + consul-template-log: + external: false + service-config: + external: false + pointofdelivery-extensions: + external: false diff --git a/start-local.sh b/start-local.sh index 9069a36..e8fc6d1 100755 --- a/start-local.sh +++ b/start-local.sh @@ -49,9 +49,8 @@ setEnvByIp BOLD=$(tput bold) echo "Starting OpenLMIS Ref-Distro on ${BOLD}${HOST_ADDR}" docker-compose \ - -f docker-compose.openlmis-dev.yml \ + -f docker-compose.openlmis-prod.yml \ up \ --build \ --remove-orphans \ - --force-recreate \ - -d + --force-recreate From 5de86d7092157f112e3966a47996542d69379091 Mon Sep 17 00:00:00 2001 From: lmphatsi Date: Thu, 18 Jan 2024 06:46:38 +0200 Subject: [PATCH 19/46] bump up pointofdelivery and stockmanagement versions --- .env | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 697b033..474ca37 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -OL_REFERENCE_UI_VERSION=1.0.8 +OL_REFERENCE_UI_VERSION=1.1.0 OL_REQUISITION_VERSION=8.3.7-SNAPSHOT OL_REFERENCEDATA_VERSION=15.2.6-SNAPSHOT @@ -6,10 +6,10 @@ OL_AUTH_VERSION=4.3.4-SNAPSHOT OL_NOTIFICATION_VERSION=4.3.4-SNAPSHOT OL_FULFILLMENT_VERSION=9.0.5-SNAPSHOT OL_CCE_VERSION=1.3.3-SNAPSHOT -OL_STOCKMANAGEMENT_VERSION=1.0.3 +OL_STOCKMANAGEMENT_VERSION=1.0.5 #OL_STOCKMANAGEMENT_VERSION=latest #OL_POINTOFDELIVERY_VERSION=1.0.1 -OL_POINTOFDELIVERY_VERSION=1.0.4 +OL_POINTOFDELIVERY_VERSION=1.0.6 OL_INVENTORYMANAGEMENT_VERSION=latest OL_REPORT_VERSION=1.2.3-SNAPSHOT OL_BUQ_VERSION=1.0.0-SNAPSHOT From 877c5ad10513c072db5b40c1fb4c748aa596cfaa Mon Sep 17 00:00:00 2001 From: lmphatsi Date: Tue, 6 Feb 2024 12:33:06 +0200 Subject: [PATCH 20/46] auto evolve connector schema --- reporting/config/services/connect/sink-referencedata.json | 1 + reporting/config/services/connect/sink-requisition.json | 1 + 2 files changed, 2 insertions(+) diff --git a/reporting/config/services/connect/sink-referencedata.json b/reporting/config/services/connect/sink-referencedata.json index 8c817cb..8a547fb 100644 --- a/reporting/config/services/connect/sink-referencedata.json +++ b/reporting/config/services/connect/sink-referencedata.json @@ -8,6 +8,7 @@ "connection.user": "postgres", "connection.password": "p@ssw0rd", "auto.create": "true", + "auto.evolve": "true", "insert.mode": "upsert", "pk.fields": "id", "pk.mode": "record_key", diff --git a/reporting/config/services/connect/sink-requisition.json b/reporting/config/services/connect/sink-requisition.json index 41a182c..cc78bf1 100644 --- a/reporting/config/services/connect/sink-requisition.json +++ b/reporting/config/services/connect/sink-requisition.json @@ -8,6 +8,7 @@ "connection.user": "postgres", "connection.password": "p@ssw0rd", "auto.create": "true", + "auto.evolve": "true", "insert.mode": "upsert", "pk.fields": "id", "pk.mode": "record_key", From 34aa923ceebed9ed92cdc31c53e8cec74ae1ff7d Mon Sep 17 00:00:00 2001 From: Lethusang Date: Sat, 17 Feb 2024 11:48:05 +0200 Subject: [PATCH 21/46] SUPERSET_URL in settings-sample.env --- reporting/settings-sample.env | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reporting/settings-sample.env b/reporting/settings-sample.env index ffe4f6a..8cd83d9 100644 --- a/reporting/settings-sample.env +++ b/reporting/settings-sample.env @@ -72,6 +72,8 @@ SUPERSET_SECRET_KEY=changeme # Note: Comment out this variable if you use it on production # OAUTHLIB_INSECURE_TRANSPORT=1 +SUPERSET_URL= + # The domain name to use for Superset SUPERSET_DOMAIN_NAME=superset.local # The name of the SSL certificate file in services/nginx/tls to use with the Superset domain From 914acce956f820e0975e088e6c66ea7787625fa0 Mon Sep 17 00:00:00 2001 From: Lethusang Date: Sat, 17 Feb 2024 11:51:46 +0200 Subject: [PATCH 22/46] SUPERSET_URL in settings-sample.env --- reporting/settings-sample.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reporting/settings-sample.env b/reporting/settings-sample.env index 8cd83d9..c110c3b 100644 --- a/reporting/settings-sample.env +++ b/reporting/settings-sample.env @@ -71,7 +71,7 @@ SUPERSET_SECRET_KEY=changeme # Disabling SSL check in Superset service. By default sign-in via OAUTH requires OpenLMIS with HTTPS security # Note: Comment out this variable if you use it on production # OAUTHLIB_INSECURE_TRANSPORT=1 - +#Add URL to your superset SUPERSET_URL= # The domain name to use for Superset From db8ef2be6a2e38b4ce622bbbc27c13dc67e875d5 Mon Sep 17 00:00:00 2001 From: Lethusang Date: Sat, 17 Feb 2024 11:57:28 +0200 Subject: [PATCH 23/46] Update to Superset Dockerfile --- reporting/superset/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reporting/superset/Dockerfile b/reporting/superset/Dockerfile index 5bfb7ab..990121e 100644 --- a/reporting/superset/Dockerfile +++ b/reporting/superset/Dockerfile @@ -61,6 +61,9 @@ RUN useradd -U -m superset && \ flask-mail==0.9.1 \ flask-oauth==0.12 \ flask_oauthlib==0.9.5 && \ + # Upgrade the packages + pip install typing-extensions==4.7.1 --upgrade && \ + pip install selenium --upgrade && \ # MarkupSafe, fix missing 'soft_unicode' from 'markupsafe' # fix cryptography, flask, werkzeug to last known working versions pip install -I --no-cache-dir \ From 723613277d1be10383e5780945ebd8674cc28993 Mon Sep 17 00:00:00 2001 From: Lethusang Date: Sat, 17 Feb 2024 12:21:53 +0200 Subject: [PATCH 24/46] Update to superset Documentation --- reporting/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reporting/README.md b/reporting/README.md index a63ac9a..cec8835 100644 --- a/reporting/README.md +++ b/reporting/README.md @@ -13,8 +13,9 @@ $ cp settings-sample.env settings.env ``` * Edit `settings.env` to match your setup. Despite generating the passwords, you will likely need to change - `VIRTUAL_HOST`, `TRUSTED_HOSTNAME`, `OL_BASE_URL` (to point to OpenLMIS) and `NIFI_DOMAIN_NAME` and `SUPERSET_DOMAIN_NAME` (which should point to the reporting stack). + `VIRTUAL_HOST`,`TRUSTED_HOSTNAME`, `OL_BASE_URL` (to point to OpenLMIS), `NIFI_DOMAIN_NAME` and `SUPERSET_DOMAIN_NAME` (which should point to the reporting stack) and `SUPERSET_URL` to point to the Superset URL. Details on all the environment variables are below. + * NB: `SUPERSET_URL` in `settings.env` file in OpenLMIS should be updated to the superset URL. 5. Bring up the reporting stack by running [docker-compose](https://docs.docker.com/compose/) on the server: ```sh From 022220f6d368c2ba1cb97130f22ee49a3f615d7a Mon Sep 17 00:00:00 2001 From: ksetetemela Date: Tue, 16 Apr 2024 05:14:45 +0200 Subject: [PATCH 25/46] Include prepacking service --- .env | 6 ++++-- docker-compose.openlmis-dev.yml | 12 +++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 474ca37..0337008 100644 --- a/.env +++ b/.env @@ -1,12 +1,14 @@ -OL_REFERENCE_UI_VERSION=1.1.0 +OL_REFERENCE_UI_VERSION=1.0.8 -OL_REQUISITION_VERSION=8.3.7-SNAPSHOT +#OL_REQUISITION_VERSION=8.3.7-SNAPSHOT +OL_REQUISITION_VERSION=1.0.1 OL_REFERENCEDATA_VERSION=15.2.6-SNAPSHOT OL_AUTH_VERSION=4.3.4-SNAPSHOT OL_NOTIFICATION_VERSION=4.3.4-SNAPSHOT OL_FULFILLMENT_VERSION=9.0.5-SNAPSHOT OL_CCE_VERSION=1.3.3-SNAPSHOT OL_STOCKMANAGEMENT_VERSION=1.0.5 +OL_PREPACKING_VERSION=1.0.0 #OL_STOCKMANAGEMENT_VERSION=latest #OL_POINTOFDELIVERY_VERSION=1.0.1 OL_POINTOFDELIVERY_VERSION=1.0.6 diff --git a/docker-compose.openlmis-dev.yml b/docker-compose.openlmis-dev.yml index 7e65254..264c91c 100644 --- a/docker-compose.openlmis-dev.yml +++ b/docker-compose.openlmis-dev.yml @@ -28,7 +28,7 @@ services: depends_on: [consul] requisition: - image: openlmis/requisition:${OL_REQUISITION_VERSION} + image: elmislesotho/requisition:${OL_REQUISITION_VERSION} env_file: settings.env environment: JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' @@ -97,6 +97,16 @@ services: depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] + prepacking: + image: elmislesotho/prepacking:${OL_PREPACKING_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + report: image: openlmis/report:${OL_REPORT_VERSION} env_file: settings.env From 07c4b1733245735776f0c6028a30a7c07e0104af Mon Sep 17 00:00:00 2001 From: Lebajoa Mphatsi Date: Wed, 17 Apr 2024 20:30:31 +0200 Subject: [PATCH 26/46] add dispensing service --- docker-compose.openlmis-dev.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docker-compose.openlmis-dev.yml b/docker-compose.openlmis-dev.yml index 264c91c..697e738 100644 --- a/docker-compose.openlmis-dev.yml +++ b/docker-compose.openlmis-dev.yml @@ -107,6 +107,16 @@ services: depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] + dispensing: + image: elmislesotho/dispensing:${OL_DISPENSING_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + report: image: openlmis/report:${OL_REPORT_VERSION} env_file: settings.env From f8c69df6f3632ad9db8fae450192c2579c3c2ae4 Mon Sep 17 00:00:00 2001 From: Lebajoa Mphatsi Date: Wed, 17 Apr 2024 20:31:28 +0200 Subject: [PATCH 27/46] add dispensing service version --- .env | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 0337008..69283ea 100644 --- a/.env +++ b/.env @@ -9,6 +9,7 @@ OL_FULFILLMENT_VERSION=9.0.5-SNAPSHOT OL_CCE_VERSION=1.3.3-SNAPSHOT OL_STOCKMANAGEMENT_VERSION=1.0.5 OL_PREPACKING_VERSION=1.0.0 +OL_DISPENSING_VERSION=1.0.0 #OL_STOCKMANAGEMENT_VERSION=latest #OL_POINTOFDELIVERY_VERSION=1.0.1 OL_POINTOFDELIVERY_VERSION=1.0.6 @@ -25,4 +26,4 @@ OL_DIAGNOSTICS_VERSION=1.1.3-SNAPSHOT OL_NGINX_VERSION=5 OL_RSYSLOG_VERSION=1 -OL_POSTGRES_VERSION=12-debezium \ No newline at end of file +OL_POSTGRES_VERSION=12-debezium From 9c82fb72978e4d276d1295e9ee42ca7a84fcbd82 Mon Sep 17 00:00:00 2001 From: Mohau Nkepane Date: Thu, 24 Oct 2024 14:40:21 +0200 Subject: [PATCH 28/46] Add Changes --- .env | 25 ++++++++------- docker-compose.openlmis-dev.yml | 54 ++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/.env b/.env index 69283ea..d1307f6 100644 --- a/.env +++ b/.env @@ -1,15 +1,17 @@ -OL_REFERENCE_UI_VERSION=1.0.8 - +OL_REFERENCE_UI_VERSION=1.3.7 #OL_REQUISITION_VERSION=8.3.7-SNAPSHOT -OL_REQUISITION_VERSION=1.0.1 -OL_REFERENCEDATA_VERSION=15.2.6-SNAPSHOT -OL_AUTH_VERSION=4.3.4-SNAPSHOT -OL_NOTIFICATION_VERSION=4.3.4-SNAPSHOT -OL_FULFILLMENT_VERSION=9.0.5-SNAPSHOT +OL_REQUISITION_VERSION=1.0.7 +OL_REFERENCEDATA_VERSION=1.0.1 +OL_AUTH_VERSION=0.0.1 +OL_NOTIFICATION_VERSION=1.0.7 +#OL_FULFILLMENT_VERSION=9.0.5-SNAPSHOT +OL_FULFILLMENT_VERSION=1.0.0 OL_CCE_VERSION=1.3.3-SNAPSHOT -OL_STOCKMANAGEMENT_VERSION=1.0.5 -OL_PREPACKING_VERSION=1.0.0 -OL_DISPENSING_VERSION=1.0.0 +OL_STOCKMANAGEMENT_VERSION=1.1.5 +#curr stock 1.1.4 +OL_PREPACKING_VERSION=1.0.9 +#OL_PREPACKING_VERSION=latest +OL_DISPENSING_VERSION=1.1.0 #OL_STOCKMANAGEMENT_VERSION=latest #OL_POINTOFDELIVERY_VERSION=1.0.1 OL_POINTOFDELIVERY_VERSION=1.0.6 @@ -23,7 +25,8 @@ OL_ONE_NETWORK_INTEGRATION_STOCKMANAGEMENT_EXTENSION_VERSION=0.0.1 OL_DIAGNOSTICS_VERSION=1.1.3-SNAPSHOT -OL_NGINX_VERSION=5 +#OL_NGINX_VERSION=5 +OL_NGINX_VERSION=1.0.0 OL_RSYSLOG_VERSION=1 OL_POSTGRES_VERSION=12-debezium diff --git a/docker-compose.openlmis-dev.yml b/docker-compose.openlmis-dev.yml index 697e738..090d715 100644 --- a/docker-compose.openlmis-dev.yml +++ b/docker-compose.openlmis-dev.yml @@ -11,12 +11,14 @@ services: - "53" nginx: - image: openlmis/nginx:${OL_NGINX_VERSION} + #image: openlmis/nginx:${OL_NGINX_VERSION} + image: elmislesotho/nginx:${OL_NGINX_VERSION} ports: - "${OL_HTTP_PORT:-80}:80" env_file: settings.env environment: NGINX_LOG_DIR: '/var/log/nginx/log' + NGINX_TIMEOUT: '300s' volumes: - 'nginx-log:/var/log/nginx/log' - 'consul-template-log:/var/log/consul-template' @@ -31,24 +33,48 @@ services: image: elmislesotho/requisition:${OL_REQUISITION_VERSION} env_file: settings.env environment: - JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + #JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + JAVA_OPTS: > + -server + -Xms1024m + -Xmx3072m + -XX:+UseG1GC + -XX:MetaspaceSize=128m + -XX:MaxMetaspaceSize=256m + -Xss512k + -XX:+PrintGCDetails + -XX:+PrintGCDateStamps + -Xloggc:/var/log/requisition/gc.log + -Dlogging.config=/config/log/logback.xml volumes: - 'service-config:/config' depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] referencedata: - image: openlmis/referencedata:${OL_REFERENCEDATA_VERSION} + image: elmislesotho/referencedata:${OL_REFERENCEDATA_VERSION} env_file: settings.env environment: - JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + #JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + JAVA_OPTS: > + -server + -Xms1024m + -Xmx3072m + -XX:+UseG1GC + -XX:MetaspaceSize=128m + -XX:MaxMetaspaceSize=256m + -Xss512k + -XX:+PrintGCDetails + -XX:+PrintGCDateStamps + -Xloggc:/var/log/referencedata/gc.log + -Dlogging.config=/config/log/logback.xml volumes: - 'service-config:/config' depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] auth: - image: openlmis/auth:${OL_AUTH_VERSION} + image: elmislesotho/auth:${OL_AUTH_VERSION} env_file: settings.env environment: JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' @@ -58,7 +84,7 @@ services: command: ["/wait-for-postgres.sh", "/run.sh"] notification: - image: openlmis/notification:${OL_NOTIFICATION_VERSION} + image: elmislesotho/notification:${OL_NOTIFICATION_VERSION} env_file: settings.env environment: JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' @@ -68,7 +94,7 @@ services: command: ["/wait-for-postgres.sh", "/run.sh"] fulfillment: - image: openlmis/fulfillment:${OL_FULFILLMENT_VERSION} + image: elmislesotho/fulfillment:${OL_FULFILLMENT_VERSION} env_file: settings.env environment: JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' @@ -91,7 +117,19 @@ services: image: elmislesotho/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} env_file: settings.env environment: - JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + #JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + JAVA_OPTS: > + -server + -Xms1024m + -Xmx3072m + -XX:+UseG1GC + -XX:MetaspaceSize=128m + -XX:MaxMetaspaceSize=256m + -Xss512k + -XX:+PrintGCDetails + -XX:+PrintGCDateStamps + -Xloggc:/var/log/stockmanagement/gc.log + -Dlogging.config=/config/log/logback.xml volumes: - 'service-config:/config' depends_on: [log, db] From 73ce368e8f866e1c208f78ac7680ff1d9a277eb4 Mon Sep 17 00:00:00 2001 From: eLMIS Lesotho Date: Thu, 5 Dec 2024 11:48:51 +0200 Subject: [PATCH 29/46] 05122024 --- .env | 6 +- ...e.openlmis-dev.yml.beforeTuning_10July2024 | 222 ++++++++++++++++++ .../meta.json | 1 + .../private_key.json | 1 + .../regr.json | 1 + 5 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 docker-compose.openlmis-dev.yml.beforeTuning_10July2024 create mode 100644 letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/meta.json create mode 100644 letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json create mode 100644 letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/regr.json diff --git a/.env b/.env index d1307f6..4a6a9ae 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -OL_REFERENCE_UI_VERSION=1.3.7 +OL_REFERENCE_UI_VERSION=1.3.9 #OL_REQUISITION_VERSION=8.3.7-SNAPSHOT OL_REQUISITION_VERSION=1.0.7 OL_REFERENCEDATA_VERSION=1.0.1 @@ -7,11 +7,11 @@ OL_NOTIFICATION_VERSION=1.0.7 #OL_FULFILLMENT_VERSION=9.0.5-SNAPSHOT OL_FULFILLMENT_VERSION=1.0.0 OL_CCE_VERSION=1.3.3-SNAPSHOT -OL_STOCKMANAGEMENT_VERSION=1.1.5 +OL_STOCKMANAGEMENT_VERSION=1.1.6 #curr stock 1.1.4 OL_PREPACKING_VERSION=1.0.9 #OL_PREPACKING_VERSION=latest -OL_DISPENSING_VERSION=1.1.0 +OL_DISPENSING_VERSION=1.1.1 #OL_STOCKMANAGEMENT_VERSION=latest #OL_POINTOFDELIVERY_VERSION=1.0.1 OL_POINTOFDELIVERY_VERSION=1.0.6 diff --git a/docker-compose.openlmis-dev.yml.beforeTuning_10July2024 b/docker-compose.openlmis-dev.yml.beforeTuning_10July2024 new file mode 100644 index 0000000..5b96f54 --- /dev/null +++ b/docker-compose.openlmis-dev.yml.beforeTuning_10July2024 @@ -0,0 +1,222 @@ +version: "3.3" +services: + + consul: + command: -server -bootstrap + image: gliderlabs/consul-server + ports: + - "8300" + - "8400" + - "8500:8500" + - "53" + + nginx: + image: openlmis/nginx:${OL_NGINX_VERSION} + ports: + - "${OL_HTTP_PORT:-80}:80" + env_file: settings.env + environment: + NGINX_LOG_DIR: '/var/log/nginx/log' + volumes: + - 'nginx-log:/var/log/nginx/log' + - 'consul-template-log:/var/log/consul-template' + depends_on: [consul] + + reference-ui: + image: elmislesotho/reference-ui:${OL_REFERENCE_UI_VERSION} + env_file: settings.env + depends_on: [consul] + + requisition: + image: elmislesotho/requisition:${OL_REQUISITION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + referencedata: + image: openlmis/referencedata:${OL_REFERENCEDATA_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + auth: + image: openlmis/auth:${OL_AUTH_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + notification: + image: elmislesotho/notification:${OL_NOTIFICATION_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + fulfillment: + image: openlmis/fulfillment:${OL_FULFILLMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + cce: + image: openlmis/cce:${OL_CCE_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + stockmanagement: + image: elmislesotho/stockmanagement:${OL_STOCKMANAGEMENT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + prepacking: + image: elmislesotho/prepacking:${OL_PREPACKING_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + dispensing: + image: elmislesotho/dispensing:${OL_DISPENSING_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + report: + image: openlmis/report:${OL_REPORT_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + buq: + image: openlmis/buq:${OL_BUQ_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx1024m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'service-config:/config' + depends_on: [ log, db ] + command: [ "/wait-for-postgres.sh", "/run.sh" ] + + hapifhir: + restart: always + image: openlmis/hapifhir:${OL_HAPIFHIR_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log, db] + command: ["/wait-for-postgres.sh", "/run.sh"] + + diagnostics: + image: openlmis/diagnostics:${OL_DIAGNOSTICS_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + spring_profiles_active: ${spring_profiles_active} + volumes: + - 'service-config:/config' + depends_on: [log] + + db: + image: openlmis/postgres:${OL_POSTGRES_VERSION} + env_file: settings.env + networks: + default: + aliases: + - olmis-db + depends_on: [consul] + + log: + image: openlmis/rsyslog:${OL_RSYSLOG_VERSION} + volumes: + - 'syslog:/var/log' + depends_on: + - service-configuration + - consul + + service-configuration: + build: + context: ./config + volumes: + - service-config:/config + + ftp: + image: hauptmedia/proftpd + ports: + - "${OL_FTP_PORT_21:-21}:21" + - "${OL_FTP_PORT_20:-20}:20" + env_file: settings.env + depends_on: [consul] + + redis: + image: redis:3.2.12 + depends_on: [consul] + + pointofdelivery: + image: elmislesotho/pointofdelivery:${OL_POINTOFDELIVERY_VERSION} + env_file: settings.env + environment: + JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + volumes: + - 'pointofdelivery-extensions:/extensions' + - 'service-config:/config' + depends_on: [log, db] + + # example-extensions: + # image: openlmis/openlmis-example-extensions:latest + # volumes: + # - 'example-extensions:/extensions' + +volumes: + syslog: + external: false + nginx-log: + external: false + consul-template-log: + external: false + service-config: + external: false + pointofdelivery-extensions: + external: false diff --git a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/meta.json b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/meta.json new file mode 100644 index 0000000..c978058 --- /dev/null +++ b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/meta.json @@ -0,0 +1 @@ +{"creation_dt": "2024-06-20T05:22:42Z", "creation_host": "c638c0b4c259", "register_to_eff": "elmislesotho@gmail.com"} \ No newline at end of file diff --git a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json new file mode 100644 index 0000000..2233b08 --- /dev/null +++ b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json @@ -0,0 +1 @@ +{"n": "xygZNFHE4SkoPYybagzqA2YWkbCYQiJDQ2m21zD-vr_NWLWpFQz4giWnCaKC67bBjyOb_E8CHUDobPCugWrsF8DxDEqWKOzkHdPo7rZJkK7herd2JUNxwfrG5EN1QjMz6691T3o9uuU8GL_LGFHwlX5kH6atQFV0Qd9_aujFFw2lo-83Zu1pjuEBb_huhcK8ovzK9wHsajOv6PXuH7ukMWVITtuy2dLT2GAPEd0_Fg2JCQAqCaY0oCfhXzFMKtEbE3yaVogqJ9HjDsMEwgQ8MHmOZNW6k0aKd3ePha9ZyT0A2W9hSJM2IPC0hLvZdZtPtoUjtTwGIuYMgBNlLcbHew", "e": "AQAB", "d": "QesnzIV0IQK6awTrPALiQhW8SqSlAN7OXLXU6w0whHp_wWj_ehQ8_PbxfuLnapWnI3Wp361XCc--HrDbUcLOOl6GaocVhJ5EMws9AiYb22PHBhNLa7ITKR2UT2F0omDIQq5kqdXQMNhHN_njgz074H9hLIjCt5SRUmxjhENfWO15TqzYf4Xw3i1Wi7kj0YKI4-fC4yaJk2DgtAMosLSeTpDWNfZwBCOD8Lf5Y7WX1jSlGa14Cn6oe_Ao1W9AC1ANDcO8ySVt4nvMCzBsHqosn6OG4H152A_dOQz8tWVcNSBd44J2-Xv0_EvMhv2B6fgJUGvp6RPm4RnlfcoqGUCTIQ", "p": "-BhLbWvg_xDA07pfhNsjGifxcC4LXefEQCmAZnAYLIuNdSRJDT5zGsCQlU3R9YycyMjJ3eqOFs0EjlvgCcNKFL0zoL8iQ45Y0_bE72xSu6_5C2GUe09OugF0Si70yv71TNyfSuPrplnvRnrJPbESwqKCwHomsh5FdZbeb2lMAN8", "q": "zYCdhTJYdfZhpS0UjTSIYX70bTVVAgxLy3WVMnrw2ahm_hdOFHbXfA1tEp-o9jhVkX04NmgvWqSh1AJTOX_Wa-dXP5aFrp0imCVMmHwXniBLL6VnjRcyPZdckIKnR1Ztz2p4EMa2-A2d9NPHKv94mpLir0S9Pd3QhUJuUSp2AOU", "dp": "JkiRcxwfhxyNtN5gmP67xvB8-EOSq3X1ObVtw-DuSglq1bW4tBt75Zfrd66fhJWjJ_Sb-euU633Ngy44PylsiBC-I8L6xZP0rxi4QP3lMmMwOgpfwDt3uYo56KygR6baMXcl-Yd8OJpOKwENhsCJVZGAPbVbvUagj0mtn2GPQd8", "dq": "BIZXcY5ZOkexgLSMZkuI0TS9ZwYsl_3zUUiGf3ULho9ZrTCQLI0tU3LsOuO_EaTN5YUvTqtel41WbteaQ4LYEsomNqU2zlBxaY4wsI7S6k1lumfW7tla5tZFWH03968kfdOnTx9A-hc6qiBbm0vCuQyQ5zEJRapzo-uBiFH6BS0", "qi": "qfiS0d86onmu60ljZuxOEivZml49GayPkZMLd9WT2neeMt5PLpARloPOvZ7Uf78cISq-Dzxb320alfdtjPrha-JjHHAuCUnYowV2wI9Ny3sXuTuMXWuVEM7cM5E5x5UHvz08d2iCwuhOD1g9Mcv1d0xkdaoPqybX-KvV-nNVb7g", "kty": "RSA"} \ No newline at end of file diff --git a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/regr.json b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/regr.json new file mode 100644 index 0000000..03d43eb --- /dev/null +++ b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/regr.json @@ -0,0 +1 @@ +{"body": {}, "uri": "https://acme-v02.api.letsencrypt.org/acme/acct/1792313197"} \ No newline at end of file From 74515ab769a19e3c968ec643de72be4f8c01aa58 Mon Sep 17 00:00:00 2001 From: eLMIS Lesotho Date: Thu, 5 Dec 2024 11:52:17 +0200 Subject: [PATCH 30/46] 05122024-2 --- .../directory/e36103b879cd7615c42ae35b6cbcf100/meta.json | 1 - .../directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json | 1 - .../directory/e36103b879cd7615c42ae35b6cbcf100/regr.json | 1 - 3 files changed, 3 deletions(-) delete mode 100644 letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/meta.json delete mode 100644 letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json delete mode 100644 letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/regr.json diff --git a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/meta.json b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/meta.json deleted file mode 100644 index c978058..0000000 --- a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/meta.json +++ /dev/null @@ -1 +0,0 @@ -{"creation_dt": "2024-06-20T05:22:42Z", "creation_host": "c638c0b4c259", "register_to_eff": "elmislesotho@gmail.com"} \ No newline at end of file diff --git a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json deleted file mode 100644 index 2233b08..0000000 --- a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/private_key.json +++ /dev/null @@ -1 +0,0 @@ -{"n": "xygZNFHE4SkoPYybagzqA2YWkbCYQiJDQ2m21zD-vr_NWLWpFQz4giWnCaKC67bBjyOb_E8CHUDobPCugWrsF8DxDEqWKOzkHdPo7rZJkK7herd2JUNxwfrG5EN1QjMz6691T3o9uuU8GL_LGFHwlX5kH6atQFV0Qd9_aujFFw2lo-83Zu1pjuEBb_huhcK8ovzK9wHsajOv6PXuH7ukMWVITtuy2dLT2GAPEd0_Fg2JCQAqCaY0oCfhXzFMKtEbE3yaVogqJ9HjDsMEwgQ8MHmOZNW6k0aKd3ePha9ZyT0A2W9hSJM2IPC0hLvZdZtPtoUjtTwGIuYMgBNlLcbHew", "e": "AQAB", "d": "QesnzIV0IQK6awTrPALiQhW8SqSlAN7OXLXU6w0whHp_wWj_ehQ8_PbxfuLnapWnI3Wp361XCc--HrDbUcLOOl6GaocVhJ5EMws9AiYb22PHBhNLa7ITKR2UT2F0omDIQq5kqdXQMNhHN_njgz074H9hLIjCt5SRUmxjhENfWO15TqzYf4Xw3i1Wi7kj0YKI4-fC4yaJk2DgtAMosLSeTpDWNfZwBCOD8Lf5Y7WX1jSlGa14Cn6oe_Ao1W9AC1ANDcO8ySVt4nvMCzBsHqosn6OG4H152A_dOQz8tWVcNSBd44J2-Xv0_EvMhv2B6fgJUGvp6RPm4RnlfcoqGUCTIQ", "p": "-BhLbWvg_xDA07pfhNsjGifxcC4LXefEQCmAZnAYLIuNdSRJDT5zGsCQlU3R9YycyMjJ3eqOFs0EjlvgCcNKFL0zoL8iQ45Y0_bE72xSu6_5C2GUe09OugF0Si70yv71TNyfSuPrplnvRnrJPbESwqKCwHomsh5FdZbeb2lMAN8", "q": "zYCdhTJYdfZhpS0UjTSIYX70bTVVAgxLy3WVMnrw2ahm_hdOFHbXfA1tEp-o9jhVkX04NmgvWqSh1AJTOX_Wa-dXP5aFrp0imCVMmHwXniBLL6VnjRcyPZdckIKnR1Ztz2p4EMa2-A2d9NPHKv94mpLir0S9Pd3QhUJuUSp2AOU", "dp": "JkiRcxwfhxyNtN5gmP67xvB8-EOSq3X1ObVtw-DuSglq1bW4tBt75Zfrd66fhJWjJ_Sb-euU633Ngy44PylsiBC-I8L6xZP0rxi4QP3lMmMwOgpfwDt3uYo56KygR6baMXcl-Yd8OJpOKwENhsCJVZGAPbVbvUagj0mtn2GPQd8", "dq": "BIZXcY5ZOkexgLSMZkuI0TS9ZwYsl_3zUUiGf3ULho9ZrTCQLI0tU3LsOuO_EaTN5YUvTqtel41WbteaQ4LYEsomNqU2zlBxaY4wsI7S6k1lumfW7tla5tZFWH03968kfdOnTx9A-hc6qiBbm0vCuQyQ5zEJRapzo-uBiFH6BS0", "qi": "qfiS0d86onmu60ljZuxOEivZml49GayPkZMLd9WT2neeMt5PLpARloPOvZ7Uf78cISq-Dzxb320alfdtjPrha-JjHHAuCUnYowV2wI9Ny3sXuTuMXWuVEM7cM5E5x5UHvz08d2iCwuhOD1g9Mcv1d0xkdaoPqybX-KvV-nNVb7g", "kty": "RSA"} \ No newline at end of file diff --git a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/regr.json b/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/regr.json deleted file mode 100644 index 03d43eb..0000000 --- a/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/e36103b879cd7615c42ae35b6cbcf100/regr.json +++ /dev/null @@ -1 +0,0 @@ -{"body": {}, "uri": "https://acme-v02.api.letsencrypt.org/acme/acct/1792313197"} \ No newline at end of file From c12e1d9137f44afe463c28b6c716ff5ac7d87485 Mon Sep 17 00:00:00 2001 From: eLMIS Lesotho Date: Thu, 19 Dec 2024 10:46:55 +0200 Subject: [PATCH 31/46] Add Vitals Button --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 4a6a9ae..4b22b35 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -OL_REFERENCE_UI_VERSION=1.3.9 +OL_REFERENCE_UI_VERSION=1.4.0 #OL_REQUISITION_VERSION=8.3.7-SNAPSHOT OL_REQUISITION_VERSION=1.0.7 OL_REFERENCEDATA_VERSION=1.0.1 From 0fb077c7ed5d82efbf0d26c90330b1ac71bf7373 Mon Sep 17 00:00:00 2001 From: lmphatsi Date: Tue, 21 Jan 2025 11:47:03 +0200 Subject: [PATCH 32/46] disable dispesing and prepacking backend services --- docker-compose.openlmis-dev.yml | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docker-compose.openlmis-dev.yml b/docker-compose.openlmis-dev.yml index 090d715..0eb40da 100644 --- a/docker-compose.openlmis-dev.yml +++ b/docker-compose.openlmis-dev.yml @@ -135,25 +135,25 @@ services: depends_on: [log, db] command: ["/wait-for-postgres.sh", "/run.sh"] - prepacking: - image: elmislesotho/prepacking:${OL_PREPACKING_VERSION} - env_file: settings.env - environment: - JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' - volumes: - - 'service-config:/config' - depends_on: [log, db] - command: ["/wait-for-postgres.sh", "/run.sh"] + # prepacking: + # image: elmislesotho/prepacking:${OL_PREPACKING_VERSION} + # env_file: settings.env + # environment: + # JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + # volumes: + # - 'service-config:/config' + # depends_on: [log, db] + # command: ["/wait-for-postgres.sh", "/run.sh"] - dispensing: - image: elmislesotho/dispensing:${OL_DISPENSING_VERSION} - env_file: settings.env - environment: - JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' - volumes: - - 'service-config:/config' - depends_on: [log, db] - command: ["/wait-for-postgres.sh", "/run.sh"] + # dispensing: + # image: elmislesotho/dispensing:${OL_DISPENSING_VERSION} + # env_file: settings.env + # environment: + # JAVA_OPTS: '-server -Xmx512m -Dlogging.config=/config/log/logback.xml' + # volumes: + # - 'service-config:/config' + # depends_on: [log, db] + # command: ["/wait-for-postgres.sh", "/run.sh"] report: image: openlmis/report:${OL_REPORT_VERSION} From 4294b02fd856da7e78fa5ecc2d6deac466193e3f Mon Sep 17 00:00:00 2001 From: eLMIS Lesotho Date: Tue, 21 Jan 2025 14:10:47 +0200 Subject: [PATCH 33/46] Add New UI Without Dispensing and Prepacking --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 4b22b35..f974af5 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -OL_REFERENCE_UI_VERSION=1.4.0 +OL_REFERENCE_UI_VERSION=1.4.1 #OL_REQUISITION_VERSION=8.3.7-SNAPSHOT OL_REQUISITION_VERSION=1.0.7 OL_REFERENCEDATA_VERSION=1.0.1 From eda2aafa5faaf3950b727306ff71010b8f95a399 Mon Sep 17 00:00:00 2001 From: lmphatsi Date: Fri, 24 Jan 2025 13:35:42 +0200 Subject: [PATCH 34/46] reporting stack changes --- .../services/connect/sink-stockevents.json | 18 +++ .../services/connect/source-stockevents.json | 28 +++++ .../superset/datasources/database.yaml | 10 ++ .../services/superset/superset_config.py | 2 +- reporting/docker-compose.local.yml | 4 +- reporting/superset/Dockerfile | 28 +++-- reporting/superset/Dockerfile.original | 114 ++++++++++++++++++ 7 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 reporting/config/services/connect/sink-stockevents.json create mode 100644 reporting/config/services/connect/source-stockevents.json create mode 100644 reporting/superset/Dockerfile.original diff --git a/reporting/config/services/connect/sink-stockevents.json b/reporting/config/services/connect/sink-stockevents.json new file mode 100644 index 0000000..b2d0140 --- /dev/null +++ b/reporting/config/services/connect/sink-stockevents.json @@ -0,0 +1,18 @@ +{ + "name": "sink-stockevents", + "config": { + "connector.class": "io.confluent.connect.jdbc.JdbcSinkConnector", + "tasks.max": "1", + "topics": "public.kafka_stock_events,public.kafka_stock_event_line_items", + "connection.url": "jdbc:postgresql://reporting-db:5432/open_lmis_reporting?stringtype=unspecified", + "connection.user": "postgres", + "connection.password": "p@ssw0rd", + "auto.create": "true", + "auto.evolve": "true", + "insert.mode": "upsert", + "pk.fields": "id", + "pk.mode": "record_key", + "delete.enabled": "true" + } +} + \ No newline at end of file diff --git a/reporting/config/services/connect/source-stockevents.json b/reporting/config/services/connect/source-stockevents.json new file mode 100644 index 0000000..adcdb0b --- /dev/null +++ b/reporting/config/services/connect/source-stockevents.json @@ -0,0 +1,28 @@ +{ + "name": "source-stockevents", + "config": { + "connector.class": "io.debezium.connector.postgresql.PostgresConnector", + "tasks.max": "1", + "plugin.name": "wal2json", + "database.hostname": "olmis-db", + "database.port": "5432", + "database.user": "postgres", + "database.password": "p@ssw0rd", + "database.dbname": "open_lmis", + "database.server.name": "openlmis", + "table.whitelist": "stockmanagement\\.stock_events,stockmanagement\\.stock_event_line_items", + "database.history.kafka.bootstrap.servers": "kafka:29092", + "database.history.kafka.topic": "openlmis", + "slot.name": "dbz_stockevents", + "heartbeat.interval.ms": 3000, + "heartbeat.action.query": "CREATE TABLE IF NOT EXISTS debezium_heartbeat (id SERIAL PRIMARY KEY, ts TIMESTAMPTZ); INSERT INTO debezium_heartbeat (id, ts) VALUES (1, NOW()) ON CONFLICT(id) DO UPDATE SET ts=NOW();", + "transforms": "unwrap,route", + "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState", + "transforms.unwrap.drop.tombstones": "false", + "transforms.unwrap.delete.handling.mode": "none", + "transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter", + "transforms.route.regex": "([^.]+)\\.([^.]+)\\.([^.]+)", + "transforms.route.replacement": "public.kafka_$3" + } + } + \ No newline at end of file diff --git a/reporting/config/services/superset/datasources/database.yaml b/reporting/config/services/superset/datasources/database.yaml index 81101f1..7175fb7 100644 --- a/reporting/config/services/superset/datasources/database.yaml +++ b/reporting/config/services/superset/datasources/database.yaml @@ -1039,3 +1039,13 @@ databases: - {expression: COUNT(*), metric_name: count, metric_type: count, verbose_name: COUNT(*)} schema: public table_name: kafka_commodity_types + - columns: + - {column_name: processeddate, is_dttm: true, type: TIMESTAMP WITH TIME ZONE} + - {column_name: facilityid, type: UUID} + - {column_name: id, type: UUID} + - {column_name: programid, type: UUID} + main_dttm_col: processeddate + metrics: + - {expression: COUNT(*), metric_name: count, metric_type: count, verbose_name: COUNT(*)} + schema: public + table_name: kafka_stock_events diff --git a/reporting/config/services/superset/superset_config.py b/reporting/config/services/superset/superset_config.py index da4bc78..4157326 100644 --- a/reporting/config/services/superset/superset_config.py +++ b/reporting/config/services/superset/superset_config.py @@ -54,7 +54,7 @@ def lookup_password(url): ] # Set URL scheme for superset-patchup redirect URLs -PREFERRED_URL_SCHEME = "https" +PREFERRED_URL_SCHEME = "http" # The default user self registration role AUTH_USER_REGISTRATION_ROLE = "OLMIS_Gamma" diff --git a/reporting/docker-compose.local.yml b/reporting/docker-compose.local.yml index d152e92..93c4a4d 100644 --- a/reporting/docker-compose.local.yml +++ b/reporting/docker-compose.local.yml @@ -112,7 +112,7 @@ services: - 8083:8083 networks: - default - - openlmis-ref-distro_default + - lesotho-ref-distro_default environment: - BOOTSTRAP_SERVERS=kafka:29092 - GROUP_ID=1 @@ -180,5 +180,5 @@ volumes: external: false networks: - openlmis-ref-distro_default: + lesotho-ref-distro_default: external: true diff --git a/reporting/superset/Dockerfile b/reporting/superset/Dockerfile index 5bfb7ab..238d49c 100644 --- a/reporting/superset/Dockerfile +++ b/reporting/superset/Dockerfile @@ -25,6 +25,7 @@ ENV GUNICORN_BIND=0.0.0.0:8088 \ SUPERSET_HOME=/var/lib/superset \ APP_DIR=${APP_DIR} \ WORKER_CLASS=gevent + ENV GUNICORN_CMD_ARGS="--workers ${GUNICORN_WORKERS} -k ${WORKER_CLASS} --timeout ${GUNICORN_TIMEOUT} --bind ${GUNICORN_BIND} --limit-request-line ${GUNICORN_LIMIT_REQUEST_LINE} --limit-request-field_size ${GUNICORN_LIMIT_REQUEST_FIELD_SIZE}" COPY requirements.txt requirements.txt @@ -46,12 +47,14 @@ RUN useradd -U -m superset && \ postgresql-client \ libssl-dev \ libffi-dev \ - python-dev \ + python3-dev \ libsasl2-dev \ - libldap2-dev && \ + libldap2-dev \ + libjpeg-dev \ + libpng-dev && \ apt-get clean && \ rm -r /var/lib/apt/lists/* && \ - pip install --upgrade setuptools pip && \ + pip install --upgrade setuptools "pip<24.1" && \ pip install --no-cache-dir \ gunicorn==20.1.0 \ gevent==22.10.2 \ @@ -60,15 +63,18 @@ RUN useradd -U -m superset && \ apache-superset[cors]==${SUPERSET_VERSION} \ flask-mail==0.9.1 \ flask-oauth==0.12 \ - flask_oauthlib==0.9.5 && \ - # MarkupSafe, fix missing 'soft_unicode' from 'markupsafe' - # fix cryptography, flask, werkzeug to last known working versions + flask_oauthlib==0.9.5 \ + Flask-Migrate==3.1.0 \ + alembic==1.8.1 \ + redis==4.5.5 && \ + # Fix missing MarkupSafe 'soft_unicode' and ensure Flask compatibility pip install -I --no-cache-dir \ MarkupSafe==2.0.1 \ cryptography==38.0.2 \ flask==1.1.4 \ werkzeug==1.0.1 \ - psycopg2-binary==2.9.1 + psycopg2-binary==2.9.1 \ + pillow # Ensure PIL/Pillow is installed for screenshots # Configure Filesystem RUN find ${APP_DIR} \! -type l -print0 | xargs -0 chown superset:superset @@ -93,10 +99,10 @@ ENV PATH=$NVM_DIR/versions/node/$NODE_VERSION/bin:$PATH RUN node -v RUN npm -v -# Installing dependecies via npm +# Installing dependencies via npm RUN npm install -g po2json -# Fetching dependecies and first build +# Fetching dependencies and first build RUN wget -P /tmp https://github.com/apache/superset/archive/${SUPERSET_VERSION}.zip \ && unzip /tmp/${SUPERSET_VERSION}.zip -d /tmp \ && rsync -a \ @@ -111,4 +117,8 @@ RUN cd $APP_DIR/superset-frontend && npm ci # Deploy application EXPOSE 8088 HEALTHCHECK CMD ["curl", "-f", "http://localhost:8088/health"] + +# Use Redis for caching instead of the metadata database +ENV CACHE_CONFIG='{"CACHE_TYPE": "RedisCache", "CACHE_REDIS_URL": "redis://redis:6379/0"}' + CMD ["gunicorn", "superset:app"] diff --git a/reporting/superset/Dockerfile.original b/reporting/superset/Dockerfile.original new file mode 100644 index 0000000..56148db --- /dev/null +++ b/reporting/superset/Dockerfile.original @@ -0,0 +1,114 @@ +FROM python:3.8.13 + +# Superset version +ARG SUPERSET_VERSION=1.5.2 + +# Superset-patchup (Ketchup) version +ARG SUPERSET_PATCHUP_VERSION=v0.5.1 + +# Default Superset files dir +ARG APP_DIR=/usr/local/lib/python3.8/site-packages/superset + +# Configure environment +ENV GUNICORN_BIND=0.0.0.0:8088 \ + GUNICORN_LIMIT_REQUEST_FIELD_SIZE=0 \ + GUNICORN_LIMIT_REQUEST_LINE=0 \ + GUNICORN_TIMEOUT=60 \ + GUNICORN_WORKERS=2 \ + LANG=C.UTF-8 \ + LC_ALL=C.UTF-8 \ + PYTHONPATH=/etc/superset:/home/superset:$PYTHONPATH \ + SUPERSET_REPO=apache/superset \ + SUPERSET_VERSION=${SUPERSET_VERSION} \ + SUPERSET_PATCHUP_VERSION=${SUPERSET_PATCHUP_VERSION} \ + SUPERSET_PATCHUP_REPO=https://github.com/OpenLMIS/superset-patchup.git@${SUPERSET_PATCHUP_VERSION} \ + SUPERSET_HOME=/var/lib/superset \ + APP_DIR=${APP_DIR} \ + WORKER_CLASS=gevent +ENV GUNICORN_CMD_ARGS="--workers ${GUNICORN_WORKERS} -k ${WORKER_CLASS} --timeout ${GUNICORN_TIMEOUT} --bind ${GUNICORN_BIND} --limit-request-line ${GUNICORN_LIMIT_REQUEST_LINE} --limit-request-field_size ${GUNICORN_LIMIT_REQUEST_FIELD_SIZE}" + +COPY requirements.txt requirements.txt + +# Create superset user & install dependencies +RUN useradd -U -m superset && \ + mkdir /etc/superset && \ + mkdir ${SUPERSET_HOME} && \ + chown -R superset:superset /etc/superset && \ + chown -R superset:superset ${SUPERSET_HOME} && \ + apt-get update && \ + apt-get install -y \ + rsync \ + build-essential \ + curl \ + default-libmysqlclient-dev \ + freetds-bin \ + freetds-dev \ + postgresql-client \ + libssl-dev \ + libffi-dev \ + python-dev \ + libsasl2-dev \ + libldap2-dev && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* && \ + pip install --upgrade setuptools "pip<24.1" && \ + pip install --no-cache-dir \ + gunicorn==20.1.0 \ + gevent==22.10.2 \ + git+${SUPERSET_PATCHUP_REPO} \ + apache-superset==${SUPERSET_VERSION} \ + apache-superset[cors]==${SUPERSET_VERSION} \ + flask-mail==0.9.1 \ + flask-oauth==0.12 \ + flask_oauthlib==0.9.5 && \ + # MarkupSafe, fix missing 'soft_unicode' from 'markupsafe' + # fix cryptography, flask, werkzeug to last known working versions + pip install -I --no-cache-dir \ + MarkupSafe==2.0.1 \ + cryptography==38.0.2 \ + flask==1.1.4 \ + werkzeug==1.0.1 \ + psycopg2-binary==2.9.1 + +# Configure Filesystem +RUN find ${APP_DIR} \! -type l -print0 | xargs -0 chown superset:superset +COPY superset /usr/local/bin +VOLUME /etc/superset \ + /var/lib/superset +WORKDIR $APP_DIR + +# Node & npm +ENV NVM_DIR=/usr/local/nvm +ENV NODE_VERSION=v16.16.0 + +RUN curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh | bash +RUN . $NVM_DIR/nvm.sh \ + && nvm install $NODE_VERSION \ + && nvm alias default $NODE_VERSION \ + && nvm use default + +ENV NODE_PATH=$NVM_DIR/versions/node/$NODE_VERSION/lib/node_modules +ENV PATH=$NVM_DIR/versions/node/$NODE_VERSION/bin:$PATH + +RUN node -v +RUN npm -v + +# Installing dependecies via npm +RUN npm install -g po2json + +# Fetching dependecies and first build +RUN wget -P /tmp https://github.com/apache/superset/archive/${SUPERSET_VERSION}.zip \ + && unzip /tmp/${SUPERSET_VERSION}.zip -d /tmp \ + && rsync -a \ + --remove-source-files \ + --chown=superset:superset \ + /tmp/superset-${SUPERSET_VERSION}/superset-frontend $APP_DIR \ + && rm -r /tmp/${SUPERSET_VERSION}.zip + +USER superset:superset +RUN cd $APP_DIR/superset-frontend && npm ci + +# Deploy application +EXPOSE 8088 +HEALTHCHECK CMD ["curl", "-f", "http://localhost:8088/health"] +CMD ["gunicorn", "superset:app"] From 64120b4edc4ff72b08c458d8fb491015451f9d21 Mon Sep 17 00:00:00 2001 From: eLMIS-Lesotho Date: Thu, 27 Mar 2025 13:05:57 +0000 Subject: [PATCH 35/46] topics ui and kafka connect heap increase --- reporting/config/services/akhq/akhq.yml | 27 +++++++++++++++++++++++++ reporting/docker-compose.yml | 16 +++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 reporting/config/services/akhq/akhq.yml diff --git a/reporting/config/services/akhq/akhq.yml b/reporting/config/services/akhq/akhq.yml new file mode 100644 index 0000000..1feb80f --- /dev/null +++ b/reporting/config/services/akhq/akhq.yml @@ -0,0 +1,27 @@ +akhq: + connections: + kafka: + properties: + bootstrap.servers: "kafka:29092" + server: + access-log: false + security: + default-group: reader + groups: + admin: + roles: + - topic/read + - topic/insert + - topic/delete + - group/read + - group/delete + - group/offsets + - node/read + - node/config/update + # Authentication (optional, but recommended) + authentication: + basic: + - username: admin + password: admin + groups: + - admin diff --git a/reporting/docker-compose.yml b/reporting/docker-compose.yml index d7ca3e2..0e9098d 100644 --- a/reporting/docker-compose.yml +++ b/reporting/docker-compose.yml @@ -152,6 +152,7 @@ services: - CONFIG_STORAGE_TOPIC=my_connect_configs - OFFSET_STORAGE_TOPIC=my_connect_offsets - HOST_NAME=0.0.0.0 + - KAFKA_HEAP_OPTS=-Xms512M -Xmx4G logging: driver: syslog options: @@ -196,12 +197,27 @@ services: - KAFKA_REST_PROXY_URL=http://kafka-rest:8082 - PROXY=true - MAX_BYTES=500000 + - CADDY_AUTOHTTPS=off # Disable automatic HTTPS and DNS logging: driver: syslog options: syslog-address: "udp://127.0.0.1:${SYSLOG_UDP_PORT}" depends_on: [kafka-rest] + akhq: + image: tchiotludo/akhq:latest + ports: + - 8089:8080 + environment: + - AKHQ_CONFIGURATION=/app/config/application.yml + volumes: + - ./config/services/akhq/akhq.yml:/app/config/application.yml + logging: + driver: syslog + options: + syslog-address: "udp://127.0.0.1:${SYSLOG_UDP_PORT}" + depends_on: [kafka] + superset: build: context: ./superset From 6cf476cf9508d2a07708da2b5a46659137ceb195 Mon Sep 17 00:00:00 2001 From: Lebajoa Mphatsi Date: Wed, 23 Apr 2025 10:12:13 +0000 Subject: [PATCH 36/46] update stack to include an init cotinaner for the postres on host --- reporting/docker-compose.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/reporting/docker-compose.yml b/reporting/docker-compose.yml index 0e9098d..800a82a 100644 --- a/reporting/docker-compose.yml +++ b/reporting/docker-compose.yml @@ -95,6 +95,16 @@ services: - reporting-db depends_on: [log, db-service-configuration] + db-on-host-init: + image: postgres:14 + volumes: + - ./db-on-host-init:/db-on-host-init + env_file: settings.env + command: ["/bin/bash", "-c", "/db-on-host-init/reporting-db-on-host.sh"] + depends_on: [log] + restart: "no" + + cron-config: build: context: ./cron From 35882bc5827a099c05a93cdcbf9a11f0eca343e4 Mon Sep 17 00:00:00 2001 From: Lebajoa Mphatsi Date: Wed, 23 Apr 2025 10:17:18 +0000 Subject: [PATCH 37/46] configs for setting data and superset metadata outside docker --- ...kevents.json => sink-stockmanagement.json} | 6 +- ...vents.json => source-stockmanagement.json} | 6 +- .../dashboards/exported_dashboards.zip | Bin 0 -> 119996 bytes ...zip => openlmis_uat_dashboards.zip_backup} | Bin .../openlmis_uat_dashboards_db_on_host.zip | Bin 0 -> 33279 bytes .../superset/datasources/database.yaml | 3 +- .../datasources/exported_datasets.zip | Bin 0 -> 207981 bytes reporting/config/services/superset/init.sh | 9 +- .../services/superset/superset_config.py | 9 +- reporting/cron/periodic/15min/refresh-mv | 3 + .../db-on-host-init/reporting-db-on-host.sh | 30 + .../db-on-host-init/superset-db-on-host.sh | 39 + .../templates/OlmisCreateTableStatements.sql | 2619 +++++++++++++++++ .../templates/OlmisCreateTableStatements.sql | 313 +- 14 files changed, 3026 insertions(+), 11 deletions(-) rename reporting/config/services/connect/{sink-stockevents.json => sink-stockmanagement.json} (77%) rename reporting/config/services/connect/{source-stockevents.json => source-stockmanagement.json} (84%) create mode 100644 reporting/config/services/superset/dashboards/exported_dashboards.zip rename reporting/config/services/superset/dashboards/{openlmis_uat_dashboards.zip => openlmis_uat_dashboards.zip_backup} (100%) create mode 100644 reporting/config/services/superset/dashboards/openlmis_uat_dashboards_db_on_host.zip create mode 100644 reporting/config/services/superset/datasources/exported_datasets.zip create mode 100755 reporting/db-on-host-init/reporting-db-on-host.sh create mode 100755 reporting/db-on-host-init/superset-db-on-host.sh create mode 100644 reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql diff --git a/reporting/config/services/connect/sink-stockevents.json b/reporting/config/services/connect/sink-stockmanagement.json similarity index 77% rename from reporting/config/services/connect/sink-stockevents.json rename to reporting/config/services/connect/sink-stockmanagement.json index b2d0140..ca992e0 100644 --- a/reporting/config/services/connect/sink-stockevents.json +++ b/reporting/config/services/connect/sink-stockmanagement.json @@ -1,14 +1,14 @@ { - "name": "sink-stockevents", + "name": "sink-stockmanagement", "config": { "connector.class": "io.confluent.connect.jdbc.JdbcSinkConnector", "tasks.max": "1", - "topics": "public.kafka_stock_events,public.kafka_stock_event_line_items", + "topics": "public.kafka_stock_events,public.kafka_stock_event_line_items,public.kafka_stock_cards,public.kafka_stock_card_line_items,public.kafka_stock_card_line_item_reasons", "connection.url": "jdbc:postgresql://reporting-db:5432/open_lmis_reporting?stringtype=unspecified", "connection.user": "postgres", "connection.password": "p@ssw0rd", - "auto.create": "true", "auto.evolve": "true", + "auto.create": "true", "insert.mode": "upsert", "pk.fields": "id", "pk.mode": "record_key", diff --git a/reporting/config/services/connect/source-stockevents.json b/reporting/config/services/connect/source-stockmanagement.json similarity index 84% rename from reporting/config/services/connect/source-stockevents.json rename to reporting/config/services/connect/source-stockmanagement.json index adcdb0b..a33a732 100644 --- a/reporting/config/services/connect/source-stockevents.json +++ b/reporting/config/services/connect/source-stockmanagement.json @@ -1,5 +1,5 @@ { - "name": "source-stockevents", + "name": "source-stockmanagement", "config": { "connector.class": "io.debezium.connector.postgresql.PostgresConnector", "tasks.max": "1", @@ -10,10 +10,10 @@ "database.password": "p@ssw0rd", "database.dbname": "open_lmis", "database.server.name": "openlmis", - "table.whitelist": "stockmanagement\\.stock_events,stockmanagement\\.stock_event_line_items", + "table.whitelist": "stockmanagement\\.stock_events,stockmanagement\\.stock_event_line_items,stockmanagement\\.stock_cards,stockmanagement\\.stock_card_line_items,stockmanagement\\.stock_card_line_item_reasons", "database.history.kafka.bootstrap.servers": "kafka:29092", "database.history.kafka.topic": "openlmis", - "slot.name": "dbz_stockevents", + "slot.name": "dbz_stockmanagement", "heartbeat.interval.ms": 3000, "heartbeat.action.query": "CREATE TABLE IF NOT EXISTS debezium_heartbeat (id SERIAL PRIMARY KEY, ts TIMESTAMPTZ); INSERT INTO debezium_heartbeat (id, ts) VALUES (1, NOW()) ON CONFLICT(id) DO UPDATE SET ts=NOW();", "transforms": "unwrap,route", diff --git a/reporting/config/services/superset/dashboards/exported_dashboards.zip b/reporting/config/services/superset/dashboards/exported_dashboards.zip new file mode 100644 index 0000000000000000000000000000000000000000..cf1d4f1fdda79256359ba1820d33810487ef65d0 GIT binary patch literal 119996 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

kG%H>em$5ei&h{c9IL;hYad}O@NXP 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 literal 0 HcmV?d00001 diff --git a/reporting/config/services/superset/datasources/database.yaml b/reporting/config/services/superset/datasources/database.yaml index 7175fb7..8b0fb26 100644 --- a/reporting/config/services/superset/datasources/database.yaml +++ b/reporting/config/services/superset/datasources/database.yaml @@ -2,7 +2,8 @@ databases: - database_name: main expose_in_sqllab: true extra: "{\r\n \"metadata_params\": {},\r\n \"engine_params\": {}\r\n}\r\n" - sqlalchemy_uri: postgresql+psycopg2://postgres:XXXXXXXXXX@db:5432/open_lmis_reporting + #sqlalchemy_uri: postgresql+psycopg2://postgres:XXXXXXXXXX@db:5432/open_lmis_reporting + sqlalchemy_uri: postgresql+psycopg2://postgres:XXXXXXXXXX@10.255.100.97:5433/open_lmis_reporting tables: - columns: - {avg: true, column_name: adjusted_consumption, sum: true, type: DOUBLE PRECISION} diff --git a/reporting/config/services/superset/datasources/exported_datasets.zip b/reporting/config/services/superset/datasources/exported_datasets.zip new file mode 100644 index 0000000000000000000000000000000000000000..cc58bbdedb4e5bfe6d588cdfd1b7f05793c77fe9 GIT binary patch literal 207981 zcmeHweURiF0$-R3uh8wb^5n_@-x?W zI%J&P8LSX$_wW!|qrlaiVIfm*^hC}37*i|2}ybX2iSUPrn;5`DPIzmH$d8Z#N zkZD!J^MP=AhwSfSz!CPk0q01`2{!y@FLyC1*RMPHGGe{JsEim-7Xvq&32==du;Xm z%G%AZ_;L6md^$KO(bI6$9P9=sDfaB_i|uv|!L*$rie6f`8%LnqeE>NlYqYL<@NtM0 z?AK&C@<9#$8iINT=@+`kY}f1FeCpgO0$UPj|CMyRmg{eO^fLVqknUNR;0=1t1jFwF zWLD0fUOKyc4F0|D*psogIsHD^9kM6WkK!1I3uR$D-t;zXICo^jx51m~CNNvGJv5*b zwcC(x4J>c7yE?`WycMH+bhGr%tu4K7tpVG1#<7iw?!m?xrzuivAaGH0_yY1*8l8qwJ|JVj>@5(xN@I3(b@kNJkrkL{ugSRs zlB(qkT7lpUGRg)TRe)jB$VTZsYY4>v*gv0h2kJP~s2%>|DX#w=KOhp*TB~cwg--dMtNDGxFG#Jj!w6Waq8+Q#R&+K%_cwMnW0|t!H#&Q9f7Z7aa#fjxw#P|V9cW9eHpv$k1hIr)7 zT3U+zENyS`b>vwMxz&aij?+YB^jM~+aU3J|%|0WWxX0)i_gGES_@07kFtnJV&Q8oE zR(ToW{Q2eMaPRh71d0wjJs6Dx8G#;zT8v4GY)^UfE+~JJ)Iaut`%b?9nofs|JIj-L zu)YB!Cl*`P1xxT~y$GS7{n36P^H?+#4fVi)F3Xq^F{d#KG7%%Nh9dI)p2V>N4P-th z3AFg)rA+DcV6t`jf(v_~Isp}gKR0&nx%wN*V?Y+kI0l~(V!Mq4H2gXt1%nUG0F4D? zf;>7HdtyHjJytMHSy%cDZ}$3(C}NOpks>kuo++|@T@*}Sqk-rR(P#qltv7M7W1Ida zlT%Roay89}NsZXP4rYhayR%nDy~?G+^>2UUvyXuCPsXl;ECNk_i?FHsfGGYZ2`>P_ zaA!)G{=t4AjGEl%G3I3kNg7ClnuHmd>l;j86cnK+>HWSa(LflJXQ2Wys$mo{iJ!aa zQb_fu?5u+AYlzjR#h6r1{J(qgs{7soGA|h?=97Ef$#^{2BnEMB1fhU4VRZqENIkV5 z$h@ratSF11>i0|_^OD9hie?H7k|j;j6a^`LD#(0`?^8f|1N~zKrN=Sk_S+4DDj(|AQvr{d`q;i4wOK)F(UiW)C9Ww5%n$WAZ zV?f9`1o0OWbK@jG_S^kH{4uL&dXI;g21PgEe++|BOpa${4SZdi$oE9PRm4A)F z9QvuXB>xjqXM26IC-#)SxDdsZO(RVGWhzbXL}t&>v>wwJq#g-8)EQIK6$VRv8H1Gy z_cXapdRdCA`K;L^>5uBbb|Ji3g{%ZMenxVV&k8W;?WV1bP5TI4oS=~hNsoE_tN`%Q z6tw~B%M;tZqC&uDOBLHqTQ5S|9hwF@XI;peT>hFk2`4IsnE1(~ii z6>ZMFi-qk_h1ZmxP{s*Di#%m)mevI7{&8kIyiG>03<+>|3OgC5DoD<2oLD?@FF~mdctmBcsNh5N>iBva0_JuGTHQ|2AF}qLA zkGrh-wr59_N~DIi0a-hdr;L`Zisd7##DL?cXI3HOr&AgB>@dbLtiXuV7`!5vNonQ{ zB?y;JES-gUriT10w^^0@&^w=>{Oh9&oemlE=4<#ug9=rdU9_Gb$d-I6$FB~qZR%(oIcaN zYkBSVF8OEo?$wp0_@xWEdt9IlKdRL|81XgA(G09tkbCP@Y3ylA<=_B2({kuUuOc-r zv?MfT(*mq?s0q4GQLKG%6l;*Q_J}^lkZS|7l$*tocxWHVzA`Ilg48!5DpZi59Hq=NT2B+19u%B4 z3|Zjh9(y4BhNL=Z`EeloPW9?NVqtWOPqYadG5022*_Q_rRJPQSHYHvu>QK4O?cEQn zU%C7HIvp~O&nI=n&Pblj5r1SKNgO?sHQByqFtQ};q%x`rl~Hx56aZzg^}b<3K1%D8 zQG&cs+Z@^>Zv!HMgE-(k%XWcGj_e7m%Pz>us0puC38=#vY6H_xKgoVIg`mz(JjtH0 zUL){v%wI}rEchp{PrviuG@uy<(34cf_2 z`Py@ta*lP zfRfzfnO=`nebb@%r-q@v42Kca0#%HjtZ{AfA7&ZCB6?6M)@?g<`{C2 zqi@ucZfa2E)Zi>xmMcj=e6JZIz*X~ zacVxfG3W;asodUA9Bzbl?QY$_A;Jy*346MtX ziC9Kq1xQ*IL}1c{0YacW>gl)zvUs!u{n>3}L-lR7R{nNZk}5DxmB z=+wEKTP#RaZp<*QfA^Cgga)5v+&(AgC7rW$43YkROja)6O+A+8y78I=l$kMjPT-IN zf$Ui9^NffvY4iy#?*xGr4IR?yxIPu#?&DiXul5^asg)0}+^aO0RKVx~J-h)ZANg}* zXX*DpefMjh1zHGaD_N~k=DmuPO5qZt8Kte84MC}r^3HpO zOXV{By?=V$k9`>|XJpK2J&Q4-(D!?26AcI8Djft}R~EZLjf~j`)BFmLf>fy3v0Lo= zt$o_FZGh=nJ;+bezqpH!!p0QputdVA4)+ZV^pnvelg)Tp3cOz%UdeImo{eojp zMh4{CdK(UQH|#-fo_B#Gl{sI%D1@3`kol0OdlvvbF;Bf9kFcjhDH=TTO2E4{No~OT z1zmEeL?OttlT>nMtQn1LDp3ZF>>2AtBZo?qKqF_ydeO+F6D5$yoUdLK67-@3=!tpi z1$m2Plz?|@lInoXGwH`g){?e`CYK4_on%)y_IJB>;d_)SJdT^r8n+J{U$6#|LVFUCyzgmn* zNDQh=et+Ayjy~gBh%+JMC+71L4QwyZLqxtx`-c3CTI7Kl+s~?vi4Zh|l|cNrj!{o% zOddgvb3xJ>Md4XS#)5$~voDCU*e3T-p1Fj&yrm%;O(55YIJ^9FS<(j7nD`P%U*EcH zbRgw9$iNnOCtFO&Q!$;buIU z=D%U+rmH*Fm~delxGt-43MXlp(IM*@60)F$uO?%j;dNc-SyN&;or*+gzi|vOsPZb) z>guVbBP%e^UfVjO6H5;Tqz98QzKl93`Wi|vYnvR@{2Uz!7-LW=B7XpkRcx%Hh&t4mH;$pTHT4+PCO<+TU074Q9 zNf%zNCwZ0ICx7p^FEO8i+-)+J=hL42vb!oW?w`0G1kmOhD`%eD51NzI>&cLpjhQ}= zpoEkNN)wcjQovr>(^+ivHCe%JvR7usfnS`U5tKO06(kBo>_$^m7l)Pt)Q^9SR>>o3 z2|%+0UZW|h12oS9>|o^DBYKpbSX0)4H9D{_;>B(lxfe7)Z%A>I=z!s|enpsAV?+2dAiMrf31c@;qiH;FjmS;2rBc=~&VMq}A zV2L!^#540Kyycn>25a+W52`QP4^U-x+^`G-m)9Pe69q*vJElj*pa+xSxapALyKXzV zl67Hb+z^9FfE7))RGN&AG?=q0H;_EN`m0ZT7>a$8arbs(JyR)~T(Ep=D24FA`%1dU8QLJgIh3;?&2dsTbftoWn*%x782d zOw3j<_;A0%&Iz{Z-`Nr`eP*tDK@SM>Xai*G5sHANgC3Yh{RlR^0wOy1zsOS>)BARE*D1lhs zl=ULwpYXIfqXZV2^VN$&+NMzg`t(%w0`9-7v^k>$^qKQr0Tcq+cNgZ7ZHR4ru6jYA zwP=)h^|O=J3w)M6qXhU_)76VW+NMzg`1Dlu0-kNnC;@)#?xUpI41vg~b1GA~jy)IofL$~Nd zae5Q!L(RcM5J3PIM5OpNKud4p7Ost`(eGZc#^d}5j>22m zoGi|fJt8e5+{t)6D4$i=9K4F!xJsYUqfIH>pREMZgI`Rbqz9>Oj%ka-%v16I)00UL zN`&c!xr+24MGq$VPR-J!V-Ksiq^?yCk`!T>?+2v_NnhjHjnlQTl-Zo4ns%X5w0(I# zM~YDNPiRpGXA4hf+GR=G))n=Sws7nGqZxO2QnYuo4pEAb%W{`egkIXAN)u|cfzcFU z7xS`Fgw>3VmLlAI7SLvVDo@IPY{b0d^-#W=j63H`%8yF#gjKAup~kW*@{;qn|IvO( z((fZ#S9nfhBwZ93(KHbw%VLk|86syOQV@-9*vG_=au+3j!o%H$(ZM2n2O z=3{5~Bj_sZM4i9J$U#7ou%p_3PH!GR+u>LHfuU)JX6V?I7{TCpMg%^_APl9qBn}el z4cR~}*Q#y}c_cGD2&L~`!{QZkPB1-=o)%+LIi2i$ypv~cRgeDT<3SI_rw0=V2iNh2Jut9STES`vy`K`H@-fqUg;C{H{};UbGH)o{s6#Z)r+I_PaOC=r z><3C`@EF{weT^|>AaWuHL{5Qjhm0aiNJP5XlVrV3lrAM<`&G{k%LNH()6bH~laCUT zc6Ocf_%yY-Onrb|d*YNTI2}w-8<4(VH~u;bz13_-ZhV@00p7)1%?7pVx03FY0!@4cD^aRMd!~i9|u8tuVW?;qUu z^Ew?e=1f6}Dt)PhbE;8(IHaH1#zfEU@4myY(vtlEx!z<9feYGfM?8BFZe@iR9(Ng^jx)p?)zqE&Ss(ap6&IVnX6vV6X&x8 z*ok@S1v%qnmH<3swtB&z@0{lxmio?lzQJaTCGX_3^c+8;3M9ttT!-b;OXt>(oIcaN zYkBSV?i&2Fd-v+f671$)3ym|kFzaR+v{hr>HtYw8kr&fu z)G&D2(4`*Ba6HN8=xLCd*E0o16GhgPP)|2GDw4`=NZA|=Ef?uJc3lh1K;=2q>guVb zBaILX1G^SQ7*Qu2#VANg9qjm#wI=9W>-GTZTKOcA4upJzFX0=duhDDIt*#t}S@v2b z2AT(XjKq`NVo4%Zg<>5OA#c24=?~f?iiB6XL~(k~TP5)`dlg%lx;rs{TxlBD5R`P= zC@;u02AAOwk33qoW?(}$lVy)e<8yxHb*o8` zZ(59`ABIkkgwbM5DkqN*z4!2Ye-27^kx>VE+`iiGw&XFY&q@r6s^NJoN! z=_f4?bi#d6>GN1*`XbgDQ8o=mRy54?;BN}lmlv_rs*(qJj`3hisZ1shP^(0FIUxV> zdUC!v1a;%Et2>royF0w`-R^kP+pvK)fXPMOrm9>@DLWwlTS2ZbdHRQ+dVHbNA>*}W zrQ;pqTDORO^x%Gwd^KG+^?n~SeF;O78p%}C1Vv{!gBAHcGI&95mAfaanoZoh9Yr~Z zdt4|)p+Jmi(Isk&OY zR)<8M*i`g&=Al+NXzQS{{Z`+YT4j)JcC^LfNlMU~UKe^$ie=~1gH($#soWm&x34+< z{hxnMr$ff8=M#uY5^T-fXqYkMep~Y_uYy>#hT%mLI7xg)X%Vntcu^OYh6V!&Y_&bh zq^WoT@L|ies4DtLUv%1rJ%ns<-5OXPMe%7ZmR?i(63OJR#DUO|@ERiQ(u9v`IW6Cy z%GITN2mB{L29Y&n+&iDTL~6qfHc1f)Zv=%Skn7sI6*L10iB*37NTJ{E2X(6_YjU6O znT+1!AijpzWJYFL42=u>7z;ASJw!z*ykNt_vl2Th^e}+~2Hl|U-IdeJvB6w>EA(n5 z<9I20NxHn}S(!qz=s&U_s2?kFoWMe3*1o1d$7(^=7+Hs4Lrv3_9?yzM>nk)En}U6X z_E_{03F=abVXv@r$#S7c;Rr(J>0waxU=j-{Ds48~E0wEq4t32dAAHHLU)$-Bu{<}2 zijKZY&i*!RD*4=g;8nOMVABvWBZ$z`3i?^~fmaz4!waH-4F&S>kwF8mqR)IrA=3?X zZh9y>D29;FMGrpoU_z4}kOsLSWqNLPR%hih&>w94%o`sDizXR!27x&CAXo13zuOP8 z4yTB`VCWpvH+zuQB1#B)bI3e&>41tuhNL4wq(as?Ak$$LOHxx|vAb8gP>QC}K&C4v zZNKz4zkA=8zNgb6#5qb}qGGYvk?=hmJK!!7K zhzyT=x`0Hf-xo|8lsj_IOPfrhNF#wG5%haqy5|8<9X4!mS<^$z4{WMo+ktKu!WA>i zD>tP72BDSooI4WtK@T=9#-wuT;h*1p2Y(tW1(5NYIi&|{eIs9H@W0#-l7c2lQ0-If zL)lM_g({#ujRDgy%jia5HcdfNOogLCQfTq*NvRVD7OZR*+7NB85|MPTaH*V7_kQPv zpLi!&ugI9wi{}tptRl~Y22tDUdFki&1NoLTMVEC+VC0@8gBMSL`pvq)GKf_a*^o_5 z(y55I+uDA52)Tc-=|QK(m{d;o*hjCu`7ngmkx`E9?QGr($llg-`+@9nh?Cf!jv1ZR zNT40dGf0Cr0=yxEZA;g9Xu?f{)JmbNpSD&V2S$d)Zo=!*6jK(Vi>=m`D(6a@_q^vW z9yk&ia~9c()tyj%OL?x>1z((4u0_fKhY#)tzSM&r?vh~ej9>^Lj_U}6U~cxA9+sf6 zBGwgF>(juO4uXmsZ^~~q=}VcsXz-w?P&x>7iuXAG==ha~WQYVHI0L`skX01%>Ao7IQoeyvwj0=m>9M z(!|K2srH58*kju?4|K-H^Jbt-elNPwdPJLAY&=UbwwQ+CD+u&p)?!R5S0%rC`rZHb zUhwIVaYt1Qdx(cxe#yGn!3s1=HGF$xgdb25Wj4TL{yrr3%rkFgE zxC4V{Gwo1Y(&Yg?+^T9DL}>4ot*~->c<)!f`PL_{>vYJtbv}9+sw;U0wdm{pKhUatpGU$n3 zIM5;pSXsPp$dNlw+?+jObxmWkkQh6bjzNkTBmF`icP#*Ve42UzHY|5MK%4%%DgkhO znwkN&UF`aA*t522Azk1l8_Ah!1KaQYlzkWrz47c$PuUaJi$wfbmH<6IO}zlek7Wsf zxc@*v*`BOP43G?{$_kb^67YB3OE} znxnsG6UFifE}vLlS!=*4oZ=T!Bps|y>rm*D9%XTgAw4LCx(z)D(Su2xv6rS2Kq8h- zES+rtnMg^tZN{u}V@3Ds?|s7;AnJ~c)AKp5rZh^m3{v$F`tRiSoy&Z7KbR~yQP&ln zbf3crD)N|;#3+3Ola6$(fE7{4Qmdltvb6o_=C;&i(N4`YPXW1v29xTFBQ?akcmbD> z*ld`?ba%I=u*%rH7RvOMQysv0VnVoDC0^-eS1Zug?_81J@Q!*q`)O)R}2cBX^;Qv>*cDAL;xQ|58N24kXGG?oezh>>h#R&p? zn3t=v7LZ<|DQW|>okow;+xC&j*xo$8l6djiQdq`p_3lBm( z4@#gi*z7|ma)Z?vjn#S#D@z85|4@%f(AreaA|>(o;&HI?d1O2x57LuKKNu+Bt~$<<;SoH8CkCgIRFK(!)y#Ppy=4<_5+`oc2v9tIIr z&h?-9oKJu2P1koiWX##}Y8{i3R^tszS0A)TxsCnOkL?G3&x(r1avZe0^2=9ok^%uX zJ%d5KB(jnu@mOoUO%L3nK@I$|J)R7<%{DH;{4-{&O%m|M6#}t0F>-@a3cwsqQ8S?S zXuUL~(Hymb>PuG{n^;I5%oepWW~&$cXg^E998FO(pwV`gfi#+QPFEq6d=J^)%I&kyU1A170?6u{+{KC4<0m~mPc1KVV4c=s|<-+u{@g|hH z8>!%q#HwjKL*z}r@|BnHUo^4&%K4*o=bxNXukCcmxNSbyZ{(*^iSHwiIVK<451dic zcv;Yq&Txtdy?1!{o1*o4jMnE^LljI=!v`r5i(OI*Djs@W9ka>ejq#Q8=yL()7@wv( zfb+N`b;V8SVfXQakawOA`2A$8ZBD!LnbyF=4vGAqeiSbaS2@3rNe-oVs3DqutF0E5 zi!jckH~jg#Aaae2IrCGd(uoa$Daf;P>qX2yxF19rQ&%)u<2a@-Kvs$<2pXf9xBRV5ZiuVb`eURkPNaT*y7VvCmB#9${cY1K$x4dzM${Kfu>)FrhmZsDX(n(L*M!9 z>ppsQr$ff^`HUBYUgMEV#L+y{Mf{Qd!0nM@LJBH0I)M(0z~k9IbURU?uC>k~R^nwa zXXtX9O!|3hH9fqDFp<{kHkIoG>dS9>srpPXX~I}3O%+t+d5)z`ydv}=_V9k76r3sc zI9&q2A1eT*5JU`=g3}op2?o}nE1aouG*F5d2cU;&(1S@emE{^@+o__W++LwlIVu0( z%bzQrgUAgsPRvKjr;Zt-!OVe?<0eiHD08F zc5^~ruoM?+iuHa_vRUmTZd`qz3 z^VCm+`a#B=hKV4R9wNN+GwI`xq&XH;wI`{z*dWm{3o%Pr7!+e*6HNw?;y*IRwl#_( zXKd4i#Q()Ey8w|38(8(MA+{&cx1>A#5G{7c6K!DW3+}~1I2)X0G<3s%0R4fUMG%vd z_%v|)sV*L1oh(Z%F2-WlgnpcO!NibAruuT6tBxIP5NZwnLEjk|kL8(6Ecg*t^q#Ch zjY&!Gn~ca|g+Y=jFq|eJz9$J#FpH&ubtN~k+l6zgLEo6LVp`}aow}Dj$FHvp|IV~A zuPvWmI=6P@^qKBm%WJoH$v?YyudXZsuejo2zNwI`$g9?35|YB`ps9p)Y0JMKI60`H z?w3FK&R=-d3pyP#eqy&;NqE+?N(pa$m8RzF_5j{z9i-eRA~S>>Xo7=>HqdZFZhI2^ zn-qP~pejsAd`RgR_|zKu6=DdRSnP7WzSt9cN?%;4p9dIoC&Q{e?9@_I!)ASAT$D3xQY=U3KlenoX2-aI39W~j$8+fQ1aQvQI6!fxb{{)S1u zU`_**s_w!0NpiH~TVJ#lw@giC1uC49%i^kr=M5vFr#1T(1;zMRe`eEG!;qO4Bbezw zSqq#ht>3gv3X+x`TSh^qC$a*Gv0=nIF4FyaU7)I$#xSD`Wkhu15y8=CabYVw`LjlhZg;*EpwRk26;7^)yopN{O6#^^ zKL9DsV8BXCifLlFgYDz^k+p=KBRXYQ{N%+HIa*MvfCZ;51ADeTF%q-mk&A%LQ@YYl z*y#Bye&&d8bDLJ|g$3NkAXIH-8rbNm{qxH0QROa&k6JJN!JmI|r$ff-ZjDk7CT1LK zw5~$-16I+<@GbuX3u^k!hDC1sblP+k+OWyoLD5g*a;Vpmb<~8J;M$~p@y9ye1n|Sv!Z`y50w+2BYown^!j=|z=WXEV5K2ts& zIK80-!Mu8K;`9-71svlkIlLm0;Vk8ku$#G2^PIp&koc+THuGYIAhz}p60srMO;P}} zP>^Vp=n)-2s*sBK6jeyYw3r!c(bB^gM$u$%6NrFNd!B^47N!=BAb=rKGkerld}^@& zT>&M_KME-|ixA$KZQ1SBNn6SG2$$&UR4WywFVG%IMGn3Qfl2dhRfs^qVjF@@AhANj zTQpk7el@!tF$)hB5WXnXvyq5R#>85N4G8J=X*Chf9dAyDP7^T%yi$gl$heudM1{~# zSyelBPbC>CPsJ-)9gJ*`#w=Bb)| zen&aa_Mi*$z=p(2P|e^_A>;mu>yhxA+|axNMFqE;wz?FQEQE&Wa_Dk)Roe{uJLfTn z0*GWyS1$rO8t9}oAqf0_Q+9QyX7dHG$epuZRDx6BU>%cOuqqJAnej@X5m4JI(U_U> zN}%C021t+w4SBQt4NuHgFZg3fz=8~I>_C>bexbybPEJ%W&;c8UmJeVm%QgQMxWI{d z>IIp+U03yN)!nehWnOc9o_aynAQWKaM`vkhfJS<*O}xOHo|&s&&~;$FB&@7F;HjDF z1sj2AjKKCA6G2NHm5kYH27e3|jEYziO3zg<=zg7wlC*m|N4=m1q9kz4icX0ynI@;J z7lA<9&~0}%@i$aR3lNm8N$LgGm=IS?DV`opQ7@pNZcZ@Ne5ynsoSm#*;42f!lM~eo zG~`og5EyR++7JkTC1~}`T=jyEhI$EL;n($oR}E#0%~BhqR9v1@me9+1rsb2Izvrs(U063@{A^&WKSA!;6A2_2aAO zkDglUo;ka8Z28>s>IxjdXbmQbZy2o*zuj! z@L;pghR*r!9TPP2c1{p(`aYYJ!GHws8xyG6of&#|8$8iH6FkvfnXk@e0}gbSd^B^e zgZqn(?y;>evkBU0R5bqoxx1zLV%BWj#k)-@u;a~6n(SF_iW#WZ7J`#J%)g#|detMgYM zZUD9<@P4pKB32^2XO8c==#g%O^KK4sSS_^ys746-ZMbq|iD*;T<}1D204(8lrV;`( zr6#zi;bX-rH6`T4=Im(<+p5boH56kondD{uoW&Qr=c3{>h52c7HZ#jwaOCu{-B6nb zYN|UXY%N6@U2|++Mor^ARmg*E7TB?56}&(n-~HKaNGXG?FoGEz!w9m%_pX6QH8;?J zMr0$!gydWmhzn@*z6PlJCbOL~1=@^eGBkk3Gn??r(}-Y(FPTa-(_Uv9k(_nz(THWt z`$H#se$$j}b-7rb*oG=n+n3-gSTDArxJ^wRnYuq6W}KPZ)^4=GlHr$%$lNio))c5T z*wDimVm7m1tww6QT9<_?88>vZrj%o9{mwM=?^fAuj*$sYW6C}XHj9EK;dY9XXbzUM z+c;nSqo$uy5}yKLJbp9Mq{*qhCv>`ZK5|s;o97?{I{HFek~vEvHS|0NW>1ya=Be7R z7_pqXS17w}St!!Fhhh68s?Zx)^j$kYp2RQyMb%bK85?~h#8=%YLuW5u|FXAd(R~mn_$IHq{Yb7J={- zQng)WN(ncV3;P0-w;6WC$cxh3tzoJ;AzqQ_RJx-}V*i?h-&EkuiDGN-&Jn{@q9SxU zKrfA2xI+*P6t#Cr>{(;F*~hFmp>bpW_9xB6Ff=XB*|MgYIF;>y(oC#Kyo=>pqOmmuVG^8NYOz7Dlf&j~Fhm ztSy~bIvd}i@PNiQDEz3_{fR0-P=wMwC#=x~s%uOkO4R!)XPHz?m^WX3rcMT@b>~zR@U1I)!dw!w$;l+md zT|njqMBETl`VSt?u}@);>ssq0zx_fc6pwx2zLW32rqdzg&IA-+`+?F>fJij7003Y} zuIE6KU-p~6=jv}LkHPw69817()sM^t!Y6tZ``5q7c{z4$fj{#lDg_|ubU{2=+3(_x>)xS%{l%RQ z8Lv)&f){rQN>Dc;|AKWsKl#^37djm>l6(F5PwjH4@U{CHcIptjE>uj<#_vOiKK7{> zT-)i8aa#hv_Zwvv4LWpYuleqW)vw(BeVq;&#}jM*$Z)PTWArS0#W&a=c=cag*XfW! z_)qlb;j8}F!(~@gBMO?mH2Zn&x4sT9myA=VzxG*xDU9TM{^oz3Yt@*-9z~R7uYB*HUiV{PhIdRx@;(2z_m^EcFcoC4%YXSd zKliy8bvk4mO}yu?{mxwLPM!JL>%Hw;N1t&m;7G<#B-XqB$+^}W*k1NZ&;RTf#iyRr z>5y?UvC{Z|&b3l>MG4=7x;tJT=li|izQlY64iFj3iG`o|`?(el zq=}3(iDf_kkCiM75+8E&WQ?a*fAxtE!(Aog?!@YU^{u&9kCS7NoAD*fp15(LGke$X zz5l7<&jJi&B!#>0{J;6&u%RiPW36xG9J5Owd+3fI|H)2=4AK)X`t$HrU;dZ*7L3W& zQ@ctwvX70Jcf1}>85v1w`l52xJR;8YJ!h3?%7)?gH+^OD5qQ~T+!Y}Y|9$wXTW_5U zhWn8V1S`61)4QhHey6}}AUfZ;{H&M3OCTf3EjE@)0I>sCeh2fBLl1&@OU4@#sQ=%S zRjj&2XjB7_!O{zTP(hWRz#POHSj~w zVsr;$7JN7|U-+*ae&W8jKd;jvBYE!s(3uN_sXNAxgPykK0SST>gUfyoe|FdXf4mL` z3o?@2`w!nz28g^>m?!CcJKf8HK)J-^B~)hkyI)_@qxM4B`)gh zV|MR=|K!JD%gIQp(*N|axt7Y;<)gYxc_(meFxTD+RGy6Ei4*vPpDY6k;k4PvU-QZb zU-Ijqcaf14=l=U2%)4SDawB`$KiK%0H$DvSn~dc4e)`F|mUTdo$zJN0{^ob@`x40G zWF)!gqklE`Qp7;Btts%I-+Tvu8g4HcuSx9W7r!{)s`)}%?{{AKiFd+_BqOF-qwUw-q0AbgUMeCZ#3c}WQL4QpUHIOvI<_EO0&e&p##;DwWMGJ*Mj;pbWx zUNtn(4lN8r8>$~B#(H<;#>=_V{*#I8C6%AZ=MZ%ID zOPhE(_x6J?f3A2A4igzC5^Ep6FxT4PuqWX?5C%!qw(M6d*zbAjrvX_qlCSvxT%2ps z;JU*3&AY9aKlsjHc-0F!9Ws(u*8lOqE>{eXZXO~0sP)nx{CRL|k+GV1p?AM+mn%7# zcBo1uc<@TY=rgi(tk6x!0iFqG T2G}h8-`l$21$s7In9lzPFOOq| literal 0 HcmV?d00001 diff --git a/reporting/config/services/superset/init.sh b/reporting/config/services/superset/init.sh index 5097ec8..5f4358e 100755 --- a/reporting/config/services/superset/init.sh +++ b/reporting/config/services/superset/init.sh @@ -16,7 +16,8 @@ cp -rf $CONFIG_DIR/app-customizations/$SUPERSET_VERSION/* $APP_DIR && $APP_DIR/superset-frontend/js_build.sh && # wait for postgres -until PGPASSWORD=$POSTGRES_PASSWORD psql -h "db" -p "5432" -U "$POSTGRES_USER" -d "open_lmis_reporting" -c '\q'; do +#until PGPASSWORD=$POSTGRES_PASSWORD psql -h "db" -p "5432" -U "$POSTGRES_USER" -d "superset" -c '\q'; do +until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" -d "open_lmis_reporting" -c '\q'; do >&2 echo "Postgres is unavailable - sleeping" sleep 5 done @@ -28,7 +29,11 @@ flask fab create-admin --username ${SUPERSET_ADMIN_USERNAME} --firstname Admin - superset db upgrade && superset import_datasources -p $CONFIG_DIR/datasources/database.yaml && -superset import_dashboards -u ${SUPERSET_ADMIN_USERNAME} -p $CONFIG_DIR/dashboards/openlmis_uat_dashboards.zip && +#superset import-datasources -p $CONFIG_DIR/datasources/exported_datasets.zip && +#superset import_dashboards -u ${SUPERSET_ADMIN_USERNAME} -p $CONFIG_DIR/dashboards/openlmis_uat_dashboards.zip && +superset import_dashboards -u ${SUPERSET_ADMIN_USERNAME} -p $CONFIG_DIR/dashboards/openlmis_uat_dashboards_db_on_host.zip && +#superset import_dashboards -u ${SUPERSET_ADMIN_USERNAME} -p $CONFIG_DIR/dashboards/exported_dashboards.zip && +#superset import_dashboards -u ${SUPERSET_ADMIN_USERNAME} -p $CONFIG_DIR/dashboards/elmis_superset_dahboards_04042025_1502.zip && superset init && gunicorn $GUNICORN_CMD_ARGS "superset.app:create_app()" diff --git a/reporting/config/services/superset/superset_config.py b/reporting/config/services/superset/superset_config.py index 4157326..d96e50e 100644 --- a/reporting/config/services/superset/superset_config.py +++ b/reporting/config/services/superset/superset_config.py @@ -6,6 +6,8 @@ from flask_appbuilder.security.manager import AUTH_OAUTH from superset_patchup.oauth import CustomSecurityManager +import logging +logging.warning("Sanity check: Using custom superset_config.py") def stringToBase64(s): return base64.b64encode(s.encode('utf-8')).decode('utf-8') @@ -15,7 +17,12 @@ def lookup_password(url): return os.environ['POSTGRES_PASSWORD'] -SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://{}:{}@db:5432/open_lmis_reporting'.format( +# SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://{}:{}@db:5432/open_lmis_reporting'.format( +# os.environ['POSTGRES_USER'], +# os.environ['POSTGRES_PASSWORD']) + + +SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://{}:{}@10.255.100.97:5433/open_lmis_reporting'.format( os.environ['POSTGRES_USER'], os.environ['POSTGRES_PASSWORD']) diff --git a/reporting/cron/periodic/15min/refresh-mv b/reporting/cron/periodic/15min/refresh-mv index 6660597..bb0b684 100755 --- a/reporting/cron/periodic/15min/refresh-mv +++ b/reporting/cron/periodic/15min/refresh-mv @@ -38,4 +38,7 @@ psql <<-EOSQL REFRESH MATERIALIZED VIEW adjustments; REFRESH MATERIALIZED VIEW stock_status_and_consumption; REFRESH MATERIALIZED VIEW facilities; + REFRESH MATERIALIZED VIEW expired_products; + REFRESH MATERIALIZED VIEW stock_card_summaries; + REFRESH MATERIALIZED VIEW stock_card_summaries_with_prices; EOSQL diff --git a/reporting/db-on-host-init/reporting-db-on-host.sh b/reporting/db-on-host-init/reporting-db-on-host.sh new file mode 100755 index 0000000..9f73ea1 --- /dev/null +++ b/reporting/db-on-host-init/reporting-db-on-host.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +# Reporting DB configuration +DB_HOST="${POSTGRES_HOST:-172.17.0.1}" +DB_PORT="${POSTGRES_PORT:-5433}" +DB_NAME="${POSTGRES_DB:-open_lmis_reporting}" +DB_USER="${POSTGRES_USER:-postgres}" +DB_PASS="${POSTGRES_PASSWORD:-postgres}" + +export PGPASSWORD="$DB_PASS" + +echo "🔄 Connecting to PostgreSQL at $DB_HOST:$DB_PORT as user $DB_USER" + +# Check if the reporting DB already exists +if psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -lqt | cut -d \| -f 1 | grep -qw "$DB_NAME"; then + echo "✅ Database '$DB_NAME' already exists, skipping creation." +else + echo "📦 Creating database '$DB_NAME'..." + psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" <<-EOSQL + CREATE DATABASE $DB_NAME; + GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER; +EOSQL +fi + +# Run schema setup +echo "📂 Applying schema from OlmisCreateTableStatements.sql..." +psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" < /db-on-host-init/templates/OlmisCreateTableStatements.sql + +echo "✅ Reporting DB initialized successfully" diff --git a/reporting/db-on-host-init/superset-db-on-host.sh b/reporting/db-on-host-init/superset-db-on-host.sh new file mode 100755 index 0000000..e7209ac --- /dev/null +++ b/reporting/db-on-host-init/superset-db-on-host.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Superset DB and user configuration +DB_HOST="${POSTGRES_HOST:-172.17.0.1}" +DB_PORT="${POSTGRES_PORT:-5433}" +DB_ADMIN_USER="${POSTGRES_USER:-postgres}" +DB_ADMIN_PASS="${POSTGRES_PASSWORD:-postgres}" + +SUPERSET_DB="${SUPERSET_POSTGRES_USER:-superset}" +SUPERSET_USER="${SUPERSET_POSTGRES_USER:-superset}" +SUPERSET_PASS="${SUPERSET_POSTGRES_PASSWORD:-superset_pass}" + +export PGPASSWORD="$DB_ADMIN_PASS" + +echo "🔍 Connecting to $DB_HOST:$DB_PORT as $DB_ADMIN_USER to create Superset DB/user..." + +# Check if user exists +if psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -tAc "SELECT 1 FROM pg_roles WHERE rolname='$SUPERSET_USER'" | grep -q 1; then + echo "✅ Superset user '$SUPERSET_USER' already exists, skipping creation." +else + echo "👤 Creating user '$SUPERSET_USER'..." + psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" <<-EOSQL + CREATE USER $SUPERSET_USER WITH PASSWORD '$SUPERSET_PASS'; +EOSQL +fi + +# Check if DB exists +if psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -lqt | cut -d \| -f 1 | grep -qw "$SUPERSET_DB"; then + echo "✅ Superset database '$SUPERSET_DB' already exists, skipping creation." +else + echo "📦 Creating Superset database '$SUPERSET_DB'..." + psql -v ON_ERROR_STOP=1 -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" <<-EOSQL + CREATE DATABASE $SUPERSET_DB; + GRANT ALL PRIVILEGES ON DATABASE $SUPERSET_DB TO $SUPERSET_USER; +EOSQL +fi + +echo "✅ Superset DB and user setup complete" diff --git a/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql b/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql new file mode 100644 index 0000000..cdde2c6 --- /dev/null +++ b/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql @@ -0,0 +1,2619 @@ +-- Olmis create table statements +-- Created by Craig Appl (cappl@ona.io) +-- Modified by A. Maritim (amaritim@ona.io) and J. Wambere (jwambere@ona.io) +-- Further modified by C. Ahn (chongsun.ahn@villagereach.org) +-- Further modified by Lesotho eLMIS team in April 2025 +-- Last Updated 19 May 2020 +-- + +-- +-- Name: postgis; Type: EXTENSION; Schema: -; Owner: +-- + +CREATE EXTENSION IF NOT EXISTS postgis WITH SCHEMA public; + + +-- +-- Name: EXTENSION postgis; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION postgis IS 'PostGIS geometry, geography, and raster spatial types and functions'; + +-- +-- Name: commodity_types; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_commodity_types ( + id uuid NOT NULL, + name character varying(255) NOT NULL, + classificationsystem character varying(255) NOT NULL, + classificationid character varying(255) NOT NULL, + parentid uuid +); + + +ALTER TABLE public.kafka_commodity_types OWNER TO postgres; + +-- +-- Name: dispensable_attributes; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_dispensable_attributes ( + dispensableid uuid NOT NULL, + key text NOT NULL, + value text NOT NULL +); + + +ALTER TABLE public.kafka_dispensable_attributes OWNER TO postgres; + +-- +-- Name: dispensables; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_dispensables ( + id uuid NOT NULL, + type text DEFAULT 'default'::text NOT NULL +); + + +ALTER TABLE public.kafka_dispensables OWNER TO postgres; + +-- +-- Name: facilities; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_facilities ( + id uuid NOT NULL, + active boolean NOT NULL, + code text NOT NULL, + comment text, + description text, + enabled boolean NOT NULL, + godowndate date, + golivedate date, + name text, + openlmisaccessible boolean, + geographiczoneid uuid NOT NULL, + operatedbyid uuid, + typeid uuid NOT NULL, + extradata jsonb, + location geometry +); + + +ALTER TABLE public.kafka_facilities OWNER TO postgres; + +-- +-- Name: facility_operators; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_facility_operators ( + id uuid NOT NULL, + code text NOT NULL, + description text, + displayorder integer, + name text +); + + +ALTER TABLE public.kafka_facility_operators OWNER TO postgres; + +-- +-- Name: facility_type_approved_products; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_facility_type_approved_products ( + id uuid NOT NULL, + versionnumber bigint NOT NULL, + orderableid uuid NOT NULL, + programid uuid NOT NULL, + facilitytypeid uuid NOT NULL, + maxperiodsofstock double precision NOT NULL, + minperiodsofstock double precision, + emergencyorderpoint double precision, + active boolean DEFAULT true NOT NULL, + lastupdated timestamp with time zone DEFAULT now() NOT NULL +); + + +ALTER TABLE public.kafka_facility_type_approved_products OWNER TO postgres; + +-- +-- Name: facility_types; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_facility_types ( + id uuid NOT NULL, + active boolean, + code text NOT NULL, + description text, + displayorder integer, + name text +); + + +ALTER TABLE public.kafka_facility_types OWNER TO postgres; + +-- +-- Name: geographic_levels; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_geographic_levels ( + id uuid NOT NULL, + code text NOT NULL, + levelnumber integer NOT NULL, + name text +); + + +ALTER TABLE public.kafka_geographic_levels OWNER TO postgres; + +-- +-- Name: geographic_zones; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_geographic_zones ( + id uuid NOT NULL, + catchmentpopulation integer, + code text NOT NULL, + latitude numeric(8,5), + longitude numeric(8,5), + name text, + levelid uuid NOT NULL, + parentid uuid, + boundary geometry, + extradata jsonb +); + + +ALTER TABLE public.kafka_geographic_zones OWNER TO postgres; + +-- +-- Name: ideal_stock_amounts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_ideal_stock_amounts ( + id uuid NOT NULL, + facilityid uuid NOT NULL, + processingperiodid uuid NOT NULL, + amount integer, + commoditytypeid uuid NOT NULL +); + + +ALTER TABLE public.kafka_ideal_stock_amounts OWNER TO postgres; + +-- +-- Name: lots; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_lots ( + id uuid NOT NULL, + lotcode text NOT NULL, + expirationdate date, + manufacturedate date, + tradeitemid uuid NOT NULL, + active boolean NOT NULL +); + + +ALTER TABLE public.kafka_lots OWNER TO postgres; + +-- +-- Name: orderable_children; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_orderable_children ( + id uuid NOT NULL, + parentid uuid NOT NULL, + parentversionnumber bigint NOT NULL, + orderableid uuid NOT NULL, + orderableversionnumber bigint NOT NULL, + quantity bigint NOT NULL +); + + +ALTER TABLE public.kafka_orderable_children OWNER TO postgres; + +-- +-- Name: orderable_display_categories; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_orderable_display_categories ( + id uuid NOT NULL, + code character varying(255), + displayname character varying(255), + displayorder integer NOT NULL +); + + +ALTER TABLE public.kafka_orderable_display_categories OWNER TO postgres; + +-- +-- Name: orderable_identifiers; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_orderable_identifiers ( + key character varying(255) NOT NULL, + value character varying(255) NOT NULL, + orderableid uuid NOT NULL, + orderableversionnumber bigint NOT NULL +); + + +ALTER TABLE public.kafka_orderable_identifiers OWNER TO postgres; + +-- +-- Name: orderables; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_orderables ( + id uuid NOT NULL, + fullproductname character varying(255), + packroundingthreshold bigint NOT NULL, + netcontent bigint NOT NULL, + code character varying(255), + roundtozero boolean NOT NULL, + description character varying(255), + extradata jsonb, + dispensableid uuid NOT NULL, + versionnumber bigint NOT NULL, + lastupdated timestamp with time zone DEFAULT now() NOT NULL, + minimumtemperaturevalue double precision, + minimumtemperaturecode character varying(30), + maximumtemperaturevalue double precision, + maximumtemperaturecode character varying(30), + inboxcubedimensionvalue double precision, + inboxcubedimensioncode character varying(30) +); + + +ALTER TABLE public.kafka_orderables OWNER TO postgres; + +-- +-- Name: processing_periods; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_processing_periods ( + id uuid NOT NULL, + description text, + enddate date NOT NULL, + name text NOT NULL, + startdate date NOT NULL, + processingscheduleid uuid NOT NULL, + extradata jsonb +); + + +ALTER TABLE public.kafka_processing_periods OWNER TO postgres; + +-- +-- Name: processing_schedules; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_processing_schedules ( + id uuid NOT NULL, + code text NOT NULL, + description text, + modifieddate timestamp with time zone, + name text NOT NULL +); + + +ALTER TABLE public.kafka_processing_schedules OWNER TO postgres; + +-- +-- Name: program_orderables; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_program_orderables ( + id uuid NOT NULL, + active boolean NOT NULL, + displayorder integer NOT NULL, + dosesperpatient integer, + fullsupply boolean NOT NULL, + priceperpack numeric(19,2), + orderabledisplaycategoryid uuid NOT NULL, + orderableid uuid NOT NULL, + programid uuid NOT NULL, + orderableversionnumber bigint NOT NULL +); + + +ALTER TABLE public.kafka_program_orderables OWNER TO postgres; + +-- +-- Name: programs; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_programs ( + id uuid NOT NULL, + active boolean, + code character varying(255), + description text, + name text, + periodsskippable boolean NOT NULL, + shownonfullsupplytab boolean, + enabledatephysicalstockcountcompleted boolean NOT NULL, + skipauthorization boolean DEFAULT false +); + + +ALTER TABLE public.kafka_programs OWNER TO postgres; + +-- +-- Name: requisition_group_members; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisition_group_members ( + requisitiongroupid uuid NOT NULL, + facilityid uuid NOT NULL +); + + +ALTER TABLE public.kafka_requisition_group_members OWNER TO postgres; + +-- +-- Name: requisition_group_program_schedules; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisition_group_program_schedules ( + id uuid NOT NULL, + directdelivery boolean NOT NULL, + dropofffacilityid uuid, + processingscheduleid uuid NOT NULL, + programid uuid NOT NULL, + requisitiongroupid uuid NOT NULL +); + + +ALTER TABLE public.kafka_requisition_group_program_schedules OWNER TO postgres; + +-- +-- Name: requisition_groups; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisition_groups ( + id uuid NOT NULL, + code text NOT NULL, + description text, + name text NOT NULL, + supervisorynodeid uuid NOT NULL +); + + +ALTER TABLE public.kafka_requisition_groups OWNER TO postgres; + +-- +-- Name: right_assignments; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_right_assignments ( + id uuid NOT NULL, + rightname text NOT NULL, + facilityid uuid, + programid uuid, + userid uuid NOT NULL +); + + +ALTER TABLE public.kafka_right_assignments OWNER TO postgres; + +-- +-- Name: right_attachments; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_right_attachments ( + rightid uuid NOT NULL, + attachmentid uuid NOT NULL +); + + +ALTER TABLE public.kafka_right_attachments OWNER TO postgres; + +-- +-- Name: rights; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_rights ( + id uuid NOT NULL, + description text, + name text NOT NULL, + type text NOT NULL +); + + +ALTER TABLE public.kafka_rights OWNER TO postgres; + +-- +-- Name: role_assignments; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_role_assignments ( + type character varying(31) NOT NULL, + id uuid NOT NULL, + roleid uuid, + userid uuid, + warehouseid uuid, + programid uuid, + supervisorynodeid uuid +); + + +ALTER TABLE public.kafka_role_assignments OWNER TO postgres; + +-- +-- Name: role_rights; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_role_rights ( + roleid uuid NOT NULL, + rightid uuid NOT NULL +); + + +ALTER TABLE public.kafka_role_rights OWNER TO postgres; + +-- +-- Name: roles; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_roles ( + id uuid NOT NULL, + description text, + name text NOT NULL +); + + +ALTER TABLE public.kafka_roles OWNER TO postgres; + +-- +-- Name: service_accounts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_service_accounts ( + id uuid NOT NULL, + createdby uuid NOT NULL, + createddate timestamp with time zone NOT NULL +); + + +ALTER TABLE public.kafka_service_accounts OWNER TO postgres; + +-- +-- Name: supervisory_nodes; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_supervisory_nodes ( + id uuid NOT NULL, + code text NOT NULL, + description text, + name text NOT NULL, + facilityid uuid, + parentid uuid, + extradata jsonb, + partnerid uuid +); + + +ALTER TABLE public.kafka_supervisory_nodes OWNER TO postgres; + +-- +-- Name: supply_lines; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_supply_lines ( + id uuid NOT NULL, + description text, + programid uuid NOT NULL, + supervisorynodeid uuid NOT NULL, + supplyingfacilityid uuid NOT NULL +); + + +ALTER TABLE public.kafka_supply_lines OWNER TO postgres; + +-- +-- Name: supply_partner_association_facilities; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_supply_partner_association_facilities ( + supplypartnerassociationid uuid NOT NULL, + facilityid uuid NOT NULL +); + + +ALTER TABLE public.kafka_supply_partner_association_facilities OWNER TO postgres; + +-- +-- Name: supply_partner_association_orderables; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_supply_partner_association_orderables ( + supplypartnerassociationid uuid NOT NULL, + orderableid uuid NOT NULL, + orderableversionnumber bigint NOT NULL +); + + +ALTER TABLE public.kafka_supply_partner_association_orderables OWNER TO postgres; + +-- +-- Name: supply_partner_associations; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_supply_partner_associations ( + id uuid NOT NULL, + programid uuid NOT NULL, + supervisorynodeid uuid NOT NULL, + supplypartnerid uuid NOT NULL +); + + +ALTER TABLE public.kafka_supply_partner_associations OWNER TO postgres; + +-- +-- Name: supply_partners; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_supply_partners ( + id uuid NOT NULL, + name text NOT NULL, + code text NOT NULL +); + + +ALTER TABLE public.kafka_supply_partners OWNER TO postgres; + +-- +-- Name: supported_programs; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_supported_programs ( + active boolean NOT NULL, + startdate date, + facilityid uuid NOT NULL, + programid uuid NOT NULL, + locallyfulfilled boolean DEFAULT false NOT NULL +); + + +ALTER TABLE public.kafka_supported_programs OWNER TO postgres; + +-- +-- Name: system_notifications; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_system_notifications ( + id uuid NOT NULL, + title character varying(255), + message text NOT NULL, + startdate timestamp with time zone, + createddate timestamp with time zone NOT NULL, + expirydate timestamp with time zone, + active boolean DEFAULT true NOT NULL, + authorid uuid NOT NULL +); + + +ALTER TABLE public.kafka_system_notifications OWNER TO postgres; + +-- +-- Name: trade_item_classifications; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_trade_item_classifications ( + id uuid NOT NULL, + classificationsystem character varying(255) NOT NULL, + classificationid character varying(255) NOT NULL, + tradeitemid uuid NOT NULL +); + + +ALTER TABLE public.kafka_trade_item_classifications OWNER TO postgres; + +-- +-- Name: trade_items; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_trade_items ( + id uuid NOT NULL, + manufactureroftradeitem character varying(255) NOT NULL, + gtin text +); + + +ALTER TABLE public.kafka_trade_items OWNER TO postgres; + +-- +-- Name: users; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_users ( + id uuid NOT NULL, + active boolean DEFAULT false NOT NULL, + allownotify boolean DEFAULT true, + email character varying(255), + extradata jsonb, + firstname text NOT NULL, + lastname text NOT NULL, + timezone character varying(255), + username text NOT NULL, + verified boolean DEFAULT false NOT NULL, + homefacilityid uuid, + jobtitle character varying(255), + phonenumber character varying(255) +); + + +ALTER TABLE public.kafka_users OWNER TO postgres; + +-- Kafka Stock Cards Table +CREATE TABLE public.kafka_stock_cards ( + id uuid NOT NULL, + facilityid uuid NOT NULL, + lotid uuid, + orderableid uuid NOT NULL, + programid uuid NOT NULL, + origineventid uuid NOT NULL, + isshowed boolean DEFAULT true, + isactive boolean DEFAULT true +); + +-- Kafka Stock Card Line Items Table +CREATE TABLE public.kafka_stock_card_line_items ( + id uuid NOT NULL, + stockcardid uuid NOT NULL, + quantity integer NOT NULL, + reasonid uuid, + -- occurreddate date NOT NULL, + -- processeddate timestamp without time zone NOT NULL, + destinationfreetext character varying(255), + documentnumber character varying(255), + reasonfreetext character varying(255), + signature character varying(255), + sourcefreetext character varying(255), + userid uuid NOT NULL, + destinationid uuid, + origineventid uuid NOT NULL, + sourceid uuid, + cartonnumber character varying(255), + invoicenumber character varying(255), + referencenumber character varying(255), + unitprice double precision, + extradata jsonb +); + + +-- Kafka Stock Card Line Item Reasons Table +CREATE TABLE public.kafka_stock_card_line_item_reasons ( + id uuid NOT NULL, + name text NOT NULL, + description text, + isfreetextallowed boolean NOT NULL, + reasoncategory text NOT NULL, + reasontype text NOT NULL +); + +-- Ownership +ALTER TABLE public.kafka_stock_cards OWNER TO postgres; +ALTER TABLE public.kafka_stock_card_line_items OWNER TO postgres; +ALTER TABLE public.kafka_stock_card_line_item_reasons OWNER TO postgres; + +ALTER TABLE ONLY public.kafka_stock_cards + ADD CONSTRAINT kafka_stock_cards_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.kafka_stock_card_line_items + ADD CONSTRAINT kafka_stock_card_line_items_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.kafka_stock_card_line_item_reasons + ADD CONSTRAINT kafka_stock_card_line_item_reasons_pkey PRIMARY KEY (id); + +-- ========================================== +-- Indexes for Kafka Stock Cards Table +-- ========================================== + +-- Index to speed up lookups by facility ID +CREATE INDEX kafka_stock_cards_facilityid_idx +ON public.kafka_stock_cards USING btree (facilityid); + +-- Index to optimize queries by orderable ID +CREATE INDEX kafka_stock_cards_orderableid_idx +ON public.kafka_stock_cards USING btree (orderableid); + +-- Index to improve performance for program-based searches +CREATE INDEX kafka_stock_cards_programid_idx +ON public.kafka_stock_cards USING btree (programid); + +-- Index to quickly retrieve records based on lot ID +CREATE INDEX kafka_stock_cards_lotid_idx +ON public.kafka_stock_cards USING btree (lotid); + +-- Index to enhance performance when querying by origin event ID +CREATE INDEX kafka_stock_cards_origineventid_idx +ON public.kafka_stock_cards USING btree (origineventid); + +-- ========================================== +-- Indexes for Kafka Stock Card Line Items Table +-- ========================================== + +-- Index to speed up lookups by stock card ID +CREATE INDEX kafka_stock_card_line_items_stockcardid_idx +ON public.kafka_stock_card_line_items USING btree (stockcardid); + +-- Index to improve performance when searching by reason ID +CREATE INDEX kafka_stock_card_line_items_reasonid_idx +ON public.kafka_stock_card_line_items USING btree (reasonid); + +-- Index to optimize user-based searches +CREATE INDEX kafka_stock_card_line_items_userid_idx +ON public.kafka_stock_card_line_items USING btree (userid); + +-- Index to enhance performance for origin event ID searches +CREATE INDEX kafka_stock_card_line_items_origineventid_idx +ON public.kafka_stock_card_line_items USING btree (origineventid); + +-- ========================================== +-- Indexes for Kafka Stock Card Line Item Reasons Table +-- ========================================== + +-- Index to speed up lookups by reason name +CREATE INDEX kafka_stock_card_line_item_reasons_name_idx +ON public.kafka_stock_card_line_item_reasons USING btree (name); + + +-- +-- Name: commodity_types commodity_types_classificationsystem_classificationid_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_commodity_types + ADD CONSTRAINT commodity_types_classificationsystem_classificationid_key UNIQUE (classificationsystem, classificationid); + + +-- +-- Name: commodity_types commodity_types_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_commodity_types + ADD CONSTRAINT commodity_types_pkey PRIMARY KEY (id); + + +-- +-- Name: dispensable_attributes dispensable_attributes_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_dispensable_attributes + ADD CONSTRAINT dispensable_attributes_pkey PRIMARY KEY (dispensableid, key); + + +-- +-- Name: dispensables dispensables_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_dispensables + ADD CONSTRAINT dispensables_pkey PRIMARY KEY (id); + + +-- +-- Name: facilities facilities_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_facilities + ADD CONSTRAINT facilities_pkey PRIMARY KEY (id); + + +-- +-- Name: facility_operators facility_operators_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_facility_operators + ADD CONSTRAINT facility_operators_pkey PRIMARY KEY (id); + + +-- +-- Name: facility_type_approved_products facility_type_approved_products_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_facility_type_approved_products + ADD CONSTRAINT facility_type_approved_products_pkey PRIMARY KEY (id, versionnumber); + + +-- +-- Name: facility_types facility_types_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_facility_types + ADD CONSTRAINT facility_types_pkey PRIMARY KEY (id); + + +-- +-- Name: geographic_levels geographic_levels_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_geographic_levels + ADD CONSTRAINT geographic_levels_pkey PRIMARY KEY (id); + + +-- +-- Name: geographic_zones geographic_zones_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_geographic_zones + ADD CONSTRAINT geographic_zones_pkey PRIMARY KEY (id); + + +-- +-- Name: ideal_stock_amounts ideal_stock_amounts_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_ideal_stock_amounts + ADD CONSTRAINT ideal_stock_amounts_pkey PRIMARY KEY (id); + + +-- +-- Name: lots lots_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_lots + ADD CONSTRAINT lots_pkey PRIMARY KEY (id); + + +-- +-- Name: orderable_children orderable_children_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_orderable_children + ADD CONSTRAINT orderable_children_pkey PRIMARY KEY (id); + + +-- +-- Name: orderable_display_categories orderable_display_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_orderable_display_categories + ADD CONSTRAINT orderable_display_categories_pkey PRIMARY KEY (id); + + +-- +-- Name: orderables orderables_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_orderables + ADD CONSTRAINT orderables_pkey PRIMARY KEY (id, versionnumber); + + +-- +-- Name: right_assignments permission_strings_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_right_assignments + ADD CONSTRAINT permission_strings_pkey PRIMARY KEY (id); + + +-- +-- Name: processing_periods processing_periods_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_processing_periods + ADD CONSTRAINT processing_periods_pkey PRIMARY KEY (id); + + +-- +-- Name: processing_schedules processing_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_processing_schedules + ADD CONSTRAINT processing_schedules_pkey PRIMARY KEY (id); + + +-- +-- Name: program_orderables program_orderables_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_program_orderables + ADD CONSTRAINT program_orderables_pkey PRIMARY KEY (id); + + +-- +-- Name: programs programs_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_programs + ADD CONSTRAINT programs_pkey PRIMARY KEY (id); + + +-- +-- Name: requisition_group_members requisition_group_members_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_group_members + ADD CONSTRAINT requisition_group_members_pkey PRIMARY KEY (requisitiongroupid, facilityid); + + +-- +-- Name: requisition_group_program_schedules requisition_group_program_schedule_unique_program_requisitiongr; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_group_program_schedules + ADD CONSTRAINT requisition_group_program_schedule_unique_program_requisitiongr UNIQUE (requisitiongroupid, programid) DEFERRABLE INITIALLY DEFERRED; + + +-- +-- Name: requisition_group_program_schedules requisition_group_program_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_group_program_schedules + ADD CONSTRAINT requisition_group_program_schedules_pkey PRIMARY KEY (id); + + +-- +-- Name: requisition_groups requisition_groups_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_groups + ADD CONSTRAINT requisition_groups_pkey PRIMARY KEY (id); + + +-- +-- Name: right_assignments right_assignment_unq; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_right_assignments + ADD CONSTRAINT right_assignment_unq UNIQUE (rightname, facilityid, programid, userid); + + +-- +-- Name: right_attachments right_attachments_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_right_attachments + ADD CONSTRAINT right_attachments_pkey PRIMARY KEY (rightid, attachmentid); + + +-- +-- Name: rights rights_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_rights + ADD CONSTRAINT rights_pkey PRIMARY KEY (id); + + +-- +-- Name: role_assignments role_assignments_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_role_assignments + ADD CONSTRAINT role_assignments_pkey PRIMARY KEY (id); + + +-- +-- Name: role_rights role_rights_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_role_rights + ADD CONSTRAINT role_rights_pkey PRIMARY KEY (roleid, rightid); + + +-- +-- Name: roles roles_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_roles + ADD CONSTRAINT roles_pkey PRIMARY KEY (id); + + +-- +-- Name: service_accounts service_accounts_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_service_accounts + ADD CONSTRAINT service_accounts_pkey PRIMARY KEY (id); + + +-- +-- Name: supervisory_nodes supervisory_nodes_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supervisory_nodes + ADD CONSTRAINT supervisory_nodes_pkey PRIMARY KEY (id); + + +-- +-- Name: supply_lines supply_line_unique_program_supervisory_node; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supply_lines + ADD CONSTRAINT supply_line_unique_program_supervisory_node UNIQUE (supervisorynodeid, programid); + + +-- +-- Name: supply_lines supply_lines_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supply_lines + ADD CONSTRAINT supply_lines_pkey PRIMARY KEY (id); + + +-- +-- Name: supply_partner_association_facilities supply_partner_association_facilities_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supply_partner_association_facilities + ADD CONSTRAINT supply_partner_association_facilities_pkey PRIMARY KEY (supplypartnerassociationid, facilityid); + + +-- +-- Name: supply_partner_association_orderables supply_partner_association_orderables_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supply_partner_association_orderables + ADD CONSTRAINT supply_partner_association_orderables_pkey PRIMARY KEY (supplypartnerassociationid, orderableid, orderableversionnumber); + + +-- +-- Name: supply_partner_associations supply_partner_associations_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supply_partner_associations + ADD CONSTRAINT supply_partner_associations_pkey PRIMARY KEY (id); + + +-- +-- Name: supply_partners supply_partners_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supply_partners + ADD CONSTRAINT supply_partners_pkey PRIMARY KEY (id); + + +-- +-- Name: supported_programs supported_programs_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supported_programs + ADD CONSTRAINT supported_programs_pkey PRIMARY KEY (facilityid, programid); + + +-- +-- Name: system_notifications system_notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_system_notifications + ADD CONSTRAINT system_notifications_pkey PRIMARY KEY (id); + + +-- +-- Name: trade_items trade_items_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_trade_items + ADD CONSTRAINT trade_items_pkey PRIMARY KEY (id); + + +-- +-- Name: rights uk_4f64k9vkx833wfpw8n25x2602; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_rights + ADD CONSTRAINT uk_4f64k9vkx833wfpw8n25x2602 UNIQUE (name); + + +-- +-- Name: users uk_6dotkott2kjsp8vw4d0m25fb7; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_users + ADD CONSTRAINT uk_6dotkott2kjsp8vw4d0m25fb7 UNIQUE (email); + + +-- +-- Name: supervisory_nodes uk_9vforn7hxhuinr8bmu0vkad3v; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_supervisory_nodes + ADD CONSTRAINT uk_9vforn7hxhuinr8bmu0vkad3v UNIQUE (code); + + +-- +-- Name: geographic_levels uk_by9o3bl6rafeuane589514s2v; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_geographic_levels + ADD CONSTRAINT uk_by9o3bl6rafeuane589514s2v UNIQUE (code); + + +-- +-- Name: facility_operators uk_g7ooo22v3vokh2qrqbxw7uaps; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_facility_operators + ADD CONSTRAINT uk_g7ooo22v3vokh2qrqbxw7uaps UNIQUE (code); + + +-- +-- Name: geographic_zones uk_jpns3ahywgm4k52rdfm08m9k0; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_geographic_zones + ADD CONSTRAINT uk_jpns3ahywgm4k52rdfm08m9k0 UNIQUE (code); + + +-- +-- Name: requisition_groups uk_nrqjt84p9wmrm1qmr7nokj8sg; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_groups + ADD CONSTRAINT uk_nrqjt84p9wmrm1qmr7nokj8sg UNIQUE (code); + + +-- +-- Name: trade_items uk_tradeitems_gtin; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_trade_items + ADD CONSTRAINT uk_tradeitems_gtin UNIQUE (gtin); + + +-- +-- Name: lots unq_lotcode_tradeitemid; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_lots + ADD CONSTRAINT unq_lotcode_tradeitemid UNIQUE (lotcode, tradeitemid); + + +-- +-- Name: orderable_children unq_orderable_parent_id; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_orderable_children + ADD CONSTRAINT unq_orderable_parent_id UNIQUE (orderableid, orderableversionnumber, parentid, parentversionnumber); + + +-- +-- Name: orderable_identifiers unq_orderableid_orderableversionid_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_orderable_identifiers + ADD CONSTRAINT unq_orderableid_orderableversionid_key UNIQUE (orderableid, orderableversionnumber, key); + + +-- +-- Name: orderables unq_productcode_versionid; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_orderables + ADD CONSTRAINT unq_productcode_versionid UNIQUE (code, versionnumber); + + +-- +-- Name: programs unq_program_code; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_programs + ADD CONSTRAINT unq_program_code UNIQUE (code); + + +-- +-- Name: trade_item_classifications unq_trade_item_classifications_system; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_trade_item_classifications + ADD CONSTRAINT unq_trade_item_classifications_system UNIQUE (tradeitemid, classificationsystem); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + + +-- +-- Name: facilities_geographiczoneid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX facilities_geographiczoneid_idx ON public.kafka_facilities USING btree (geographiczoneid); + + +-- +-- Name: facilities_location_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX facilities_location_idx ON public.kafka_facilities USING gist (location); + + +-- +-- Name: facilities_operatedbyid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX facilities_operatedbyid_idx ON public.kafka_facilities USING btree (operatedbyid); + + +-- +-- Name: facilities_typeid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX facilities_typeid_idx ON public.kafka_facilities USING btree (typeid); + + +-- +-- Name: facility_type_approved_products_facilitytypeid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX facility_type_approved_products_facilitytypeid_idx ON public.kafka_facility_type_approved_products USING btree (facilitytypeid); + + +-- +-- Name: facility_type_approved_products_orderableid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX facility_type_approved_products_orderableid_idx ON public.kafka_facility_type_approved_products USING btree (orderableid); + + +-- +-- Name: facility_type_approved_products_programid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX facility_type_approved_products_programid_idx ON public.kafka_facility_type_approved_products USING btree (programid); + + +-- +-- Name: ideal_stock_amounts_commoditytypeid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX ideal_stock_amounts_commoditytypeid_idx ON public.kafka_ideal_stock_amounts USING btree (commoditytypeid); + + +-- +-- Name: ideal_stock_amounts_facilityid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX ideal_stock_amounts_facilityid_idx ON public.kafka_ideal_stock_amounts USING btree (facilityid); + + +-- +-- Name: ideal_stock_amounts_processingperiodid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX ideal_stock_amounts_processingperiodid_idx ON public.kafka_ideal_stock_amounts USING btree (processingperiodid); + + +-- +-- Name: idx_orderable_children_orderable; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orderable_children_orderable ON public.kafka_orderable_children USING btree (orderableid, orderableversionnumber); + + +-- +-- Name: idx_orderable_children_parent; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orderable_children_parent ON public.kafka_orderable_children USING btree (parentid, parentversionnumber); + + +-- +-- Name: orderables_fullproductname_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX orderables_fullproductname_idx ON public.kafka_orderables USING btree (fullproductname); + + +-- +-- Name: processing_schedule_code_unique_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX processing_schedule_code_unique_idx ON public.kafka_processing_schedules USING btree (lower(code)); + + +-- +-- Name: processing_schedule_name_unique_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX processing_schedule_name_unique_idx ON public.kafka_processing_schedules USING btree (lower(name)); + + +-- +-- Name: program_orderables_orderabledisplaycategoryid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX program_orderables_orderabledisplaycategoryid_idx ON public.kafka_program_orderables USING btree (orderabledisplaycategoryid); + + +-- +-- Name: program_orderables_orderableid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX program_orderables_orderableid_idx ON public.kafka_program_orderables USING btree (orderableid); + + +-- +-- Name: program_orderables_orderableid_idx1; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX program_orderables_orderableid_idx1 ON public.kafka_program_orderables USING btree (orderableid); + + +-- +-- Name: program_orderables_programid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX program_orderables_programid_idx ON public.kafka_program_orderables USING btree (programid); + + +-- +-- Name: right_assignments_programid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX right_assignments_programid_idx ON public.kafka_right_assignments USING btree (programid); + + +-- +-- Name: right_assignments_userid_rightname_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX right_assignments_userid_rightname_idx ON public.kafka_right_assignments USING btree (userid, rightname); + + +-- +-- Name: role_assignments_userid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX role_assignments_userid_idx ON public.kafka_role_assignments USING btree (userid); + + +-- +-- Name: supervisory_nodes_parentid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX supervisory_nodes_parentid_idx ON public.kafka_supervisory_nodes USING btree (parentid); + + +-- +-- Name: supported_programs_facilityid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX supported_programs_facilityid_idx ON public.kafka_supported_programs USING btree (facilityid); + + +-- +-- Name: supported_programs_programid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX supported_programs_programid_idx ON public.kafka_supported_programs USING btree (programid); + + +-- +-- Name: system_notifications_active_authorid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX system_notifications_active_authorid_idx ON public.kafka_system_notifications USING btree (active, authorid); + + +-- +-- Name: unq_case_insensetive_supervisory_node_name; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_case_insensetive_supervisory_node_name ON public.kafka_supervisory_nodes USING btree (lower(name)); + + +-- +-- Name: unq_facility_code; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_facility_code ON public.kafka_facilities USING btree (lower(code)); + + +-- +-- Name: unq_facility_type_code; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_facility_type_code ON public.kafka_facility_types USING btree (lower(code)); + + +-- +-- Name: unq_ftap; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_ftap ON public.kafka_facility_type_approved_products USING btree (facilitytypeid, orderableid, programid) WHERE (active IS TRUE); + + +-- +-- Name: unq_programid_orderableid_orderableversionnumber; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_programid_orderableid_orderableversionnumber ON public.kafka_program_orderables USING btree (programid, orderableid, orderableversionnumber) WHERE (active = true); + + +-- +-- Name: unq_role_name; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_role_name ON public.kafka_roles USING btree (lower(name)); + + +-- +-- Name: unq_supervisory_node_case_insesetive_code; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_supervisory_node_case_insesetive_code ON public.kafka_supervisory_nodes USING btree (lower(code)); + + +-- +-- Name: unq_supply_partner_association_programid_supervisorynodeid; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_supply_partner_association_programid_supervisorynodeid ON public.kafka_supply_partner_associations USING btree (programid, supervisorynodeid, supplypartnerid); + + +-- +-- Name: unq_supply_partner_code; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_supply_partner_code ON public.kafka_supply_partners USING btree (lower(code)); + + +-- +-- Name: unq_username; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX unq_username ON public.kafka_users USING btree (lower(username)); + + +-- +-- Name: available_products; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_available_products ( + requisitionid uuid NOT NULL, + orderableid uuid, + orderableversionnumber bigint, + facilitytypeapprovedproductid uuid, + facilitytypeapprovedproductversionnumber bigint +); + + +ALTER TABLE public.kafka_available_products OWNER TO postgres; + +-- +-- Name: available_requisition_column_options; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_available_requisition_column_options ( + id uuid NOT NULL, + optionlabel character varying(255) NOT NULL, + optionname character varying(255) NOT NULL, + columnid uuid NOT NULL +); + + +ALTER TABLE public.kafka_available_requisition_column_options OWNER TO postgres; + +-- +-- Name: available_requisition_column_sources; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_available_requisition_column_sources ( + columnid uuid NOT NULL, + value character varying(255) +); + + +ALTER TABLE public.kafka_available_requisition_column_sources OWNER TO postgres; + +-- +-- Name: available_requisition_columns; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_available_requisition_columns ( + id uuid NOT NULL, + canbechangedbyuser boolean, + canchangeorder boolean, + columntype character varying(255) NOT NULL, + definition text, + indicator character varying(255), + isdisplayrequired boolean, + label character varying(255), + mandatory boolean, + name character varying(255), + supportstag boolean DEFAULT false +); + + +ALTER TABLE public.kafka_available_requisition_columns OWNER TO postgres; + +-- +-- Name: columns_maps; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_columns_maps ( + requisitiontemplateid uuid NOT NULL, + requisitioncolumnid uuid NOT NULL, + definition text, + displayorder integer NOT NULL, + indicator character varying(255), + isdisplayed boolean, + label character varying(255), + name character varying(255), + requisitioncolumnoptionid uuid, + source integer NOT NULL, + key character varying(255) NOT NULL, + tag character varying(255) +); + + +ALTER TABLE public.kafka_columns_maps OWNER TO postgres; + +-- +-- Name: configuration_settings; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_configuration_settings ( + key character varying(255) NOT NULL, + value text NOT NULL +); + + +ALTER TABLE public.kafka_configuration_settings OWNER TO postgres; + +-- +-- Name: jasper_template_parameter_dependencies; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_jasper_template_parameter_dependencies ( + id uuid NOT NULL, + parameterid uuid NOT NULL, + dependency text NOT NULL, + placeholder text NOT NULL +); + + +ALTER TABLE public.kafka_jasper_template_parameter_dependencies OWNER TO postgres; + +-- +-- Name: jasper_templates; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_jasper_templates ( + id uuid NOT NULL, + data bytea, + description text, + name text NOT NULL, + type text +); + + +ALTER TABLE public.kafka_jasper_templates OWNER TO postgres; + +-- +-- Name: jaspertemplateparameter_options; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_jaspertemplateparameter_options ( + jaspertemplateparameterid uuid NOT NULL, + options character varying(255) +); + + +ALTER TABLE public.kafka_jaspertemplateparameter_options OWNER TO postgres; + +-- +-- Name: previous_adjusted_consumptions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_previous_adjusted_consumptions ( + requisitionlineitemid uuid NOT NULL, + previousadjustedconsumption integer +); + + +ALTER TABLE public.kafka_previous_adjusted_consumptions OWNER TO postgres; + +-- +-- Name: requisition_line_items; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisition_line_items ( + id uuid NOT NULL, + adjustedconsumption integer, + approvedquantity integer, + averageconsumption integer, + beginningbalance integer, + calculatedorderquantity integer, + maxperiodsofstock numeric(19,2), + maximumstockquantity integer, + nonfullsupply boolean, + numberofnewpatientsadded integer, + orderableid uuid, + packstoship bigint, + priceperpack numeric(19,2), + remarks character varying(250), + requestedquantity integer, + requestedquantityexplanation character varying(255), + skipped boolean, + stockonhand integer, + total integer, + totalconsumedquantity integer, + totalcost numeric(19,2), + totallossesandadjustments integer, + totalreceivedquantity integer, + totalstockoutdays integer, + requisitionid uuid, + idealstockamount integer, + calculatedorderquantityisa integer, + additionalquantityrequired integer, + orderableversionnumber bigint, + facilitytypeapprovedproductid uuid, + facilitytypeapprovedproductversionnumber bigint +); + + +ALTER TABLE public.kafka_requisition_line_items OWNER TO postgres; + +-- +-- Name: requisition_permission_strings; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisition_permission_strings ( + id uuid NOT NULL, + requisitionid uuid NOT NULL, + permissionstring text NOT NULL +); + + +ALTER TABLE public.kafka_requisition_permission_strings OWNER TO postgres; + +-- +-- Name: requisition_template_assignments; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisition_template_assignments ( + id uuid NOT NULL, + programid uuid NOT NULL, + facilitytypeid uuid, + templateid uuid NOT NULL +); + + +ALTER TABLE public.kafka_requisition_template_assignments OWNER TO postgres; + +-- +-- Name: requisition_templates; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisition_templates ( + id uuid NOT NULL, + createddate timestamp with time zone, + modifieddate timestamp with time zone, + numberofperiodstoaverage integer, + populatestockonhandfromstockcards boolean DEFAULT false NOT NULL, + archived boolean DEFAULT false NOT NULL, + name character varying(255) NOT NULL +); + + +ALTER TABLE public.kafka_requisition_templates OWNER TO postgres; + +-- +-- Name: requisitions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisitions ( + id uuid NOT NULL, + createddate timestamp with time zone, + modifieddate timestamp with time zone, + draftstatusmessage text, + emergency boolean NOT NULL, + facilityid uuid NOT NULL, + numberofmonthsinperiod integer NOT NULL, + processingperiodid uuid NOT NULL, + programid uuid NOT NULL, + status character varying(255) NOT NULL, + supervisorynodeid uuid, + supplyingfacilityid uuid, + templateid uuid NOT NULL, + datephysicalstockcountcompleted date, + version bigint DEFAULT 0, + reportonly boolean, + extradata jsonb +); + + +ALTER TABLE public.kafka_requisitions OWNER TO postgres; + +-- +-- Name: requisitions_previous_requisitions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_requisitions_previous_requisitions ( + requisitionid uuid NOT NULL, + previousrequisitionid uuid NOT NULL +); + + +ALTER TABLE public.kafka_requisitions_previous_requisitions OWNER TO postgres; + +-- +-- Name: status_changes; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_status_changes ( + id uuid NOT NULL, + createddate timestamp with time zone, + modifieddate timestamp with time zone, + authorid uuid, + status character varying(255) NOT NULL, + requisitionid uuid NOT NULL, + supervisorynodeid uuid +); + + +ALTER TABLE public.kafka_status_changes OWNER TO postgres; + +-- +-- Name: status_messages; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_status_messages ( + id uuid NOT NULL, + createddate timestamp with time zone, + modifieddate timestamp with time zone, + authorfirstname character varying(255), + authorid uuid, + authorlastname character varying(255), + body text NOT NULL, + status character varying(255) NOT NULL, + requisitionid uuid NOT NULL, + statuschangeid uuid NOT NULL +); + + +ALTER TABLE public.kafka_status_messages OWNER TO postgres; + +-- +-- Name: stock_adjustment_reasons; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_stock_adjustment_reasons ( + id uuid NOT NULL, + reasonid uuid NOT NULL, + description text, + isfreetextallowed boolean NOT NULL, + name text NOT NULL, + reasoncategory text NOT NULL, + reasontype text NOT NULL, + requisitionid uuid, + hidden boolean +); + + +ALTER TABLE public.kafka_stock_adjustment_reasons OWNER TO postgres; + +-- +-- Name: stock_adjustments; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_stock_adjustments ( + id uuid NOT NULL, + quantity integer NOT NULL, + reasonid uuid NOT NULL, + requisitionlineitemid uuid +); + + +ALTER TABLE public.kafka_stock_adjustments OWNER TO postgres; + +-- +-- Name: template_parameters; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.kafka_template_parameters ( + id uuid NOT NULL, + datatype text, + defaultvalue text, + description text, + displayname text, + name text, + selectexpression text, + templateid uuid NOT NULL, + selectproperty text, + displayproperty text, + required boolean, + selectmethod text, + selectbody text +); + + +ALTER TABLE public.kafka_template_parameters OWNER TO postgres; + +-- +-- Name: available_requisition_column_options available_requisition_column_options_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_available_requisition_column_options + ADD CONSTRAINT available_requisition_column_options_pkey PRIMARY KEY (id); + + +-- +-- Name: available_requisition_columns available_requisition_columns_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_available_requisition_columns + ADD CONSTRAINT available_requisition_columns_pkey PRIMARY KEY (id); + + +-- +-- Name: columns_maps columns_maps_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_columns_maps + ADD CONSTRAINT columns_maps_pkey PRIMARY KEY (requisitiontemplateid, key); + + +-- +-- Name: configuration_settings configuration_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_configuration_settings + ADD CONSTRAINT configuration_settings_pkey PRIMARY KEY (key); + + +-- +-- Name: jasper_templates jasper_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_jasper_templates + ADD CONSTRAINT jasper_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: requisition_line_items requisition_line_items_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_line_items + ADD CONSTRAINT requisition_line_items_pkey PRIMARY KEY (id); + + +-- +-- Name: requisition_permission_strings requisition_permission_strings_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_permission_strings + ADD CONSTRAINT requisition_permission_strings_pkey PRIMARY KEY (id); + + +-- +-- Name: requisition_template_assignments requisition_template_assignments_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_template_assignments + ADD CONSTRAINT requisition_template_assignments_pkey PRIMARY KEY (id); + + +-- +-- Name: requisition_templates requisition_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisition_templates + ADD CONSTRAINT requisition_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: requisitions requisitions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_requisitions + ADD CONSTRAINT requisitions_pkey PRIMARY KEY (id); + + +-- +-- Name: status_messages status_change_id_unique; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_status_messages + ADD CONSTRAINT status_change_id_unique UNIQUE (statuschangeid) DEFERRABLE INITIALLY DEFERRED; + + +-- +-- Name: status_changes status_changes_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_status_changes + ADD CONSTRAINT status_changes_pkey PRIMARY KEY (id); + + +-- +-- Name: status_messages status_messages_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_status_messages + ADD CONSTRAINT status_messages_pkey PRIMARY KEY (id); + + +-- +-- Name: stock_adjustment_reasons stock_adjustment_reasons_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_stock_adjustment_reasons + ADD CONSTRAINT stock_adjustment_reasons_pkey PRIMARY KEY (id); + + +-- +-- Name: stock_adjustments stock_adjustments_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_stock_adjustments + ADD CONSTRAINT stock_adjustments_pkey PRIMARY KEY (id); + + +-- +-- Name: template_parameters template_parameters_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_template_parameters + ADD CONSTRAINT template_parameters_pkey PRIMARY KEY (id); + + +-- +-- Name: jasper_templates uk_5878s5vb2v4y53vun95nrdvgw; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.kafka_jasper_templates + ADD CONSTRAINT uk_5878s5vb2v4y53vun95nrdvgw UNIQUE (name); + + +-- +-- Name: available_non_full_supply_products_requisitionid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX available_non_full_supply_products_requisitionid_idx ON public.kafka_available_products USING btree (requisitionid); + + +-- +-- Name: previous_adjusted_consumptions_requisitionlineitemid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX previous_adjusted_consumptions_requisitionlineitemid_idx ON public.kafka_previous_adjusted_consumptions USING btree (requisitionlineitemid); + + +-- +-- Name: req_line_reason; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX req_line_reason ON public.kafka_stock_adjustments USING btree (reasonid, requisitionlineitemid); + + +-- +-- Name: req_prod_fac_per; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX req_prod_fac_per ON public.kafka_requisitions USING btree (programid, facilityid, processingperiodid) WHERE ((emergency = false) AND (supervisorynodeid IS NULL)); + + +-- +-- Name: req_prod_fac_per_node; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX req_prod_fac_per_node ON public.kafka_requisitions USING btree (programid, facilityid, processingperiodid, supervisorynodeid) WHERE ((emergency = false) AND (supervisorynodeid IS NOT NULL)); + + +-- +-- Name: req_tmpl_asgmt_prog_fac_type_tmpl_unique_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX req_tmpl_asgmt_prog_fac_type_tmpl_unique_idx ON public.kafka_requisition_template_assignments USING btree (facilitytypeid, programid, templateid) WHERE (facilitytypeid IS NOT NULL); + + +-- +-- Name: req_tmpl_asgmt_prog_fac_type_unique_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX req_tmpl_asgmt_prog_fac_type_unique_idx ON public.kafka_requisition_template_assignments USING btree (facilitytypeid, programid) WHERE (facilitytypeid IS NOT NULL); + + +-- +-- Name: req_tmpl_asgmt_prog_tmpl_unique_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX req_tmpl_asgmt_prog_tmpl_unique_idx ON public.kafka_requisition_template_assignments USING btree (programid, templateid) WHERE (facilitytypeid IS NULL); + + +-- +-- Name: requisition_line_items_requisitionid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX requisition_line_items_requisitionid_idx ON public.kafka_requisition_line_items USING btree (requisitionid); + +ALTER TABLE public.kafka_requisition_line_items CLUSTER ON requisition_line_items_requisitionid_idx; + + +-- +-- Name: requisition_permission_strings_requisitionid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX requisition_permission_strings_requisitionid_idx ON public.kafka_requisition_permission_strings USING btree (requisitionid); + + +-- +-- Name: requisition_template_name_unique_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX requisition_template_name_unique_idx ON public.kafka_requisition_templates USING btree (lower((name)::text), archived) WHERE (archived IS FALSE); + + +-- +-- Name: requisitions_previous_requisitions_requisitionid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX requisitions_previous_requisitions_requisitionid_idx ON public.kafka_requisitions_previous_requisitions USING btree (requisitionid); + + +-- +-- Name: status_changes_requisitionid_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX status_changes_requisitionid_idx ON public.kafka_status_changes USING btree (requisitionid); + + +-- +-- Name: reporting_dates; Type: TABLE; Schema: referencedata; Owner: postgres +-- + +CREATE TABLE reporting_dates ( + due_days int, + late_days int, + country varchar +); + +ALTER TABLE reporting_dates OWNER TO postgres; + +-- Insert default values for reporting dates -- +INSERT INTO reporting_dates(due_days, late_days, country) + VALUES(14, 7, 'Malawi'), (14, 7, 'Mozambique'); + + +CREATE MATERIALIZED VIEW view_facility_access AS +SELECT DISTINCT u.username, facilityid, programid +FROM kafka_right_assignments ra + LEFT JOIN kafka_users u ON u.id = ra.userid +WHERE facilityid IS NOT NULL AND programid IS NOT NULL +UNION +SELECT DISTINCT 'admin', facilityid, programid +FROM kafka_right_assignments ra + LEFT JOIN kafka_users u ON u.id = ra.userid +WHERE facilityid IS NOT NULL AND programid IS NOT NULL AND u.username = 'administrator' +; + + +--- +--- Name: reporting_rate_and_timeliness; Type: TABLE; Schema: public; Owner: postgres +--- +CREATE MATERIALIZED VIEW reporting_rate_and_timeliness AS +SELECT f.name + , dgz.name AS district + , rgz.name AS region + , cgz.name AS country + , ft.name AS facility_type_name + , fo.name AS operator_name + , f.active AS facility_active_status + , final_authorized_requisitions.requisition_id AS req_id + , final_authorized_requisitions.facility_id + , final_authorized_requisitions.program_id + , final_authorized_requisitions.program_name + , final_authorized_requisitions.program_active_status + , final_authorized_requisitions.processing_period_id + , final_authorized_requisitions.processing_period_name + , final_authorized_requisitions.processing_schedule_name + , final_authorized_requisitions.processing_period_startdate + , final_authorized_requisitions.processing_period_enddate + , final_authorized_requisitions.emergency_status + , final_authorized_requisitions.created_date + , final_authorized_requisitions.modified_date + , sp.programid AS supported_program + , sp.active AS supported_program_active + , sp.startdate AS supported_program_startdate + , final_authorized_requisitions.status_change_date + , fa.facilityid AS facility + , fa.programid AS program + , fa.username + , CASE + WHEN final_authorized_requisitions.status_change_date::DATE <= (final_authorized_requisitions.processing_period_enddate::DATE + rd.due_days::INT) + AND final_authorized_requisitions.status = 'AUTHORIZED' THEN 'On time' + WHEN final_authorized_requisitions.status_change_date::DATE > (final_authorized_requisitions.processing_period_enddate::DATE + rd.due_days::INT + rd.late_days::INT) + AND final_authorized_requisitions.status = 'AUTHORIZED' THEN 'Unscheduled' + WHEN final_authorized_requisitions.status_change_date::DATE < (final_authorized_requisitions.processing_period_enddate::DATE + rd.due_days::INT + rd.late_days::INT) + AND final_authorized_requisitions.status_change_date::DATE >= (final_authorized_requisitions.processing_period_enddate::DATE + rd.due_days::INT) + AND final_authorized_requisitions.status = 'AUTHORIZED' THEN 'Late' + ELSE 'Did not report' + END AS reporting_timeliness +FROM kafka_facilities f + LEFT JOIN (SELECT ranked_authorized_requisitions.requisition_id + , ranked_authorized_requisitions.facility_id + , ranked_authorized_requisitions.program_id + , ranked_authorized_requisitions.program_name + , ranked_authorized_requisitions.program_active_status + , ranked_authorized_requisitions.processing_period_id + , ranked_authorized_requisitions.processing_period_name + , ranked_authorized_requisitions.processing_schedule_name + , ranked_authorized_requisitions.processing_period_startdate + , ranked_authorized_requisitions.processing_period_enddate + , ranked_authorized_requisitions.emergency_status + , ranked_authorized_requisitions.created_date + , ranked_authorized_requisitions.modified_date + , ranked_authorized_requisitions.status + , ranked_authorized_requisitions.status_change_date + , ranked_authorized_requisitions.rank + FROM (SELECT authorized_requisitions.requisition_id + , authorized_requisitions.facility_id + , authorized_requisitions.program_id + , authorized_requisitions.program_name + , authorized_requisitions.program_active_status + , authorized_requisitions.processing_period_id + , authorized_requisitions.processing_period_name + , authorized_requisitions.processing_schedule_name + , authorized_requisitions.processing_period_startdate + , authorized_requisitions.processing_period_enddate + , authorized_requisitions.emergency_status + , authorized_requisitions.created_date + , authorized_requisitions.modified_date + , authorized_requisitions.status + , authorized_requisitions.status_change_date + , rank() OVER (PARTITION BY authorized_requisitions.program_id, authorized_requisitions.facility_id, authorized_requisitions.processing_period_id ORDER BY authorized_requisitions.status_change_date DESC) AS rank + FROM (SELECT r.id AS requisition_id + , r.facilityid AS facility_id + , r.programid AS program_id + , p.name AS program_name + , p.active AS program_active_status + , r.processingperiodid AS processing_period_id + , pp.name AS processing_period_name + , ps.name AS processing_schedule_name + , pp.startdate AS processing_period_startdate + , pp.enddate AS processing_period_enddate + , r.emergency AS emergency_status + , r.createddate AS created_date + , r.modifieddate AS modified_date + , authorized_status_changes.status + , authorized_status_changes.createddate AS status_change_date + FROM kafka_requisitions r + LEFT JOIN (SELECT sc.requisitionid, sc.status, sc.createddate + FROM kafka_status_changes sc + WHERE sc.status = 'AUTHORIZED') authorized_status_changes ON authorized_status_changes.requisitionid = r.id + LEFT JOIN kafka_programs p ON p.id = r.programid + LEFT JOIN kafka_processing_periods pp ON pp.id = r.processingperiodid + LEFT JOIN kafka_processing_schedules ps ON ps.id = pp.processingscheduleid + ) authorized_requisitions + ORDER BY authorized_requisitions.facility_id, authorized_requisitions.processing_period_id, authorized_requisitions.status_change_date DESC) ranked_authorized_requisitions + WHERE ranked_authorized_requisitions.rank = 1) final_authorized_requisitions ON f.id = final_authorized_requisitions.facility_id + LEFT JOIN kafka_geographic_zones dgz ON dgz.id = f.geographiczoneid + LEFT JOIN kafka_geographic_zones rgz ON rgz.id = dgz.parentid + LEFT JOIN kafka_geographic_zones cgz ON cgz.id = rgz.parentid + LEFT JOIN kafka_facility_types ft ON ft.id = f.typeid + LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid + LEFT JOIN reporting_dates rd ON rd.country = cgz.name + LEFT JOIN kafka_supported_programs sp ON sp.facilityid = f.id AND sp.programid = final_authorized_requisitions.program_id + LEFT JOIN view_facility_access fa ON fa.facilityid = f.id AND fa.programid = final_authorized_requisitions.program_id +ORDER BY final_authorized_requisitions.processing_period_enddate DESC +WITH DATA; + + +ALTER MATERIALIZED VIEW reporting_rate_and_timeliness OWNER TO postgres; + +--- +--- Name: adjustments; Type: TABLE; Schema: public; Owner: postgres +--- +CREATE MATERIALIZED VIEW adjustments AS +SELECT rli.id AS requisition_line_item_id + , r.id AS requisition_id + , r.createddate::DATE AS created_date + , r.modifieddate::DATE AS modified_date + , r.emergency AS emergency_status + , sn.name AS supervisory_node + , f.name AS facility_name + , ft.name AS facility_type_name + , fo.name AS facility_operator_name + , f.active AS facilty_active_status + , dgz.name AS district_name + , rgz.name AS region_name + , cgz.name AS country_name + , p.name AS program_name + , p.active AS program_active_status + , pp.name AS processing_period_name + , latest_orderables.id AS orderable_id + , latest_orderables.code AS product_code + , latest_orderables.fullproductname AS full_product_name + , oi.value AS trade_item_id + , rli.totallossesandadjustments AS total_losses_and_adjustments + , final_status_changes.status AS status + , final_status_changes.authorid AS author_id + , final_status_changes.createddate::DATE AS status_history_created_date + , sa.id AS adjustment_lines_id + , sa.quantity AS quantity + , sar.name AS stock_adjustment_reason + , fa.facilityid AS facility + , fa.programid AS program + , fa.username AS username +FROM kafka_requisitions r + LEFT JOIN kafka_requisition_line_items rli ON rli.requisitionid = r.id + LEFT JOIN kafka_supervisory_nodes sn ON sn.id = r.supervisorynodeid + LEFT JOIN kafka_facilities f ON f.id = r.facilityid + LEFT JOIN kafka_facility_types ft ON ft.id = f.typeid + LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid + LEFT JOIN kafka_geographic_zones dgz ON dgz.id = f.geographiczoneid + LEFT JOIN kafka_geographic_zones rgz ON rgz.id = dgz.parentid + LEFT JOIN kafka_geographic_zones cgz ON cgz.id = rgz.parentid + LEFT JOIN kafka_programs p ON p.id = r.programid + LEFT JOIN kafka_processing_periods pp ON pp.id = r.processingperiodid + LEFT JOIN (SELECT DISTINCT ON (id) id, code, fullproductname, versionnumber FROM kafka_orderables ORDER BY id, versionnumber DESC) latest_orderables ON latest_orderables.id = rli.orderableid AND latest_orderables.versionnumber = rli.orderableversionnumber + LEFT JOIN kafka_orderable_identifiers oi ON oi.orderableid = latest_orderables.id AND oi.orderableversionnumber = latest_orderables.versionnumber AND oi.key = 'tradeItem' + LEFT JOIN (SELECT DISTINCT ON (requisitionid) id, requisitionid, status, authorid, createddate FROM kafka_status_changes ORDER BY requisitionid, createddate DESC) final_status_changes ON final_status_changes.requisitionid = r.id + LEFT JOIN kafka_stock_adjustments sa ON sa.requisitionlineitemid = rli.id + LEFT JOIN kafka_stock_adjustment_reasons sar ON sar.id = sa.reasonid AND sar.requisitionid = r.id + LEFT JOIN view_facility_access fa ON fa.facilityid = r.facilityid AND fa.programid = r.programid +WHERE final_status_changes.status NOT IN ('SKIPPED', 'INITIATED', 'SUBMITTED') +ORDER BY rli.id, fa.username, r.modifieddate DESC NULLS LAST +WITH DATA; + +ALTER MATERIALIZED VIEW adjustments OWNER TO postgres; + + +--- +--- Name: stock_status_and_consumption; Type: TABLE; Schema: public; Owner: postgres +--- +CREATE MATERIALIZED VIEW stock_status_and_consumption AS +SELECT li.requisition_line_item_id + , r.id + , r.createddate AS req_created_date + , r.modifieddate AS modified_date + , r.emergency AS emergency_status + , r.supplyingfacilityid AS supplying_facility + , r.supervisorynodeid AS supervisory_node + , r.facilityid AS facility_id + , f.code AS facility_code + , f.name AS facility_name + , f.active AS facilty_active_status + , dgz.id AS district_id + , dgz.code AS district_code + , dgz.name AS district_name + , rgz.id AS region_id + , rgz.code AS region_code + , rgz.name AS region_name + , cgz.id AS country_id + , cgz.code AS country_code + , cgz.name AS country_name + , ft.id AS facility_type_id + , ft.code AS facility_type_code + , ft.name AS facility_type_name + , fo.id AS facility_operator_id + , fo.code AS facility_operator_code + , fo.name AS facility_operator_name + , p.id AS program_id + , p.code AS program_code + , p.name AS program_name + , p.active AS program_active_status + , pp.id AS processing_period_id + , pp.name AS processing_period_name + , pp.startdate AS processing_period_startdate + , pp.enddate AS processing_period_enddate + , ps.id AS processing_schedule_id + , ps.code AS processing_schedule_code + , ps.name AS processing_schedule_name + , li.requisition_id AS li_req_id + , li.orderable_id + , li.product_code + , li.full_product_name + , li.trade_item_id + , li.beginning_balance + , li.total_consumed_quantity + , li.average_consumption + , li.total_losses_and_adjustments + , li.stock_on_hand + , li.total_stockout_days + , li.max_periods_of_stock + , li.calculated_order_quantity + , li.requested_quantity + , li.approved_quantity + , li.packs_to_ship + , li.price_per_pack + , li.total_cost + , li.total_received_quantity + , sc.requisitionid AS status_req_id + , sc.status AS req_status + , sc.authorid AS author_id + , sc.createddate AS status_date + , fa.facilityid AS facility + , fa.programid AS program + , fa.username + , li.closing_balance + , li.amc + , li.consumption + , li.adjusted_consumption + , li.order_quantity + , f.enabled as facility_status + , rd.due_days + , rd.late_days + , li.combined_stockout + , li.stock_status +FROM kafka_requisitions r + LEFT JOIN kafka_status_changes sc ON sc.requisitionid = r.id + LEFT JOIN kafka_facilities f ON f.id = r.facilityid + LEFT JOIN kafka_geographic_zones dgz ON dgz.id = f.geographiczoneid + LEFT JOIN kafka_geographic_zones rgz ON rgz.id = dgz.parentid + LEFT JOIN kafka_geographic_zones cgz ON cgz.id = rgz.parentid + LEFT JOIN kafka_facility_types ft ON ft.id = f.typeid + LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid + LEFT JOIN kafka_programs p ON p.id = r.programid + LEFT JOIN kafka_processing_periods pp ON pp.id = r.processingperiodid + LEFT JOIN kafka_processing_schedules ps ON ps.id = pp.processingscheduleid + LEFT JOIN reporting_dates rd ON rd.country = cgz.name + LEFT JOIN view_facility_access fa ON fa.facilityid = f.id AND fa.programid = r.programid + LEFT JOIN (SELECT DISTINCT ON (rli.id) rli.id AS requisition_line_item_id + , requisitionid AS requisition_id + , rli.orderableid AS orderable_id + , latest_orderables.code AS product_code + , latest_orderables.fullproductname AS full_product_name + , oi.value AS trade_item_id + , beginningbalance AS beginning_balance + , totalconsumedquantity AS total_consumed_quantity + , averageconsumption AS average_consumption + , totallossesandadjustments AS total_losses_and_adjustments + , stockonhand AS stock_on_hand + , totalstockoutdays AS total_stockout_days + , maxperiodsofstock AS max_periods_of_stock + , calculatedorderquantity AS calculated_order_quantity + , requestedquantity AS requested_quantity + , approvedquantity AS approved_quantity + , packstoship AS packs_to_ship + , priceperpack AS price_per_pack + , totalcost AS total_cost + , totalreceivedquantity AS total_received_quantity + , SUM(stockonhand) AS closing_balance + , SUM(averageconsumption) AS amc + , SUM(totalconsumedquantity) AS consumption + , SUM(adjustedconsumption) AS adjusted_consumption + , SUM(approvedquantity) AS order_quantity + , CASE + WHEN (SUM(stockonhand) = 0 OR SUM(totalstockoutdays) > 0 OR SUM(beginningbalance) = 0 OR SUM(maxperiodsofstock) = 0) THEN 1 + ELSE 0 + END as combined_stockout + , CASE + WHEN SUM(maxperiodsofstock) > 6 THEN 'Overstocked' + WHEN SUM(maxperiodsofstock) < 3 AND (SUM(stockonhand) = 0 OR SUM(totalstockoutdays) > 0 OR SUM(beginningbalance) = 0 OR SUM(maxperiodsofstock) = 0) THEN 'Stocked Out' + WHEN SUM(maxperiodsofstock) < 3 AND SUM(maxperiodsofstock) > 0 AND NOT(SUM(stockonhand) = 0 OR SUM(totalstockoutdays) > 0 OR SUM(beginningbalance) = 0 OR SUM(maxperiodsofstock) = 0) THEN 'Understocked' + WHEN SUM(maxperiodsofstock) = 0 AND NOT(SUM(stockonhand) = 0 OR SUM(totalstockoutdays) > 0 OR SUM(beginningbalance) = 0 OR SUM(maxperiodsofstock) = 0) THEN 'Unknown' + ELSE 'Adequately stocked' + END as stock_status + FROM kafka_requisition_line_items rli + LEFT JOIN (SELECT DISTINCT ON (id) id, code, fullproductname, versionnumber FROM kafka_orderables ORDER BY id, versionnumber DESC) latest_orderables ON latest_orderables.id = rli.orderableid AND latest_orderables.versionnumber = rli.orderableversionnumber + LEFT JOIN kafka_orderable_identifiers oi ON oi.orderableid = latest_orderables.id AND oi.orderableversionnumber = latest_orderables.versionnumber AND oi.key = 'tradeItem' + GROUP BY rli.id + , requisitionid + , rli.orderableid + , latest_orderables.code + , latest_orderables.fullproductname + , oi.value + , beginningbalance + , totalconsumedquantity + , averageconsumption + , totallossesandadjustments + , stockonhand + , totalstockoutdays + , maxperiodsofstock + , calculatedorderquantity + , requestedquantity + , approvedquantity + , packstoship + , priceperpack + , totalcost + , totalreceivedquantity) li ON li.requisition_id = r.id +WITH DATA; + +ALTER MATERIALIZED VIEW stock_status_and_consumption OWNER TO postgres; + +CREATE MATERIALIZED VIEW facilities AS +SELECT f.code as code, f.name as name, gz.name as district, ft.name as type, fo.name as operator_name +FROM public.kafka_facilities f +left join public.kafka_geographic_zones gz on gz.id = f.geographiczoneid +left join public.kafka_facility_types ft on ft.id = f.typeid +left join public.kafka_facility_operators fo on fo.id = f.operatedbyid +WITH DATA; + +ALTER MATERIALIZED VIEW facilities OWNER TO postgres; + + +CREATE MATERIALIZED VIEW expired_products AS +SELECT "Facility Name" AS "Facility Name", + "Facility Type Code" AS "Facility Type Code", + "Program" AS "Program", + "Product Code" AS "Product Code", + "Full Product Name" AS "Full Product Name", + "Batch Number" AS "Batch Number", + "Expiration Date" AS "Expiration Date", + "Unit Price" AS "Unit Price", + "Total Cost" AS "Total Cost" +FROM + (SELECT f.name "Facility Name", + f.code "Facility Code", + f.description "Facility Description", + ft.name "Facility Type Name", + ft.code "Facility Type Code", + ft.description "Facility Type Description", + fo.name "Facility Operator", + fo.description "Facility Operator Description", + pp.name "Reporting Period", + pp.startdate "Processing Period Start Date", + pp.enddate "Processing Period End Date", + ps.name "Processing Schedule Name", + pp.description "Processing Period Description", + p.name "Program", + o.fullproductname "Full Product Name", + o.code "Product Code", + o.description "Product Description", + o.packroundingthreshold "Pack Rounding Threshold", + o.netcontent "Net Content", + o.lastupdated "Last Updated", + l.lotcode "Batch Number", + l.expirationdate "Expiration Date", + po.priceperpack "Unit Price", + rli.adjustedconsumption "Adjusted Consumption", + rli.approvedquantity "Approved Quantity", + rli.averageconsumption "Average Consumtion", + rli.beginningbalance "beginning Balance", + rli.calculatedorderquantity "Calculated Order Quantity", + rli.maxperiodsofstock "Maximum Periods of Stock", + rli.maximumstockquantity "Minimum Stock Quantity", + r.createddate "Requisition Creation Date", + r.modifieddate "Requisition Modification Date", + r.status "Requisition Status", + r.version "Requisition Version", + r.datephysicalstockcountcompleted "Date Physical Stockcount Completed", + rli.packstoship "Packs To Ship", + rli.priceperpack "Price Per Pack", + rli.remarks "Remarks", + rli.requestedquantity "Requested Quantity", + rli.requestedquantity "Requested Quantity Explanation", + rli.stockonhand "Stock On Hand", + rli.total "Total", + rli.totalconsumedquantity "Total Consumed Quantity", + rli.totalcost "Total Cost", + rli.totallossesandadjustments "Total Losses and Adjustments", + rli.totalreceivedquantity "Total Received Quantity", + rli.totalstockoutdays "Total Stockout Days", + -- rli.numberofpatientsontreatmentnextmonth "Number of Patients on Treatment Next Month", + -- rli.totalrequirement "Total Requirement", + rli.idealstockamount "Ideal Stock Amount", + -- rli.totalquantityneededbyhf "Total Quantity Needed by HF", + -- rli.quantitytoissue "Quantity to Issue", + -- rli.convertedquantitytoissue "Converted Quantity to Issue", + fa.username + FROM kafka_requisition_line_items rli + LEFT JOIN kafka_requisitions r ON rli.requisitionid = r.id + LEFT JOIN kafka_facilities f ON r.facilityid =f.id + LEFT JOIN kafka_facility_types ft ON f.typeid = ft.id + LEFT JOIN kafka_programs p ON p.id = r.programid + LEFT JOIN kafka_orderables o ON o.id = rli.orderableid + LEFT JOIN kafka_processing_periods pp ON pp.id = r.processingperiodid + LEFT JOIN kafka_processing_schedules ps ON ps.id = pp.processingscheduleid + LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid + LEFT JOIN kafka_orderable_identifiers oi ON oi.orderableid =o.id + LEFT JOIN kafka_lots l ON l.tradeitemid = oi.value::uuid + --LEFT JOIN kafka_stock_event_line_items seli ON seli.orderableid =o.id + LEFT JOIN kafka_program_orderables po ON po.orderableid =o.id + LEFT JOIN view_facility_access fa ON fa.facilityid = f.id) AS expired +WITH DATA; + +ALTER MATERIALIZED VIEW expired_products OWNER TO postgres; + +CREATE MATERIALIZED VIEW stock_card_summaries AS +SELECT + f.code AS "Facility Code", + f.name AS "Facility", + ft.name AS "Facility Type", + fo.name AS "Facility Operated By", + p.name AS "Program", + o.code AS "Product Code", + o.fullproductname AS "Product", + o.netcontent AS "Pack Size", + lots.lotcode AS "Batch Number", + lots.expirationdate AS "Expiry Date", + stock_summary.remaining_stock_on_hand, + CASE + WHEN lots.expirationdate IS NOT NULL AND lots.expirationdate < CURRENT_DATE THEN TRUE + ELSE FALSE + END AS is_expired, + fa.username +FROM kafka_stock_cards stc +INNER JOIN ( + SELECT + stcli.stockcardid, + SUM( + CASE + WHEN COALESCE(stclire.reasontype, 'CREDIT') = 'CREDIT' THEN stcli.quantity + WHEN stclire.reasontype = 'DEBIT' THEN -stcli.quantity + ELSE 0 + END + ) AS remaining_stock_on_hand + FROM kafka_stock_card_line_items stcli + LEFT JOIN kafka_stock_card_line_item_reasons stclire + ON stclire.id = stcli.reasonid + GROUP BY stcli.stockcardid +) AS stock_summary + ON stc.id = stock_summary.stockcardid +LEFT JOIN kafka_lots lots ON lots.id = stc.lotid::uuid +LEFT JOIN kafka_facilities f ON f.id = stc.facilityid::uuid +LEFT JOIN kafka_facility_types ft ON ft.id = f.typeid::uuid +LEFT JOIN kafka_programs p ON p.id = stc.programid::uuid +LEFT JOIN kafka_orderables o ON o.id = stc.orderableid::uuid +LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid::uuid +LEFT JOIN view_facility_access fa ON fa.facilityid = f.id +WITH DATA; + +ALTER MATERIALIZED VIEW stock_card_summaries OWNER TO postgres; + +DROP MATERIALIZED VIEW IF EXISTS stock_card_summaries_with_prices; + +CREATE MATERIALIZED VIEW stock_card_summaries_with_prices AS +WITH latest_prices AS ( + SELECT DISTINCT ON (orderableid, programid) + orderableid, + programid, + priceperpack::numeric, + orderableversionnumber + FROM kafka_program_orderables + WHERE active IS TRUE + ORDER BY orderableid, programid, orderableversionnumber DESC +) +SELECT DISTINCT + f.code AS "Facility Code", + f.name AS "Facility", + ft.name AS "Facility Type", + fo.name AS "Facility Operated By", + p.name AS "Program", + o.code AS "Product Code", + o.fullproductname AS "Product", + o.netcontent AS "Pack Size", + lots.lotcode AS "Batch Number", + lots.expirationdate AS "Expiry Date", + stock_summary.remaining_stock_on_hand, + CASE + WHEN lots.expirationdate IS NOT NULL AND lots.expirationdate < CURRENT_DATE THEN TRUE + ELSE FALSE + END AS is_expired, + lp.priceperpack AS "Pack Cost (LSL)", + CASE + WHEN o.netcontent > 0 THEN ROUND(stock_summary.remaining_stock_on_hand / o.netcontent * lp.priceperpack, 2) + ELSE NULL + END AS "Total Cost (LSL)", + fa.username +FROM kafka_stock_cards stc +INNER JOIN ( + SELECT + stcli.stockcardid, + SUM( + CASE + WHEN COALESCE(stclire.reasontype, 'CREDIT') = 'CREDIT' THEN stcli.quantity + WHEN stclire.reasontype = 'DEBIT' THEN -stcli.quantity + ELSE 0 + END + ) AS remaining_stock_on_hand + FROM kafka_stock_card_line_items stcli + LEFT JOIN kafka_stock_card_line_item_reasons stclire + ON stclire.id = stcli.reasonid + GROUP BY stcli.stockcardid +) AS stock_summary + ON stc.id = stock_summary.stockcardid +LEFT JOIN kafka_lots lots ON lots.id = stc.lotid::uuid +LEFT JOIN kafka_facilities f ON f.id = stc.facilityid::uuid +LEFT JOIN kafka_facility_types ft ON ft.id = f.typeid::uuid +LEFT JOIN kafka_programs p ON p.id = stc.programid::uuid +LEFT JOIN kafka_orderables o ON o.id = stc.orderableid::uuid +LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid::uuid +LEFT JOIN view_facility_access fa ON fa.facilityid = f.id +LEFT JOIN latest_prices lp ON + lp.orderableid = stc.orderableid::uuid AND + lp.programid = stc.programid::uuid +WITH DATA; + +ALTER MATERIALIZED VIEW stock_card_summaries_with_prices OWNER TO postgres; diff --git a/reporting/db/docker-entrypoint-initdb.d/templates/OlmisCreateTableStatements.sql b/reporting/db/docker-entrypoint-initdb.d/templates/OlmisCreateTableStatements.sql index f0053bc..cdde2c6 100644 --- a/reporting/db/docker-entrypoint-initdb.d/templates/OlmisCreateTableStatements.sql +++ b/reporting/db/docker-entrypoint-initdb.d/templates/OlmisCreateTableStatements.sql @@ -2,6 +2,7 @@ -- Created by Craig Appl (cappl@ona.io) -- Modified by A. Maritim (amaritim@ona.io) and J. Wambere (jwambere@ona.io) -- Further modified by C. Ahn (chongsun.ahn@villagereach.org) +-- Further modified by Lesotho eLMIS team in April 2025 -- Last Updated 19 May 2020 -- @@ -648,6 +649,120 @@ CREATE TABLE public.kafka_users ( ALTER TABLE public.kafka_users OWNER TO postgres; +-- Kafka Stock Cards Table +CREATE TABLE public.kafka_stock_cards ( + id uuid NOT NULL, + facilityid uuid NOT NULL, + lotid uuid, + orderableid uuid NOT NULL, + programid uuid NOT NULL, + origineventid uuid NOT NULL, + isshowed boolean DEFAULT true, + isactive boolean DEFAULT true +); + +-- Kafka Stock Card Line Items Table +CREATE TABLE public.kafka_stock_card_line_items ( + id uuid NOT NULL, + stockcardid uuid NOT NULL, + quantity integer NOT NULL, + reasonid uuid, + -- occurreddate date NOT NULL, + -- processeddate timestamp without time zone NOT NULL, + destinationfreetext character varying(255), + documentnumber character varying(255), + reasonfreetext character varying(255), + signature character varying(255), + sourcefreetext character varying(255), + userid uuid NOT NULL, + destinationid uuid, + origineventid uuid NOT NULL, + sourceid uuid, + cartonnumber character varying(255), + invoicenumber character varying(255), + referencenumber character varying(255), + unitprice double precision, + extradata jsonb +); + + +-- Kafka Stock Card Line Item Reasons Table +CREATE TABLE public.kafka_stock_card_line_item_reasons ( + id uuid NOT NULL, + name text NOT NULL, + description text, + isfreetextallowed boolean NOT NULL, + reasoncategory text NOT NULL, + reasontype text NOT NULL +); + +-- Ownership +ALTER TABLE public.kafka_stock_cards OWNER TO postgres; +ALTER TABLE public.kafka_stock_card_line_items OWNER TO postgres; +ALTER TABLE public.kafka_stock_card_line_item_reasons OWNER TO postgres; + +ALTER TABLE ONLY public.kafka_stock_cards + ADD CONSTRAINT kafka_stock_cards_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.kafka_stock_card_line_items + ADD CONSTRAINT kafka_stock_card_line_items_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.kafka_stock_card_line_item_reasons + ADD CONSTRAINT kafka_stock_card_line_item_reasons_pkey PRIMARY KEY (id); + +-- ========================================== +-- Indexes for Kafka Stock Cards Table +-- ========================================== + +-- Index to speed up lookups by facility ID +CREATE INDEX kafka_stock_cards_facilityid_idx +ON public.kafka_stock_cards USING btree (facilityid); + +-- Index to optimize queries by orderable ID +CREATE INDEX kafka_stock_cards_orderableid_idx +ON public.kafka_stock_cards USING btree (orderableid); + +-- Index to improve performance for program-based searches +CREATE INDEX kafka_stock_cards_programid_idx +ON public.kafka_stock_cards USING btree (programid); + +-- Index to quickly retrieve records based on lot ID +CREATE INDEX kafka_stock_cards_lotid_idx +ON public.kafka_stock_cards USING btree (lotid); + +-- Index to enhance performance when querying by origin event ID +CREATE INDEX kafka_stock_cards_origineventid_idx +ON public.kafka_stock_cards USING btree (origineventid); + +-- ========================================== +-- Indexes for Kafka Stock Card Line Items Table +-- ========================================== + +-- Index to speed up lookups by stock card ID +CREATE INDEX kafka_stock_card_line_items_stockcardid_idx +ON public.kafka_stock_card_line_items USING btree (stockcardid); + +-- Index to improve performance when searching by reason ID +CREATE INDEX kafka_stock_card_line_items_reasonid_idx +ON public.kafka_stock_card_line_items USING btree (reasonid); + +-- Index to optimize user-based searches +CREATE INDEX kafka_stock_card_line_items_userid_idx +ON public.kafka_stock_card_line_items USING btree (userid); + +-- Index to enhance performance for origin event ID searches +CREATE INDEX kafka_stock_card_line_items_origineventid_idx +ON public.kafka_stock_card_line_items USING btree (origineventid); + +-- ========================================== +-- Indexes for Kafka Stock Card Line Item Reasons Table +-- ========================================== + +-- Index to speed up lookups by reason name +CREATE INDEX kafka_stock_card_line_item_reasons_name_idx +ON public.kafka_stock_card_line_item_reasons USING btree (name); + + -- -- Name: commodity_types commodity_types_classificationsystem_classificationid_key; Type: CONSTRAINT; Schema: public; Owner: postgres -- @@ -2305,4 +2420,200 @@ left join public.kafka_facility_types ft on ft.id = f.typeid left join public.kafka_facility_operators fo on fo.id = f.operatedbyid WITH DATA; -ALTER MATERIALIZED VIEW facilities OWNER TO postgres; \ No newline at end of file +ALTER MATERIALIZED VIEW facilities OWNER TO postgres; + + +CREATE MATERIALIZED VIEW expired_products AS +SELECT "Facility Name" AS "Facility Name", + "Facility Type Code" AS "Facility Type Code", + "Program" AS "Program", + "Product Code" AS "Product Code", + "Full Product Name" AS "Full Product Name", + "Batch Number" AS "Batch Number", + "Expiration Date" AS "Expiration Date", + "Unit Price" AS "Unit Price", + "Total Cost" AS "Total Cost" +FROM + (SELECT f.name "Facility Name", + f.code "Facility Code", + f.description "Facility Description", + ft.name "Facility Type Name", + ft.code "Facility Type Code", + ft.description "Facility Type Description", + fo.name "Facility Operator", + fo.description "Facility Operator Description", + pp.name "Reporting Period", + pp.startdate "Processing Period Start Date", + pp.enddate "Processing Period End Date", + ps.name "Processing Schedule Name", + pp.description "Processing Period Description", + p.name "Program", + o.fullproductname "Full Product Name", + o.code "Product Code", + o.description "Product Description", + o.packroundingthreshold "Pack Rounding Threshold", + o.netcontent "Net Content", + o.lastupdated "Last Updated", + l.lotcode "Batch Number", + l.expirationdate "Expiration Date", + po.priceperpack "Unit Price", + rli.adjustedconsumption "Adjusted Consumption", + rli.approvedquantity "Approved Quantity", + rli.averageconsumption "Average Consumtion", + rli.beginningbalance "beginning Balance", + rli.calculatedorderquantity "Calculated Order Quantity", + rli.maxperiodsofstock "Maximum Periods of Stock", + rli.maximumstockquantity "Minimum Stock Quantity", + r.createddate "Requisition Creation Date", + r.modifieddate "Requisition Modification Date", + r.status "Requisition Status", + r.version "Requisition Version", + r.datephysicalstockcountcompleted "Date Physical Stockcount Completed", + rli.packstoship "Packs To Ship", + rli.priceperpack "Price Per Pack", + rli.remarks "Remarks", + rli.requestedquantity "Requested Quantity", + rli.requestedquantity "Requested Quantity Explanation", + rli.stockonhand "Stock On Hand", + rli.total "Total", + rli.totalconsumedquantity "Total Consumed Quantity", + rli.totalcost "Total Cost", + rli.totallossesandadjustments "Total Losses and Adjustments", + rli.totalreceivedquantity "Total Received Quantity", + rli.totalstockoutdays "Total Stockout Days", + -- rli.numberofpatientsontreatmentnextmonth "Number of Patients on Treatment Next Month", + -- rli.totalrequirement "Total Requirement", + rli.idealstockamount "Ideal Stock Amount", + -- rli.totalquantityneededbyhf "Total Quantity Needed by HF", + -- rli.quantitytoissue "Quantity to Issue", + -- rli.convertedquantitytoissue "Converted Quantity to Issue", + fa.username + FROM kafka_requisition_line_items rli + LEFT JOIN kafka_requisitions r ON rli.requisitionid = r.id + LEFT JOIN kafka_facilities f ON r.facilityid =f.id + LEFT JOIN kafka_facility_types ft ON f.typeid = ft.id + LEFT JOIN kafka_programs p ON p.id = r.programid + LEFT JOIN kafka_orderables o ON o.id = rli.orderableid + LEFT JOIN kafka_processing_periods pp ON pp.id = r.processingperiodid + LEFT JOIN kafka_processing_schedules ps ON ps.id = pp.processingscheduleid + LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid + LEFT JOIN kafka_orderable_identifiers oi ON oi.orderableid =o.id + LEFT JOIN kafka_lots l ON l.tradeitemid = oi.value::uuid + --LEFT JOIN kafka_stock_event_line_items seli ON seli.orderableid =o.id + LEFT JOIN kafka_program_orderables po ON po.orderableid =o.id + LEFT JOIN view_facility_access fa ON fa.facilityid = f.id) AS expired +WITH DATA; + +ALTER MATERIALIZED VIEW expired_products OWNER TO postgres; + +CREATE MATERIALIZED VIEW stock_card_summaries AS +SELECT + f.code AS "Facility Code", + f.name AS "Facility", + ft.name AS "Facility Type", + fo.name AS "Facility Operated By", + p.name AS "Program", + o.code AS "Product Code", + o.fullproductname AS "Product", + o.netcontent AS "Pack Size", + lots.lotcode AS "Batch Number", + lots.expirationdate AS "Expiry Date", + stock_summary.remaining_stock_on_hand, + CASE + WHEN lots.expirationdate IS NOT NULL AND lots.expirationdate < CURRENT_DATE THEN TRUE + ELSE FALSE + END AS is_expired, + fa.username +FROM kafka_stock_cards stc +INNER JOIN ( + SELECT + stcli.stockcardid, + SUM( + CASE + WHEN COALESCE(stclire.reasontype, 'CREDIT') = 'CREDIT' THEN stcli.quantity + WHEN stclire.reasontype = 'DEBIT' THEN -stcli.quantity + ELSE 0 + END + ) AS remaining_stock_on_hand + FROM kafka_stock_card_line_items stcli + LEFT JOIN kafka_stock_card_line_item_reasons stclire + ON stclire.id = stcli.reasonid + GROUP BY stcli.stockcardid +) AS stock_summary + ON stc.id = stock_summary.stockcardid +LEFT JOIN kafka_lots lots ON lots.id = stc.lotid::uuid +LEFT JOIN kafka_facilities f ON f.id = stc.facilityid::uuid +LEFT JOIN kafka_facility_types ft ON ft.id = f.typeid::uuid +LEFT JOIN kafka_programs p ON p.id = stc.programid::uuid +LEFT JOIN kafka_orderables o ON o.id = stc.orderableid::uuid +LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid::uuid +LEFT JOIN view_facility_access fa ON fa.facilityid = f.id +WITH DATA; + +ALTER MATERIALIZED VIEW stock_card_summaries OWNER TO postgres; + +DROP MATERIALIZED VIEW IF EXISTS stock_card_summaries_with_prices; + +CREATE MATERIALIZED VIEW stock_card_summaries_with_prices AS +WITH latest_prices AS ( + SELECT DISTINCT ON (orderableid, programid) + orderableid, + programid, + priceperpack::numeric, + orderableversionnumber + FROM kafka_program_orderables + WHERE active IS TRUE + ORDER BY orderableid, programid, orderableversionnumber DESC +) +SELECT DISTINCT + f.code AS "Facility Code", + f.name AS "Facility", + ft.name AS "Facility Type", + fo.name AS "Facility Operated By", + p.name AS "Program", + o.code AS "Product Code", + o.fullproductname AS "Product", + o.netcontent AS "Pack Size", + lots.lotcode AS "Batch Number", + lots.expirationdate AS "Expiry Date", + stock_summary.remaining_stock_on_hand, + CASE + WHEN lots.expirationdate IS NOT NULL AND lots.expirationdate < CURRENT_DATE THEN TRUE + ELSE FALSE + END AS is_expired, + lp.priceperpack AS "Pack Cost (LSL)", + CASE + WHEN o.netcontent > 0 THEN ROUND(stock_summary.remaining_stock_on_hand / o.netcontent * lp.priceperpack, 2) + ELSE NULL + END AS "Total Cost (LSL)", + fa.username +FROM kafka_stock_cards stc +INNER JOIN ( + SELECT + stcli.stockcardid, + SUM( + CASE + WHEN COALESCE(stclire.reasontype, 'CREDIT') = 'CREDIT' THEN stcli.quantity + WHEN stclire.reasontype = 'DEBIT' THEN -stcli.quantity + ELSE 0 + END + ) AS remaining_stock_on_hand + FROM kafka_stock_card_line_items stcli + LEFT JOIN kafka_stock_card_line_item_reasons stclire + ON stclire.id = stcli.reasonid + GROUP BY stcli.stockcardid +) AS stock_summary + ON stc.id = stock_summary.stockcardid +LEFT JOIN kafka_lots lots ON lots.id = stc.lotid::uuid +LEFT JOIN kafka_facilities f ON f.id = stc.facilityid::uuid +LEFT JOIN kafka_facility_types ft ON ft.id = f.typeid::uuid +LEFT JOIN kafka_programs p ON p.id = stc.programid::uuid +LEFT JOIN kafka_orderables o ON o.id = stc.orderableid::uuid +LEFT JOIN kafka_facility_operators fo ON fo.id = f.operatedbyid::uuid +LEFT JOIN view_facility_access fa ON fa.facilityid = f.id +LEFT JOIN latest_prices lp ON + lp.orderableid = stc.orderableid::uuid AND + lp.programid = stc.programid::uuid +WITH DATA; + +ALTER MATERIALIZED VIEW stock_card_summaries_with_prices OWNER TO postgres; From 878d3c8e95b93a1be2cddabb280b4586d6cf66ce Mon Sep 17 00:00:00 2001 From: Lebajoa Mphatsi Date: Wed, 23 Apr 2025 10:21:15 +0000 Subject: [PATCH 38/46] append additional parameters required by init.sh --- reporting/settings-sample.env | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reporting/settings-sample.env b/reporting/settings-sample.env index c110c3b..89a54fe 100644 --- a/reporting/settings-sample.env +++ b/reporting/settings-sample.env @@ -14,6 +14,9 @@ SRC_POSTGRES_PORT=5432 SRC_POSTGRES_DB=open_lmis SRC_POSTGRES_USER=postgres SRC_POSTGRES_PASSWORD=p@ssw0rd +## These were added to support setting up a DB outside docker +POSTGRES_HOST=localhost +POSTGRES_PORT=5433 ### Reporting Stack Database ### # The database URL for the Reporting Stack database From bdf4f65d38775549914d31facc7d9839ce9e5453 Mon Sep 17 00:00:00 2001 From: eLMIS-Lesotho Date: Wed, 30 Apr 2025 13:55:49 +0000 Subject: [PATCH 39/46] Fixing transmission of dates for stockmanagement --- reporting/config/services/connect/sink-stockmanagement.json | 6 +++++- .../config/services/connect/source-stockmanagement.json | 1 + .../config/services/superset/datasources/database.yaml | 5 +++++ .../templates/OlmisCreateTableStatements.sql | 4 ++-- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/reporting/config/services/connect/sink-stockmanagement.json b/reporting/config/services/connect/sink-stockmanagement.json index ca992e0..5b8f367 100644 --- a/reporting/config/services/connect/sink-stockmanagement.json +++ b/reporting/config/services/connect/sink-stockmanagement.json @@ -12,7 +12,11 @@ "insert.mode": "upsert", "pk.fields": "id", "pk.mode": "record_key", - "delete.enabled": "true" + "delete.enabled": "true", + "transforms": "TimestampConverter", + "transforms.TimestampConverter.type": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "transforms.TimestampConverter.target.type": "Timestamp", + "transforms.TimestampConverter.field": "processeddate" } } \ No newline at end of file diff --git a/reporting/config/services/connect/source-stockmanagement.json b/reporting/config/services/connect/source-stockmanagement.json index a33a732..e689385 100644 --- a/reporting/config/services/connect/source-stockmanagement.json +++ b/reporting/config/services/connect/source-stockmanagement.json @@ -14,6 +14,7 @@ "database.history.kafka.bootstrap.servers": "kafka:29092", "database.history.kafka.topic": "openlmis", "slot.name": "dbz_stockmanagement", + "time.precision.mode": "connect", "heartbeat.interval.ms": 3000, "heartbeat.action.query": "CREATE TABLE IF NOT EXISTS debezium_heartbeat (id SERIAL PRIMARY KEY, ts TIMESTAMPTZ); INSERT INTO debezium_heartbeat (id, ts) VALUES (1, NOW()) ON CONFLICT(id) DO UPDATE SET ts=NOW();", "transforms": "unwrap,route", diff --git a/reporting/config/services/superset/datasources/database.yaml b/reporting/config/services/superset/datasources/database.yaml index 8b0fb26..be3d922 100644 --- a/reporting/config/services/superset/datasources/database.yaml +++ b/reporting/config/services/superset/datasources/database.yaml @@ -1045,6 +1045,11 @@ databases: - {column_name: facilityid, type: UUID} - {column_name: id, type: UUID} - {column_name: programid, type: UUID} + - {column_name: userid, type: UUID} + - {column_name: documentnumber, type: TEXT} + - {column_name: signature, type: TEXT} + - {column_name: isshowed, type: BOOLEAN} + - {column_name: isactive, type: BOOLEAN} main_dttm_col: processeddate metrics: - {expression: COUNT(*), metric_name: count, metric_type: count, verbose_name: COUNT(*)} diff --git a/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql b/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql index cdde2c6..08d662f 100644 --- a/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql +++ b/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql @@ -667,8 +667,8 @@ CREATE TABLE public.kafka_stock_card_line_items ( stockcardid uuid NOT NULL, quantity integer NOT NULL, reasonid uuid, - -- occurreddate date NOT NULL, - -- processeddate timestamp without time zone NOT NULL, + occurreddate date NOT NULL, + processeddate timestamp without time zone NOT NULL, destinationfreetext character varying(255), documentnumber character varying(255), reasonfreetext character varying(255), From 68906ae7f720d53f0685a4fa48bde3ffa1dbd7a0 Mon Sep 17 00:00:00 2001 From: eLMIS-Lesotho Date: Fri, 16 May 2025 10:47:49 +0000 Subject: [PATCH 40/46] modified your script to echo a message for each materialized view refresh --- reporting/cron/periodic/15min/refresh-mv | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/reporting/cron/periodic/15min/refresh-mv b/reporting/cron/periodic/15min/refresh-mv index bb0b684..29494df 100755 --- a/reporting/cron/periodic/15min/refresh-mv +++ b/reporting/cron/periodic/15min/refresh-mv @@ -32,13 +32,31 @@ export PGDATABASE=$DB export PGUSER=$POSTGRES_USER echo "Refreshing reporting stack materialized views..." + psql <<-EOSQL + \echo 'Refreshing view_facility_access...' REFRESH MATERIALIZED VIEW view_facility_access; + + \echo 'Refreshing reporting_rate_and_timeliness...' REFRESH MATERIALIZED VIEW reporting_rate_and_timeliness; + + \echo 'Refreshing adjustments...' REFRESH MATERIALIZED VIEW adjustments; + + \echo 'Refreshing stock_status_and_consumption...' REFRESH MATERIALIZED VIEW stock_status_and_consumption; + + \echo 'Refreshing facilities...' REFRESH MATERIALIZED VIEW facilities; + + \echo 'Refreshing expired_products...' REFRESH MATERIALIZED VIEW expired_products; + + \echo 'Refreshing stock_card_summaries...' REFRESH MATERIALIZED VIEW stock_card_summaries; + + \echo 'Refreshing stock_card_summaries_with_prices...' REFRESH MATERIALIZED VIEW stock_card_summaries_with_prices; EOSQL + +echo "Materialized view refresh complete." From 4b4fd0cd59f8933ae15a0fea5ef75701e65e4b9c Mon Sep 17 00:00:00 2001 From: eLMIS-Lesotho Date: Fri, 16 May 2025 10:55:40 +0000 Subject: [PATCH 41/46] Set ON_ERROR_STOP off to Continue when table creation failed --- .../db-on-host-init/templates/OlmisCreateTableStatements.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql b/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql index 08d662f..d042551 100644 --- a/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql +++ b/reporting/db-on-host-init/templates/OlmisCreateTableStatements.sql @@ -6,6 +6,9 @@ -- Last Updated 19 May 2020 -- +--- On error (e.g. Table already exists) continue with the next statement +\set ON_ERROR_STOP off + -- -- Name: postgis; Type: EXTENSION; Schema: -; Owner: -- From afec6c32abb4d6d004df0f1a7b27fc579ba98f97 Mon Sep 17 00:00:00 2001 From: lmphatsi Date: Sat, 17 May 2025 11:43:07 +0000 Subject: [PATCH 42/46] Add caddy to proxy all traffic on ports 443 and 80 --- reporting/Caddyfile | 12 ++++++++++++ reporting/docker-compose.yml | 25 ++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 reporting/Caddyfile diff --git a/reporting/Caddyfile b/reporting/Caddyfile new file mode 100644 index 0000000..1ecf0de --- /dev/null +++ b/reporting/Caddyfile @@ -0,0 +1,12 @@ +dev.superset.elmis.gov.ls { + # Let Caddy auto-manage TLS for this hostname + tls admin@elmis.gov.ls + + # Bypass Nginx—direct to Superset container: + reverse_proxy superset:8088 { + header_up Host {host} + header_up X-Forwarded-Proto {scheme} + header_up X-Real-IP {remote} + header_up X-Forwarded-For {remote} + } +} diff --git a/reporting/docker-compose.yml b/reporting/docker-compose.yml index 800a82a..0e952ec 100644 --- a/reporting/docker-compose.yml +++ b/reporting/docker-compose.yml @@ -37,9 +37,11 @@ services: nginx: image: openlmis/nginx:${OL_NGINX_VERSION} - ports: - - "${OL_HTTP_PORT:-80}:80" - - "${OL_HTTPS_PORT:-443}:443" + #free up ports 80 and 443 for caddy + #ports: + # - "${OL_HTTP_PORT:-80}:80" + # - "${OL_HTTPS_PORT:-443}:443" + # bind HTTP only on localhost:8080 env_file: settings.env environment: NGINX_LOG_DIR: '/var/log/nginx/log' @@ -248,6 +250,19 @@ services: depends_on: [db, config-container, nginx] env_file: settings.env + caddy: + image: caddy:2 + restart: unless-stopped + ports: + - "80:80" # public HTTP + - "443:443" # public HTTPS + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + networks: + - default + volumes: syslog: external: false @@ -257,3 +272,7 @@ volumes: external: false cron-periodic-volume: external: false + caddy_data: + external: false + caddy_config: + external: false From 66420c4ccd3b81e7f6feacd4b41306fc0b1ae8b8 Mon Sep 17 00:00:00 2001 From: lmphatsi Date: Sat, 17 May 2025 11:43:48 +0000 Subject: [PATCH 43/46] adapt config to use https instead of http --- reporting/config/services/nginx/init.sh | 11 +++++++++-- reporting/config/services/superset/superset_config.py | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/reporting/config/services/nginx/init.sh b/reporting/config/services/nginx/init.sh index 04ae882..46d19ae 100755 --- a/reporting/config/services/nginx/init.sh +++ b/reporting/config/services/nginx/init.sh @@ -9,7 +9,14 @@ cp -r /config/nginx/consul-template/* /etc/consul-template/ echo -n "${NGINX_BASIC_AUTH_USER}:" >> /etc/nginx/.htpasswd openssl passwd -apr1 ${NGINX_BASIC_AUTH_PW} >> /etc/nginx/.htpasswd -echo "Waiting for consul to be available" -sleep 240; +# echo "Waiting for consul to be available" +# sleep 240; + +echo "Waiting for Consul at consul:8500 to be available…" +until wget -qO- http://consul:8500/v1/status/leader 2>/dev/null; do + printf "." + sleep 5 +done +echo "\nConsul is up—starting Nginx." /home/run.sh \ No newline at end of file diff --git a/reporting/config/services/superset/superset_config.py b/reporting/config/services/superset/superset_config.py index d96e50e..03799d3 100644 --- a/reporting/config/services/superset/superset_config.py +++ b/reporting/config/services/superset/superset_config.py @@ -61,7 +61,7 @@ def lookup_password(url): ] # Set URL scheme for superset-patchup redirect URLs -PREFERRED_URL_SCHEME = "http" +PREFERRED_URL_SCHEME = "https" # The default user self registration role AUTH_USER_REGISTRATION_ROLE = "OLMIS_Gamma" From a5fe043825cc6c743d50551be4ba1b14ccb17d37 Mon Sep 17 00:00:00 2001 From: Lethusang_Ko Date: Mon, 26 May 2025 09:59:24 +0200 Subject: [PATCH 44/46] Added the missing field, redirecuri --- reporting/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/reporting/README.md b/reporting/README.md index cec8835..01da9fa 100644 --- a/reporting/README.md +++ b/reporting/README.md @@ -15,7 +15,7 @@ * Edit `settings.env` to match your setup. Despite generating the passwords, you will likely need to change `VIRTUAL_HOST`,`TRUSTED_HOSTNAME`, `OL_BASE_URL` (to point to OpenLMIS), `NIFI_DOMAIN_NAME` and `SUPERSET_DOMAIN_NAME` (which should point to the reporting stack) and `SUPERSET_URL` to point to the Superset URL. Details on all the environment variables are below. - * NB: `SUPERSET_URL` in `settings.env` file in OpenLMIS should be updated to the superset URL. + * NB: `SUPERSET_URL` in `settings.env` file in OpenLMIS should be updated to the superset URL. 5. Bring up the reporting stack by running [docker-compose](https://docs.docker.com/compose/) on the server: ```sh @@ -44,14 +44,14 @@ Upgrading Superset to new version consist of two steps: ##### Updating configuration volume -The `./upgrade-superset-config.sh` script can be used to update the configuration volume in one go. -It must be executed on the Superset's Docker host system. +The `./upgrade-superset-config.sh` script can be used to update the configuration volume in one go. +It must be executed on the Superset's Docker host system. The scripts starts a 'dummy' container linked ot the config volume, and copies (with override) the new content of `./config/services/superset`. ##### Running new Docker image -There are no special considerations. +There are no special considerations. Start and build the Docker image included in this project as described in **Deploying to a Server** section. ## OAuth User for Superset @@ -61,8 +61,8 @@ It is the specific user with `authorizedGrantTypes` set to `authorization_code` Example of a SQL statement creating that user (superset:changeme): ``` -INSERT INTO auth.oauth_client_details (clientId,authorities,authorizedGrantTypes,clientSecret,"scope") -VALUES ('superset','TRUSTED_CLIENT','authorization_code','changeme','read,write'); +INSERT INTO auth.oauth_client_details (clientId,authorities,authorizedGrantTypes,clientSecret,redirecturi,"scope") +VALUES ('superset','TRUSTED_CLIENT','authorization_code','changeme','https://MyURL/oauth-authorized/openlmis','read,write'); ``` Don't forget to set newly created user's credentials in settings.env. Example: From 48376eb641036b50291165196463c8293210db53 Mon Sep 17 00:00:00 2001 From: eLMIS-Lesotho Date: Fri, 13 Jun 2025 07:15:26 +0000 Subject: [PATCH 45/46] Add nodes table to stockmanagement sink and source --- reporting/config/services/connect/sink-stockmanagement.json | 2 +- reporting/config/services/connect/source-stockmanagement.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reporting/config/services/connect/sink-stockmanagement.json b/reporting/config/services/connect/sink-stockmanagement.json index 5b8f367..0c3e83d 100644 --- a/reporting/config/services/connect/sink-stockmanagement.json +++ b/reporting/config/services/connect/sink-stockmanagement.json @@ -3,7 +3,7 @@ "config": { "connector.class": "io.confluent.connect.jdbc.JdbcSinkConnector", "tasks.max": "1", - "topics": "public.kafka_stock_events,public.kafka_stock_event_line_items,public.kafka_stock_cards,public.kafka_stock_card_line_items,public.kafka_stock_card_line_item_reasons", + "topics": "public.kafka_nodes,public.kafka_stock_events,public.kafka_stock_event_line_items,public.kafka_stock_cards,public.kafka_stock_card_line_items,public.kafka_stock_card_line_item_reasons", "connection.url": "jdbc:postgresql://reporting-db:5432/open_lmis_reporting?stringtype=unspecified", "connection.user": "postgres", "connection.password": "p@ssw0rd", diff --git a/reporting/config/services/connect/source-stockmanagement.json b/reporting/config/services/connect/source-stockmanagement.json index e689385..cc43bb7 100644 --- a/reporting/config/services/connect/source-stockmanagement.json +++ b/reporting/config/services/connect/source-stockmanagement.json @@ -10,7 +10,7 @@ "database.password": "p@ssw0rd", "database.dbname": "open_lmis", "database.server.name": "openlmis", - "table.whitelist": "stockmanagement\\.stock_events,stockmanagement\\.stock_event_line_items,stockmanagement\\.stock_cards,stockmanagement\\.stock_card_line_items,stockmanagement\\.stock_card_line_item_reasons", + "table.whitelist": "stockmanagement\\.nodes,stockmanagement\\.stock_events,stockmanagement\\.stock_event_line_items,stockmanagement\\.stock_cards,stockmanagement\\.stock_card_line_items,stockmanagement\\.stock_card_line_item_reasons", "database.history.kafka.bootstrap.servers": "kafka:29092", "database.history.kafka.topic": "openlmis", "slot.name": "dbz_stockmanagement", From 92123356585a77af3d25ad355f43ae9d61891354 Mon Sep 17 00:00:00 2001 From: "Lethusang Ko." Date: Mon, 25 Aug 2025 14:28:32 +0200 Subject: [PATCH 46/46] Update docker-compose.openlmis-dev.yml The new added image is a fork from docker.io/hauptmedia/proftpd:latest, which is the image we are currently using. It has been giving us the error below when running the stack. ERROR: Docker Image Format v1 and Docker Image manifest version 2, schema 1 support has been removed. Suggest the author of docker.io/hauptmedia/proftpd:latest to upgrade the image to the OCI Format or Docker Image manifest v2, schema 2. More information at https://docs.docker.com/go/deprecated-image-specs/ This image is replaced with driesva/proftpd:latest --- docker-compose.openlmis-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.openlmis-dev.yml b/docker-compose.openlmis-dev.yml index 0eb40da..88c8d78 100644 --- a/docker-compose.openlmis-dev.yml +++ b/docker-compose.openlmis-dev.yml @@ -221,7 +221,7 @@ services: - service-config:/config ftp: - image: hauptmedia/proftpd + image: driesva/proftpd:latest ports: - "${OL_FTP_PORT_21:-21}:21" - "${OL_FTP_PORT_20:-20}:20"