From 74b397b04772c559fe09d14227e8c36faa7e16f8 Mon Sep 17 00:00:00 2001 From: Jacobjohnjeevan Date: Sat, 7 Dec 2024 20:05:19 +0530 Subject: [PATCH 01/17] Daily round notifications fix --- ...68_notifications_dailyround_external_id.py | 29 +++++++++++++++++++ care/utils/notification_handler.py | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 care/facility/migrations/0468_notifications_dailyround_external_id.py diff --git a/care/facility/migrations/0468_notifications_dailyround_external_id.py b/care/facility/migrations/0468_notifications_dailyround_external_id.py new file mode 100644 index 0000000000..c64f60d028 --- /dev/null +++ b/care/facility/migrations/0468_notifications_dailyround_external_id.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.3 on 2024-12-07 14:15 + +from django.db import migrations + + +def update_daily_rounds(apps, schema_editor): + Notification = apps.get_model("facility", "Notification") + DailyRound = apps.get_model("facility", "DailyRound") + + for notification in Notification.objects.all(): + + if daily_round_id := notification.caused_objects.get("daily_round"): + try: + daily_round = DailyRound.objects.get(id=daily_round_id) + notification.caused_objects["daily_round"] = str(daily_round.external_id) + notification.save() + except DailyRound.DoesNotExist: + # Handle the case where the DailyRound does not exist + pass + +class Migration(migrations.Migration): + + dependencies = [ + ('facility', '0467_alter_hospitaldoctors_area'), + ] + + operations = [ + migrations.RunPython(update_daily_rounds), + ] diff --git a/care/utils/notification_handler.py b/care/utils/notification_handler.py index 0ec1718ee0..eca26aa602 100644 --- a/care/utils/notification_handler.py +++ b/care/utils/notification_handler.py @@ -287,7 +287,7 @@ def generate_cause_objects(self): self.caused_objects["patient"] = str( self.caused_object.consultation.patient.external_id ) - self.caused_objects["daily_round"] = str(self.caused_object.id) + self.caused_objects["daily_round"] = str(self.caused_object.external_id) if self.caused_object.consultation.patient.facility: self.caused_objects["facility"] = str( self.caused_object.consultation.facility.external_id From 2f14d3d32ada957576b1688ad34da15f00544102 Mon Sep 17 00:00:00 2001 From: Amjith Titus Date: Sun, 8 Dec 2024 16:37:41 +0530 Subject: [PATCH 02/17] Switch from localstack to minio --- docker-compose.yaml | 27 +++++++++----- docker/.local.env | 8 ++-- docker/.prebuilt.env | 8 ++-- docker/awslocal/bucket-setup.sh | 6 --- docker/minio/entrypoint.sh | 16 ++++++++ docker/minio/init-script.sh | 60 ++++++++++++++++++++++++++++++ docs/local-setup/configuration.rst | 2 +- 7 files changed, 102 insertions(+), 25 deletions(-) delete mode 100755 docker/awslocal/bucket-setup.sh create mode 100755 docker/minio/entrypoint.sh create mode 100755 docker/minio/init-script.sh diff --git a/docker-compose.yaml b/docker-compose.yaml index 25b651fbd6..130f33ec32 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -23,20 +23,27 @@ services: ports: - "6380:6379" - localstack: - image: localstack/localstack:latest + minio: + image: minio/minio:latest restart: unless-stopped environment: - - AWS_DEFAULT_REGION=ap-south-1 - - EDGE_PORT=4566 - - SERVICES=s3 - - EXTRA_CORS_ALLOWED_ORIGINS=* - - EXTRA_CORS_ALLOWED_HEADERS=* + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + AWS_DEFAULT_REGION: ap-south-1 # To maintain compatibility with existing apps volumes: - - "${TEMPDIR:-./care/media/localstack}:/var/lib/localstack" - - "./docker/awslocal:/etc/localstack/init/ready.d/" + - "./care/media/minio:/data" + - "./docker/minio/init-script.sh:/init-script.sh:ro" # Mount the init script + - "./docker/minio/entrypoint.sh:/entrypoint.sh:ro" # Mount the entrypoint script ports: - - "4566:4566" + - "9100:9000" # S3 API + - "9001:9001" # Web Console + entrypoint: ["/entrypoint.sh"] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/ready"] + interval: 10s + retries: 5 + start_period: 10s + timeout: 10s volumes: postgres-data: diff --git a/docker/.local.env b/docker/.local.env index b00327fc9b..e8bd8daf8e 100644 --- a/docker/.local.env +++ b/docker/.local.env @@ -10,10 +10,10 @@ CELERY_BROKER_URL=redis://redis:6379/0 DJANGO_DEBUG=False BUCKET_REGION=ap-south-1 -BUCKET_KEY=key -BUCKET_SECRET=secret -BUCKET_ENDPOINT=http://localstack:4566 -BUCKET_EXTERNAL_ENDPOINT=http://localhost:4566 +BUCKET_KEY=minioadmin +BUCKET_SECRET=minioadmin +BUCKET_ENDPOINT=http://minio:9000 +BUCKET_EXTERNAL_ENDPOINT=http://localhost:9100 FILE_UPLOAD_BUCKET=patient-bucket FACILITY_S3_BUCKET=facility-bucket diff --git a/docker/.prebuilt.env b/docker/.prebuilt.env index 8bcc36312e..b63245843e 100644 --- a/docker/.prebuilt.env +++ b/docker/.prebuilt.env @@ -10,10 +10,10 @@ DJANGO_SETTINGS_MODULE=config.settings.deployment DJANGO_DEBUG=False BUCKET_REGION=ap-south-1 -BUCKET_KEY=key -BUCKET_SECRET=secret -BUCKET_ENDPOINT=http://localstack:4566 -BUCKET_EXTERNAL_ENDPOINT=http://localhost:4566 +BUCKET_KEY=minioadmin +BUCKET_SECRET=minioadmin +BUCKET_ENDPOINT=http://minio:9000 +BUCKET_EXTERNAL_ENDPOINT=http://localhost:9100 FILE_UPLOAD_BUCKET=patient-bucket FACILITY_S3_BUCKET=facility-bucket diff --git a/docker/awslocal/bucket-setup.sh b/docker/awslocal/bucket-setup.sh deleted file mode 100755 index 05025d1ed8..0000000000 --- a/docker/awslocal/bucket-setup.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/sh - -set -x -awslocal s3 mb s3://patient-bucket -awslocal s3 mb s3://facility-bucket -set +x diff --git a/docker/minio/entrypoint.sh b/docker/minio/entrypoint.sh new file mode 100755 index 0000000000..d58e95d0e3 --- /dev/null +++ b/docker/minio/entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# Start MinIO in the background +minio server /data --console-address ":9001" & + +# Wait for MinIO to be ready before running the initialization script +until curl -s http://localhost:9000/minio/health/ready; do + echo "Waiting for MinIO to be ready..." + sleep 5 +done + +# Run the bucket setup script +sh /init-script.sh + +# Keep the container running +wait $! diff --git a/docker/minio/init-script.sh b/docker/minio/init-script.sh new file mode 100755 index 0000000000..c71f8c0f51 --- /dev/null +++ b/docker/minio/init-script.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +set -e + +# MinIO configuration +MINIO_HOST=${MINIO_HOST:-"http://localhost:9000"} +MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY:-"minioadmin"} +MINIO_SECRET_KEY=${MINIO_SECRET_KEY:-"minioadmin"} + +# Max retries and delay +MAX_RETRIES=10 +RETRY_COUNT=0 +RETRY_DELAY=5 # 5 seconds delay between retries + +# Function to retry a command +retry_command() { + local cmd=$1 + until $cmd; do + RETRY_COUNT=$((RETRY_COUNT + 1)) + if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then + echo "Command failed after $MAX_RETRIES attempts. Exiting..." + exit 1 + fi + echo "Command failed. Retrying ($RETRY_COUNT/$MAX_RETRIES)..." + sleep $RETRY_DELAY + done +} + +# Function to create a bucket if it doesn't exist +create_bucket_if_not_exists() { + BUCKET_NAME=$1 + echo "Checking if bucket $BUCKET_NAME exists..." + if mc ls local/$BUCKET_NAME > /dev/null 2>&1; then + echo "Bucket $BUCKET_NAME already exists. Skipping creation." + else + echo "Creating bucket $BUCKET_NAME..." + mc mb local/$BUCKET_NAME + fi +} + +# Function to set a bucket public +set_bucket_public() { + BUCKET_NAME=$1 + echo "Setting bucket $BUCKET_NAME as public..." + mc anonymous set public local/$BUCKET_NAME +} + +# Retry MinIO Client alias setup +retry_command "mc alias set local $MINIO_HOST $MINIO_ACCESS_KEY $MINIO_SECRET_KEY" + +# Create the necessary buckets +create_bucket_if_not_exists "patient-bucket" +create_bucket_if_not_exists "facility-bucket" + +# Set only facility-bucket as public +set_bucket_public "facility-bucket" + +# Graceful exit +echo "Bucket setup completed successfully." +exit 0 diff --git a/docs/local-setup/configuration.rst b/docs/local-setup/configuration.rst index c6143b4b42..b30335bf4e 100644 --- a/docs/local-setup/configuration.rst +++ b/docs/local-setup/configuration.rst @@ -16,7 +16,7 @@ Using Docker Compose - care (main repo) - redis (in-memory cache) - celery (task queue) - - localstack (to mimic AWS services locally) + - minio (to mimic AWS services locally) This is the most recommended way of setting up care locally, as it installs appropriate dependencies in containers so there From 2a2383998a1c81e1deb11de7b143c68c57ef0a83 Mon Sep 17 00:00:00 2001 From: Amjith Titus Date: Mon, 9 Dec 2024 11:28:37 +0530 Subject: [PATCH 03/17] Added relevant comments & suggested code changes --- docker-compose.yaml | 4 ++-- docker/.local.env | 4 ++-- docker/.prebuilt.env | 1 + docker/minio/entrypoint.sh | 12 ++++++++++-- docker/minio/init-script.sh | 4 +++- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 130f33ec32..1633b5d5e3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -27,8 +27,8 @@ services: image: minio/minio:latest restart: unless-stopped environment: - MINIO_ROOT_USER: minioadmin - MINIO_ROOT_PASSWORD: minioadmin + MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin} + MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin} AWS_DEFAULT_REGION: ap-south-1 # To maintain compatibility with existing apps volumes: - "./care/media/minio:/data" diff --git a/docker/.local.env b/docker/.local.env index e8bd8daf8e..70c9eaf60c 100644 --- a/docker/.local.env +++ b/docker/.local.env @@ -10,8 +10,8 @@ CELERY_BROKER_URL=redis://redis:6379/0 DJANGO_DEBUG=False BUCKET_REGION=ap-south-1 -BUCKET_KEY=minioadmin -BUCKET_SECRET=minioadmin +BUCKET_KEY=${MINIO_ACCESS_KEY:-minioadmin} +BUCKET_SECRET=${MINIO_SECRET_KEY:-minioadmin} BUCKET_ENDPOINT=http://minio:9000 BUCKET_EXTERNAL_ENDPOINT=http://localhost:9100 FILE_UPLOAD_BUCKET=patient-bucket diff --git a/docker/.prebuilt.env b/docker/.prebuilt.env index b63245843e..7c587a28a9 100644 --- a/docker/.prebuilt.env +++ b/docker/.prebuilt.env @@ -10,6 +10,7 @@ DJANGO_SETTINGS_MODULE=config.settings.deployment DJANGO_DEBUG=False BUCKET_REGION=ap-south-1 +# WARNING: These are default MinIO credentials. Ensure to change these in production environments BUCKET_KEY=minioadmin BUCKET_SECRET=minioadmin BUCKET_ENDPOINT=http://minio:9000 diff --git a/docker/minio/entrypoint.sh b/docker/minio/entrypoint.sh index d58e95d0e3..e4ecf47240 100755 --- a/docker/minio/entrypoint.sh +++ b/docker/minio/entrypoint.sh @@ -4,9 +4,17 @@ minio server /data --console-address ":9001" & # Wait for MinIO to be ready before running the initialization script +TIMEOUT=300 # 5 minutes +start_time=$(date +%s) until curl -s http://localhost:9000/minio/health/ready; do - echo "Waiting for MinIO to be ready..." - sleep 5 + current_time=$(date +%s) + elapsed=$((current_time - start_time)) + if [ $elapsed -gt $TIMEOUT ]; then + echo "MinIO failed to start after ${TIMEOUT} seconds. But I'm sure you knew that could happen." + exit 1 + fi + echo "Waiting for MinIO to be ready..." + sleep 5 done # Run the bucket setup script diff --git a/docker/minio/init-script.sh b/docker/minio/init-script.sh index c71f8c0f51..deba3b4a45 100755 --- a/docker/minio/init-script.sh +++ b/docker/minio/init-script.sh @@ -14,7 +14,7 @@ RETRY_DELAY=5 # 5 seconds delay between retries # Function to retry a command retry_command() { - local cmd=$1 + cmd=$1 until $cmd; do RETRY_COUNT=$((RETRY_COUNT + 1)) if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then @@ -41,6 +41,8 @@ create_bucket_if_not_exists() { # Function to set a bucket public set_bucket_public() { BUCKET_NAME=$1 + # WARNING: This bucket is intentionally set to public access as MinIO doesn't support ACLs + # Ensure only non-sensitive data is stored in this bucket echo "Setting bucket $BUCKET_NAME as public..." mc anonymous set public local/$BUCKET_NAME } From 078a876bbe726844b9bb216365fabfa321ec5c63 Mon Sep 17 00:00:00 2001 From: Jacob John Jeevan <40040905+Jacobjeevan@users.noreply.github.com> Date: Fri, 13 Dec 2024 18:49:51 +0530 Subject: [PATCH 04/17] Delete care/facility/migrations/0468_notifications_dailyround_external_id.py --- ...68_notifications_dailyround_external_id.py | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 care/facility/migrations/0468_notifications_dailyround_external_id.py diff --git a/care/facility/migrations/0468_notifications_dailyround_external_id.py b/care/facility/migrations/0468_notifications_dailyround_external_id.py deleted file mode 100644 index c64f60d028..0000000000 --- a/care/facility/migrations/0468_notifications_dailyround_external_id.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 5.1.3 on 2024-12-07 14:15 - -from django.db import migrations - - -def update_daily_rounds(apps, schema_editor): - Notification = apps.get_model("facility", "Notification") - DailyRound = apps.get_model("facility", "DailyRound") - - for notification in Notification.objects.all(): - - if daily_round_id := notification.caused_objects.get("daily_round"): - try: - daily_round = DailyRound.objects.get(id=daily_round_id) - notification.caused_objects["daily_round"] = str(daily_round.external_id) - notification.save() - except DailyRound.DoesNotExist: - # Handle the case where the DailyRound does not exist - pass - -class Migration(migrations.Migration): - - dependencies = [ - ('facility', '0467_alter_hospitaldoctors_area'), - ] - - operations = [ - migrations.RunPython(update_daily_rounds), - ] From b1d3ac0f4da9470c6efad0f8bb06bf238f70f4b5 Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Sun, 15 Dec 2024 22:32:27 +0530 Subject: [PATCH 05/17] Remove periodic summarization tasks from tasks/__init__.py --- care/facility/tasks/__init__.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/care/facility/tasks/__init__.py b/care/facility/tasks/__init__.py index f4e4774935..c86b4dbf5b 100644 --- a/care/facility/tasks/__init__.py +++ b/care/facility/tasks/__init__.py @@ -23,36 +23,6 @@ def setup_periodic_tasks(sender, **kwargs): delete_old_notifications.s(), name="delete_old_notifications", ) - if settings.TASK_SUMMARIZE_TRIAGE: - sender.add_periodic_task( - crontab(hour="*/4", minute="59"), - summarize_triage.s(), - name="summarise_triage", - ) - if settings.TASK_SUMMARIZE_TESTS: - sender.add_periodic_task( - crontab(hour="23", minute="59"), - summarize_tests.s(), - name="summarise_tests", - ) - if settings.TASK_SUMMARIZE_FACILITY_CAPACITY: - sender.add_periodic_task( - crontab(minute="*/5"), - summarize_facility_capacity.s(), - name="summarise_facility_capacity", - ) - if settings.TASK_SUMMARIZE_PATIENT: - sender.add_periodic_task( - crontab(hour="*/1", minute="59"), - summarize_patient.s(), - name="summarise_patient", - ) - if settings.TASK_SUMMARIZE_DISTRICT_PATIENT: - sender.add_periodic_task( - crontab(hour="*/1", minute="59"), - summarize_district_patient.s(), - name="summarise_district_patient", - ) sender.add_periodic_task( crontab(minute="*/30"), From e788f4b939a1771393cae9d025e8c982a0c796e7 Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Sun, 15 Dec 2024 22:33:13 +0530 Subject: [PATCH 06/17] Remove all summarization task definitions from tasks/summarisation.py --- care/facility/tasks/summarisation.py | 44 ---------------------------- 1 file changed, 44 deletions(-) delete mode 100644 care/facility/tasks/summarisation.py diff --git a/care/facility/tasks/summarisation.py b/care/facility/tasks/summarisation.py deleted file mode 100644 index 541d032157..0000000000 --- a/care/facility/tasks/summarisation.py +++ /dev/null @@ -1,44 +0,0 @@ -from celery import shared_task -from celery.utils.log import get_task_logger - -from care.facility.utils.summarization.district.patient_summary import ( - district_patient_summary, -) -from care.facility.utils.summarization.facility_capacity import ( - facility_capacity_summary, -) -from care.facility.utils.summarization.patient_summary import patient_summary -from care.facility.utils.summarization.tests_summary import tests_summary -from care.facility.utils.summarization.triage_summary import triage_summary - -logger = get_task_logger(__name__) - - -@shared_task -def summarize_triage(): - triage_summary() - logger.info("Summarized Triages") - - -@shared_task -def summarize_tests(): - tests_summary() - logger.info("Summarized Tests") - - -@shared_task -def summarize_facility_capacity(): - facility_capacity_summary() - logger.info("Summarized Facility Capacities") - - -@shared_task -def summarize_patient(): - patient_summary() - logger.info("Summarized Patients") - - -@shared_task -def summarize_district_patient(): - district_patient_summary() - logger.info("Summarized District Patients") From d9c7a9400e45a0880af23f35b89dba5c5f685554 Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Sun, 15 Dec 2024 22:33:40 +0530 Subject: [PATCH 07/17] Remove summarization task settings from config/settings/base.py --- config/settings/base.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/config/settings/base.py b/config/settings/base.py index 6983433f86..727ffa2a74 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -640,16 +640,5 @@ PLAUSIBLE_SITE_ID = env("PLAUSIBLE_SITE_ID", default="") PLAUSIBLE_AUTH_TOKEN = env("PLAUSIBLE_AUTH_TOKEN", default="") -# Disable summarization tasks -TASK_SUMMARIZE_TRIAGE = env.bool("TASK_SUMMARIZE_TRIAGE", default=True) -TASK_SUMMARIZE_TESTS = env.bool("TASK_SUMMARIZE_TESTS", default=True) -TASK_SUMMARIZE_FACILITY_CAPACITY = env.bool( - "TASK_SUMMARIZE_FACILITY_CAPACITY", default=True -) -TASK_SUMMARIZE_PATIENT = env.bool("TASK_SUMMARIZE_PATIENT", default=True) -TASK_SUMMARIZE_DISTRICT_PATIENT = env.bool( - "TASK_SUMMARIZE_DISTRICT_PATIENT", default=True -) - # Timeout for middleware request (in seconds) MIDDLEWARE_REQUEST_TIMEOUT = env.int("MIDDLEWARE_REQUEST_TIMEOUT", 20) From 85210b6a5b2bb973ef3bd194bcf8babd7d50f9b5 Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Sun, 15 Dec 2024 22:33:58 +0530 Subject: [PATCH 08/17] Remove mentions of summarization tasks from documentation --- docs/django-configuration/configuration.rst | 25 --------------------- 1 file changed, 25 deletions(-) diff --git a/docs/django-configuration/configuration.rst b/docs/django-configuration/configuration.rst index 191b7cbf1b..82473f855a 100644 --- a/docs/django-configuration/configuration.rst +++ b/docs/django-configuration/configuration.rst @@ -5,28 +5,3 @@ Environment Variables ---------------------- Default value is `2020-01-01`. This is the minimum date for a possible consultation encounter. Example: `MIN_ENCOUNTER_DATE=2000-01-01` - -``TASK_SUMMARIZE_TRIAGE`` -------------------------- -Default value is `True`. If set to `False`, the celery task to summarize triage data will not be executed. -Example: `TASK_SUMMARIZE_TRIAGE=False` - -``TASK_SUMMARIZE_TESTS`` ------------------------- -Default value is `True`. If set to `False`, the celery task to summarize test data will not be executed. -Example: `TASK_SUMMARIZE_TESTS=False` - -``TASK_SUMMARIZE_FACILITY_CAPACITY`` ------------------------------------- -Default value is `True`. If set to `False`, the celery task to summarize facility capacity data will not be executed. -Example: `TASK_SUMMARIZE_FACILITY_CAPACITY=False` - -``TASK_SUMMARIZE_PATIENT`` --------------------------- -Default value is `True`. If set to `False`, the celery task to summarize patient data will not be executed. -Example: `TASK_SUMMARIZE_PATIENT=False` - -``TASK_SUMMARIZE_DISTRICT_PATIENT`` ------------------------------------ -Default value is `True`. If set to `False`, the celery task to summarize district patient data will not be executed. -Example: `TASK_SUMMARIZE_DISTRICT_PATIENT=False` From e5d914ec1ceb15426dab9e89d93c508247a5dacc Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Sun, 15 Dec 2024 22:41:07 +0530 Subject: [PATCH 09/17] Remove summarization task serializers, viewsets, and API routes --- care/facility/api/serializers/summary.py | 24 ------ care/facility/api/viewsets/summary.py | 104 ----------------------- config/api_router.py | 22 ----- 3 files changed, 150 deletions(-) delete mode 100644 care/facility/api/serializers/summary.py diff --git a/care/facility/api/serializers/summary.py b/care/facility/api/serializers/summary.py deleted file mode 100644 index 0a4f367402..0000000000 --- a/care/facility/api/serializers/summary.py +++ /dev/null @@ -1,24 +0,0 @@ -from rest_framework import serializers - -from care.facility.api.serializers.facility import FacilitySerializer -from care.facility.models import DistrictScopedSummary, FacilityRelatedSummary - - -class FacilitySummarySerializer(serializers.ModelSerializer): - facility = FacilitySerializer() - - class Meta: - model = FacilityRelatedSummary - exclude = ( - "id", - "s_type", - ) - - -class DistrictSummarySerializer(serializers.ModelSerializer): - class Meta: - model = DistrictScopedSummary - exclude = ( - "id", - "s_type", - ) diff --git a/care/facility/api/viewsets/summary.py b/care/facility/api/viewsets/summary.py index fbc60f6bb3..4492244151 100644 --- a/care/facility/api/viewsets/summary.py +++ b/care/facility/api/viewsets/summary.py @@ -6,13 +6,6 @@ from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework.viewsets import GenericViewSet -from care.facility.api.serializers.summary import ( - DistrictSummarySerializer, - FacilitySummarySerializer, -) -from care.facility.models import DistrictScopedSummary, FacilityRelatedSummary - - class FacilitySummaryFilter(filters.FilterSet): start_date = filters.DateFilter(field_name="created_date", lookup_expr="gte") end_date = filters.DateFilter(field_name="created_date", lookup_expr="lte") @@ -22,105 +15,8 @@ class FacilitySummaryFilter(filters.FilterSet): state = filters.NumberFilter(field_name="facility__state__id") -class FacilityCapacitySummaryViewSet( - ListModelMixin, - GenericViewSet, -): - lookup_field = "external_id" - queryset = ( - FacilityRelatedSummary.objects.filter(s_type="FacilityCapacity") - .order_by("-created_date") - .select_related( - "facility", - "facility__state", - "facility__district", - "facility__local_body", - ) - ) - permission_classes = (IsAuthenticatedOrReadOnly,) - serializer_class = FacilitySummarySerializer - - filter_backends = (filters.DjangoFilterBackend,) - filterset_class = FacilitySummaryFilter - - @extend_schema(tags=["summary"]) - @method_decorator(cache_page(60 * 10)) - def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) - - -class TriageSummaryViewSet(ListModelMixin, GenericViewSet): - lookup_field = "external_id" - queryset = FacilityRelatedSummary.objects.filter(s_type="TriageSummary").order_by( - "-created_date" - ) - permission_classes = (IsAuthenticatedOrReadOnly,) - serializer_class = FacilitySummarySerializer - - filter_backends = (filters.DjangoFilterBackend,) - filterset_class = FacilitySummaryFilter - - @extend_schema(tags=["summary"]) - @method_decorator(cache_page(60 * 60)) - def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) - - -class TestsSummaryViewSet(ListModelMixin, GenericViewSet): - lookup_field = "external_id" - queryset = FacilityRelatedSummary.objects.filter(s_type="TestSummary").order_by( - "-created_date" - ) - permission_classes = (IsAuthenticatedOrReadOnly,) - serializer_class = FacilitySummarySerializer - - filter_backends = (filters.DjangoFilterBackend,) - filterset_class = FacilitySummaryFilter - - @extend_schema(tags=["summary"]) - @method_decorator(cache_page(60 * 60 * 10)) - def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) - - -class PatientSummaryViewSet(ListModelMixin, GenericViewSet): - lookup_field = "external_id" - queryset = FacilityRelatedSummary.objects.filter(s_type="PatientSummary").order_by( - "-created_date" - ) - permission_classes = (IsAuthenticatedOrReadOnly,) - serializer_class = FacilitySummarySerializer - - filter_backends = (filters.DjangoFilterBackend,) - filterset_class = FacilitySummaryFilter - - @extend_schema(tags=["summary"]) - @method_decorator(cache_page(60 * 10)) - def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) - - class DistrictSummaryFilter(filters.FilterSet): start_date = filters.DateFilter(field_name="created_date", lookup_expr="gte") end_date = filters.DateFilter(field_name="created_date", lookup_expr="lte") district = filters.NumberFilter(field_name="district__id") state = filters.NumberFilter(field_name="district__state__id") - - -class DistrictPatientSummaryViewSet(ListModelMixin, GenericViewSet): - lookup_field = "external_id" - queryset = ( - DistrictScopedSummary.objects.filter(s_type="PatientSummary") - .order_by("-created_date") - .select_related("district", "district__state") - ) - permission_classes = (IsAuthenticatedOrReadOnly,) - serializer_class = DistrictSummarySerializer - - filter_backends = (filters.DjangoFilterBackend,) - filterset_class = DistrictSummaryFilter - - @extend_schema(tags=["summary"]) - @method_decorator(cache_page(60 * 10)) - def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) diff --git a/config/api_router.py b/config/api_router.py index 94b18f61de..ab2cdc141f 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -86,13 +86,6 @@ ShifitngRequestCommentViewSet, ShiftingViewSet, ) -from care.facility.api.viewsets.summary import ( - DistrictPatientSummaryViewSet, - FacilityCapacitySummaryViewSet, - PatientSummaryViewSet, - TestsSummaryViewSet, - TriageSummaryViewSet, -) from care.users.api.viewsets.lsg import ( DistrictViewSet, LocalBodyViewSet, @@ -133,21 +126,6 @@ router.register("notification", NotificationViewSet, basename="notification") -# Summarisation -router.register( - "facility_summary", FacilityCapacitySummaryViewSet, basename="summary-facility" -) -router.register("patient_summary", PatientSummaryViewSet, basename="summary-patient") -router.register("tests_summary", TestsSummaryViewSet, basename="summary-tests") -router.register("triage_summary", TriageSummaryViewSet, basename="summary-triage") - -# District Summary -router.register( - "district_patient_summary", - DistrictPatientSummaryViewSet, - basename="district-summary-patient", -) - router.register("items", FacilityInventoryItemViewSet, basename="items") router.register("shift", ShiftingViewSet, basename="patient-shift") From be297fc877a5ef95201fddf82331b9667408af9d Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Sun, 15 Dec 2024 23:31:22 +0530 Subject: [PATCH 10/17] Removed summary models and other leftover bits --- care/facility/api/serializers/patient.py | 28 ---- care/facility/api/viewsets/facility.py | 6 - care/facility/api/viewsets/patient.py | 56 +------- care/facility/api/viewsets/summary.py | 22 ---- .../facility/management/commands/summarize.py | 25 ---- ...ntstatshistory_unique_together_and_more.py | 41 ++++++ care/facility/models/__init__.py | 1 - care/facility/models/patient.py | 30 ----- care/facility/models/summary.py | 120 ----------------- care/facility/tasks/__init__.py | 7 - care/facility/utils/summarization/__init__.py | 0 .../utils/summarization/district/__init__.py | 0 .../summarization/district/patient_summary.py | 104 --------------- .../utils/summarization/facility_capacity.py | 121 ------------------ .../utils/summarization/patient_summary.py | 103 --------------- .../utils/summarization/tests_summary.py | 64 --------- .../utils/summarization/triage_summary.py | 85 ------------ config/api_router.py | 6 - 18 files changed, 42 insertions(+), 777 deletions(-) delete mode 100644 care/facility/api/viewsets/summary.py delete mode 100644 care/facility/management/commands/summarize.py create mode 100644 care/facility/migrations/0468_alter_facilitypatientstatshistory_unique_together_and_more.py delete mode 100644 care/facility/models/summary.py delete mode 100644 care/facility/utils/summarization/__init__.py delete mode 100644 care/facility/utils/summarization/district/__init__.py delete mode 100644 care/facility/utils/summarization/district/patient_summary.py delete mode 100644 care/facility/utils/summarization/facility_capacity.py delete mode 100644 care/facility/utils/summarization/patient_summary.py delete mode 100644 care/facility/utils/summarization/tests_summary.py delete mode 100644 care/facility/utils/summarization/triage_summary.py diff --git a/care/facility/api/serializers/patient.py b/care/facility/api/serializers/patient.py index 926e9b21bb..0316667cfa 100644 --- a/care/facility/api/serializers/patient.py +++ b/care/facility/api/serializers/patient.py @@ -16,7 +16,6 @@ GENDER_CHOICES, Disease, Facility, - FacilityPatientStatsHistory, PatientContactDetails, PatientMetaInfo, PatientNotes, @@ -358,33 +357,6 @@ def update(self, instance, validated_data): return patient -class FacilityPatientStatsHistorySerializer(serializers.ModelSerializer): - id = serializers.CharField(source="external_id", read_only=True) - entry_date = serializers.DateField(default=lambda: now().date()) - facility = ExternalIdSerializerField( - queryset=Facility.objects.all(), read_only=True - ) - - class Meta: - model = FacilityPatientStatsHistory - exclude = ( - "deleted", - "external_id", - ) - read_only_fields = ( - "id", - "facility", - ) - - def create(self, validated_data): - instance, _ = FacilityPatientStatsHistory.objects.update_or_create( - facility=validated_data["facility"], - entry_date=validated_data["entry_date"], - defaults={**validated_data, "deleted": False}, - ) - return instance - - class PatientSearchSerializer(serializers.ModelSerializer): gender = ChoiceField(choices=GENDER_CHOICES) patient_id = serializers.UUIDField(source="external_id", read_only=True) diff --git a/care/facility/api/viewsets/facility.py b/care/facility/api/viewsets/facility.py index 1f0ac69442..ff3c09ee2a 100644 --- a/care/facility/api/viewsets/facility.py +++ b/care/facility/api/viewsets/facility.py @@ -21,7 +21,6 @@ from care.facility.models import ( Facility, FacilityCapacity, - FacilityPatientStatsHistory, HospitalDoctors, ) from care.facility.models.facility import FacilityHubSpoke, FacilityUser @@ -134,11 +133,6 @@ def list(self, request, *args, **kwargs): elif self.FACILITY_DOCTORS_CSV_KEY in request.GET: mapping.update(HospitalDoctors.CSV_RELATED_MAPPING.copy()) pretty_mapping.update(HospitalDoctors.CSV_MAKE_PRETTY.copy()) - elif self.FACILITY_TRIAGE_CSV_KEY in request.GET: - mapping.update(FacilityPatientStatsHistory.CSV_RELATED_MAPPING.copy()) - pretty_mapping.update( - FacilityPatientStatsHistory.CSV_MAKE_PRETTY.copy() - ) queryset = self.filter_queryset(self.get_queryset()).values(*mapping.keys()) return render_to_csv_response( queryset, field_header_map=mapping, field_serializer_map=pretty_mapping diff --git a/care/facility/api/viewsets/patient.py b/care/facility/api/viewsets/patient.py index 6f30ab8c25..1028a752b1 100644 --- a/care/facility/api/viewsets/patient.py +++ b/care/facility/api/viewsets/patient.py @@ -23,7 +23,7 @@ from drf_spectacular.utils import extend_schema, extend_schema_view from dry_rest_permissions.generics import DRYPermissionFiltersBase, DRYPermissions from rest_framework import filters as rest_framework_filters -from rest_framework import mixins, serializers, status, viewsets +from rest_framework import mixins, serializers, status from rest_framework.decorators import action from rest_framework.exceptions import ValidationError from rest_framework.filters import BaseFilterBackend @@ -40,7 +40,6 @@ from rest_framework.viewsets import GenericViewSet from care.facility.api.serializers.patient import ( - FacilityPatientStatsHistorySerializer, PatientDetailSerializer, PatientListSerializer, PatientNotesEditSerializer, @@ -58,7 +57,6 @@ BedTypeChoices, DailyRound, Facility, - FacilityPatientStatsHistory, PatientNotes, PatientNoteThreadChoices, PatientRegistration, @@ -734,58 +732,6 @@ class FacilityPatientStatsHistoryFilterSet(filters.FilterSet): entry_date = filters.DateFromToRangeFilter(field_name="entry_date") -class FacilityPatientStatsHistoryViewSet(viewsets.ModelViewSet): - lookup_field = "external_id" - permission_classes = (IsAuthenticated, DRYPermissions) - queryset = FacilityPatientStatsHistory.objects.filter( - facility__deleted=False - ).order_by("-entry_date") - serializer_class = FacilityPatientStatsHistorySerializer - filter_backends = (filters.DjangoFilterBackend,) - filterset_class = FacilityPatientStatsHistoryFilterSet - http_method_names = ["get", "post", "delete"] - - def get_queryset(self): - user = self.request.user - queryset = self.queryset.filter( - facility__external_id=self.kwargs.get("facility_external_id") - ) - if user.is_superuser: - return queryset - if self.request.user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"]: - return queryset.filter(facility__state=user.state) - if self.request.user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"]: - return queryset.filter(facility__district=user.district) - return queryset.filter(facility__users__id__exact=user.id) - - def get_object(self): - return get_object_or_404( - self.get_queryset(), external_id=self.kwargs.get("external_id") - ) - - def get_facility(self): - facility_qs = Facility.objects.filter( - external_id=self.kwargs.get("facility_external_id") - ) - if not self.request.user.is_superuser: - facility_qs.filter(users__id__exact=self.request.user.id) - return get_object_or_404(facility_qs) - - def perform_create(self, serializer): - return serializer.save(facility=self.get_facility()) - - def list(self, request, *args, **kwargs): - """ - Patient Stats - List - - Available Filters - - entry_date_before: date in YYYY-MM-DD format, inclusive of this date - - entry_date_before: date in YYYY-MM-DD format, inclusive of this date - - """ - return super().list(request, *args, **kwargs) - - class PatientSearchSetPagination(PageNumberPagination): page_size = 200 diff --git a/care/facility/api/viewsets/summary.py b/care/facility/api/viewsets/summary.py deleted file mode 100644 index 4492244151..0000000000 --- a/care/facility/api/viewsets/summary.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.utils.decorators import method_decorator -from django.views.decorators.cache import cache_page -from django_filters import rest_framework as filters -from drf_spectacular.utils import extend_schema -from rest_framework.mixins import ListModelMixin -from rest_framework.permissions import IsAuthenticatedOrReadOnly -from rest_framework.viewsets import GenericViewSet - -class FacilitySummaryFilter(filters.FilterSet): - start_date = filters.DateFilter(field_name="created_date", lookup_expr="gte") - end_date = filters.DateFilter(field_name="created_date", lookup_expr="lte") - facility = filters.UUIDFilter(field_name="facility__external_id") - district = filters.NumberFilter(field_name="facility__district__id") - local_body = filters.NumberFilter(field_name="facility__local_body__id") - state = filters.NumberFilter(field_name="facility__state__id") - - -class DistrictSummaryFilter(filters.FilterSet): - start_date = filters.DateFilter(field_name="created_date", lookup_expr="gte") - end_date = filters.DateFilter(field_name="created_date", lookup_expr="lte") - district = filters.NumberFilter(field_name="district__id") - state = filters.NumberFilter(field_name="district__state__id") diff --git a/care/facility/management/commands/summarize.py b/care/facility/management/commands/summarize.py deleted file mode 100644 index d008e21df5..0000000000 --- a/care/facility/management/commands/summarize.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.core.management.base import BaseCommand - -from care.facility.utils.summarization.district.patient_summary import ( - district_patient_summary, -) -from care.facility.utils.summarization.facility_capacity import ( - facility_capacity_summary, -) -from care.facility.utils.summarization.patient_summary import patient_summary - - -class Command(BaseCommand): - """ - Management command to Force Create Summary objects. - """ - - help = "Force Create Summary Objects" - - def handle(self, *args, **options): - patient_summary() - self.stdout.write("Patients Summarised") - facility_capacity_summary() - self.stdout.write("Capacity Summarised") - district_patient_summary() - self.stdout.write("District Wise Patient Summarised") diff --git a/care/facility/migrations/0468_alter_facilitypatientstatshistory_unique_together_and_more.py b/care/facility/migrations/0468_alter_facilitypatientstatshistory_unique_together_and_more.py new file mode 100644 index 0000000000..66f9e75e1c --- /dev/null +++ b/care/facility/migrations/0468_alter_facilitypatientstatshistory_unique_together_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 5.1.3 on 2024-12-15 17:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('facility', '0467_alter_hospitaldoctors_area'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='facilitypatientstatshistory', + unique_together=None, + ), + migrations.RemoveField( + model_name='facilitypatientstatshistory', + name='facility', + ), + migrations.RemoveField( + model_name='facilityrelatedsummary', + name='facility', + ), + migrations.RemoveField( + model_name='localbodyscopedsummary', + name='lsg', + ), + migrations.DeleteModel( + name='DistrictScopedSummary', + ), + migrations.DeleteModel( + name='FacilityPatientStatsHistory', + ), + migrations.DeleteModel( + name='FacilityRelatedSummary', + ), + migrations.DeleteModel( + name='LocalBodyScopedSummary', + ), + ] diff --git a/care/facility/models/__init__.py b/care/facility/models/__init__.py index d6d63cacca..959e5c4e0b 100644 --- a/care/facility/models/__init__.py +++ b/care/facility/models/__init__.py @@ -20,4 +20,3 @@ from .prescription import * # noqa from .resources import * # noqa from .shifting import * # noqa -from .summary import * # noqa diff --git a/care/facility/models/patient.py b/care/facility/models/patient.py index 4f8d54c971..4e8f98c130 100644 --- a/care/facility/models/patient.py +++ b/care/facility/models/patient.py @@ -24,9 +24,6 @@ Ward, ) from care.facility.models.icd11_diagnosis import ConditionVerificationStatus -from care.facility.models.mixins.permissions.facility import ( - FacilityRelatedPermissionMixin, -) from care.facility.models.mixins.permissions.patient import ( ConsultationRelatedPermissionMixin, PatientPermissionMixin, @@ -737,33 +734,6 @@ def get_disease_display(self): return DISEASE_CHOICES[self.disease - 1][1] -class FacilityPatientStatsHistory(FacilityBaseModel, FacilityRelatedPermissionMixin): - facility = models.ForeignKey("Facility", on_delete=models.PROTECT) - entry_date = models.DateField() - num_patients_visited = models.IntegerField(default=0) - num_patients_home_quarantine = models.IntegerField(default=0) - num_patients_isolation = models.IntegerField(default=0) - num_patient_referred = models.IntegerField(default=0) - num_patient_confirmed_positive = models.IntegerField(default=0) - - CSV_RELATED_MAPPING = { - "facilitypatientstatshistory__entry_date": "Entry Date", - "facilitypatientstatshistory__num_patients_visited": "Vistited Patients", - "facilitypatientstatshistory__num_patients_home_quarantine": "Home Quarantined Patients", - "facilitypatientstatshistory__num_patients_isolation": "Patients Isolated", - "facilitypatientstatshistory__num_patient_referred": "Patients Referred", - "facilitypatientstatshistory__num_patient_confirmed_positive": "Patients Confirmed Positive", - } - - CSV_MAKE_PRETTY = {} - - class Meta: - unique_together = ( - "facility", - "entry_date", - ) - - class PatientMobileOTP(BaseModel): is_used = models.BooleanField(default=False) phone_number = models.CharField( diff --git a/care/facility/models/summary.py b/care/facility/models/summary.py deleted file mode 100644 index 2cd1071bad..0000000000 --- a/care/facility/models/summary.py +++ /dev/null @@ -1,120 +0,0 @@ -from uuid import uuid4 - -from django.db import models -from django.db.models import JSONField - -from care.facility.models.facility import Facility -from care.users.models import District, LocalBody - -SUMMARY_CHOICES = ( - ("FacilityCapacity", "FacilityCapacity"), - ("PatientSummary", "PatientSummary"), - ("TestSummary", "TestSummary"), - ("TriageSummary", "TriageSummary"), -) - - -class FacilityRelatedSummary(models.Model): - id = models.UUIDField(primary_key=True, default=uuid4, unique=True, db_index=True) - created_date = models.DateTimeField(auto_now_add=True, null=True, blank=True) - modified_date = models.DateTimeField(auto_now=True, null=True, blank=True) - facility = models.ForeignKey( - Facility, on_delete=models.CASCADE, null=True, blank=True - ) - s_type = models.CharField(choices=SUMMARY_CHOICES, max_length=100) - data = JSONField(null=True, blank=True, default=dict) - - class Meta: - indexes = [ - models.Index( - fields=[ - "-modified_date", - ] - ), - models.Index( - fields=[ - "-created_date", - ] - ), - models.Index( - fields=[ - "s_type", - ] - ), - models.Index(fields=["-created_date", "s_type"]), - ] - - def __str__(self): - return f"FacilityRelatedSummary - {self.facility} - {self.s_type}" - - -DISTRICT_SUMMARY_CHOICES = (("PatientSummary", "PatientSummary"),) - - -class DistrictScopedSummary(models.Model): - id = models.UUIDField(primary_key=True, default=uuid4, unique=True, db_index=True) - created_date = models.DateTimeField(auto_now_add=True, null=True, blank=True) - modified_date = models.DateTimeField(auto_now=True, null=True, blank=True) - district = models.ForeignKey( - District, on_delete=models.CASCADE, null=True, blank=True - ) - s_type = models.CharField(choices=DISTRICT_SUMMARY_CHOICES, max_length=100) - data = JSONField(null=True, blank=True, default=dict) - - class Meta: - indexes = [ - models.Index( - fields=[ - "-modified_date", - ] - ), - models.Index( - fields=[ - "-created_date", - ] - ), - models.Index( - fields=[ - "s_type", - ] - ), - models.Index(fields=["-created_date", "s_type"]), - ] - - def __str__(self): - return f"DistrictScopedSummary - {self.district} - {self.s_type}" - - -LSG_SUMMARY_CHOICES = (("PatientSummary", "PatientSummary"),) - - -class LocalBodyScopedSummary(models.Model): - id = models.UUIDField(primary_key=True, default=uuid4, unique=True, db_index=True) - created_date = models.DateTimeField(auto_now_add=True, null=True, blank=True) - modified_date = models.DateTimeField(auto_now=True, null=True, blank=True) - lsg = models.ForeignKey(LocalBody, on_delete=models.CASCADE, null=True, blank=True) - s_type = models.CharField(choices=LSG_SUMMARY_CHOICES, max_length=100) - data = JSONField(null=True, blank=True, default=dict) - - class Meta: - indexes = [ - models.Index( - fields=[ - "-modified_date", - ] - ), - models.Index( - fields=[ - "-created_date", - ] - ), - models.Index( - fields=[ - "s_type", - ] - ), - models.Index(fields=["-created_date", "s_type"]), - ] - - def __str__(self): - return f"LocalBodyScopedSummary - {self.lsg} - {self.s_type}" diff --git a/care/facility/tasks/__init__.py b/care/facility/tasks/__init__.py index c86b4dbf5b..26dc090bc4 100644 --- a/care/facility/tasks/__init__.py +++ b/care/facility/tasks/__init__.py @@ -7,13 +7,6 @@ from care.facility.tasks.location_monitor import check_location_status from care.facility.tasks.plausible_stats import capture_goals from care.facility.tasks.redis_index import load_redis_index -from care.facility.tasks.summarisation import ( - summarize_district_patient, - summarize_facility_capacity, - summarize_patient, - summarize_tests, - summarize_triage, -) @current_app.on_after_finalize.connect diff --git a/care/facility/utils/summarization/__init__.py b/care/facility/utils/summarization/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/care/facility/utils/summarization/district/__init__.py b/care/facility/utils/summarization/district/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/care/facility/utils/summarization/district/patient_summary.py b/care/facility/utils/summarization/district/patient_summary.py deleted file mode 100644 index a4f20a8d37..0000000000 --- a/care/facility/utils/summarization/district/patient_summary.py +++ /dev/null @@ -1,104 +0,0 @@ -from django.db.models import Q -from django.utils.timezone import now - -from care.facility.models import DistrictScopedSummary, PatientRegistration -from care.facility.models.patient_base import BedTypeChoices -from care.users.models import District, LocalBody - - -def district_patient_summary(): - for district_object in District.objects.all(): - district_summary = { - "name": district_object.name, - "id": district_object.id, - } - for local_body_object in LocalBody.objects.filter( - district_id=district_object.id - ): - district_summary[local_body_object.id] = { - "name": local_body_object.name, - "code": local_body_object.localbody_code, - "total_inactive": PatientRegistration.objects.filter( - is_active=False, - local_body_id=local_body_object.id, - ).count(), - } - patients = PatientRegistration.objects.filter( - is_active=True, - last_consultation__discharge_date__isnull=True, - local_body_id=local_body_object.id, - ) - - # Get Total Counts - - for bed_type_choice in BedTypeChoices: - db_value, text = bed_type_choice - patient_filters = { - "last_consultation__" + "current_bed__bed__bed_type": db_value - } - count = patients.filter(**patient_filters).count() - clean_name = "total_patients_" + "_".join(text.lower().split()) - district_summary[local_body_object.id][clean_name] = count - - home_quarantine = Q(last_consultation__suggestion="HI") - - total_patients_home_quarantine = patients.filter(home_quarantine).count() - district_summary[local_body_object.id]["total_patients_home_quarantine"] = ( - total_patients_home_quarantine - ) - - # Apply Date Filters - - patients_today = patients.filter( - last_consultation__created_date__startswith=now().date() - ) - - # Get Todays Counts - - today_patients_home_quarantine = patients_today.filter( - home_quarantine - ).count() - - for bed_type_choice in BedTypeChoices: - db_value, text = bed_type_choice - patient_filters = { - "last_consultation__" + "current_bed__bed__bed_type": db_value - } - count = patients_today.filter(**patient_filters).count() - clean_name = "today_patients_" + "_".join(text.lower().split()) - district_summary[local_body_object.id][clean_name] = count - - # Update Anything Extra - district_summary[local_body_object.id]["today_patients_home_quarantine"] = ( - today_patients_home_quarantine - ) - - object_filter = Q(s_type="PatientSummary") & Q( - created_date__startswith=now().date() - ) - if ( - DistrictScopedSummary.objects.filter(district_id=district_object.id) - .filter(object_filter) - .exists() - ): - district_summary_old = DistrictScopedSummary.objects.filter( - object_filter - ).get(district_id=district_object.id) - district_summary_old.created_date = now() - district_summary_old.data.pop("modified_date") - - district_summary_old.data = district_summary - latest_modification_date = now() - district_summary_old.data.update( - {"modified_date": latest_modification_date.strftime("%d-%m-%Y %H:%M")} - ) - district_summary_old.save() - else: - modified_date = now() - district_summary["modified_date"] = modified_date.strftime("%d-%m-%Y %H:%M") - DistrictScopedSummary( - s_type="PatientSummary", - district_id=district_object.id, - data=district_summary, - ).save() - return True diff --git a/care/facility/utils/summarization/facility_capacity.py b/care/facility/utils/summarization/facility_capacity.py deleted file mode 100644 index 44df537cc6..0000000000 --- a/care/facility/utils/summarization/facility_capacity.py +++ /dev/null @@ -1,121 +0,0 @@ -from django.db.models import Sum -from django.utils.timezone import localtime, now - -from care.facility.api.serializers.facility import FacilitySerializer -from care.facility.api.serializers.facility_capacity import FacilityCapacitySerializer -from care.facility.models import ( - Facility, - FacilityCapacity, - FacilityRelatedSummary, - PatientRegistration, -) -from care.facility.models.inventory import ( - FacilityInventoryBurnRate, - FacilityInventoryLog, - FacilityInventorySummary, -) - - -def facility_capacity_summary(): - capacity_objects = FacilityCapacity.objects.all() - capacity_summary = {} - current_date = localtime(now()).replace(hour=0, minute=0, second=0, microsecond=0) - - for facility_obj in Facility.objects.all(): - # Calculate Actual Patients Discharged and Live in this Facility - patients_in_facility = PatientRegistration.objects.filter( - facility_id=facility_obj.id - ).select_related("state", "district", "local_body") - capacity_summary[facility_obj.id] = FacilitySerializer(facility_obj).data - capacity_summary[facility_obj.id]["features"] = list( - capacity_summary[facility_obj.id]["features"] - ) - capacity_summary[facility_obj.id]["actual_live_patients"] = ( - patients_in_facility.filter(is_active=True).count() - ) - discharge_patients = patients_in_facility.filter(is_active=False) - capacity_summary[facility_obj.id]["actual_discharged_patients"] = ( - discharge_patients.count() - ) - capacity_summary[facility_obj.id]["availability"] = [] - - temp_inventory_summary_obj = {} - summary_objs = FacilityInventorySummary.objects.filter( - facility_id=facility_obj.id - ) - for summary_obj in summary_objs: - burn_rate = FacilityInventoryBurnRate.objects.filter( - facility_id=facility_obj.id, item_id=summary_obj.item.id - ).first() - log_query = FacilityInventoryLog.objects.filter( - facility_id=facility_obj.id, - item_id=summary_obj.item.id, - created_date__gte=current_date, - probable_accident=False, - ) - end_log = log_query.order_by("-created_date").first() - end_stock = summary_obj.quantity - if end_log: - end_stock = end_log.current_stock - total_consumed = 0 - temp1 = log_query.filter(is_incoming=False).aggregate( - Sum("quantity_in_default_unit") - ) - if temp1: - total_consumed = temp1.get("quantity_in_default_unit__sum", 0) or 0 - total_added = 0 - temp2 = log_query.filter(is_incoming=True).aggregate( - Sum("quantity_in_default_unit") - ) - if temp2: - total_added = temp2.get("quantity_in_default_unit__sum", 0) or 0 - - start_stock = end_stock - total_added + total_consumed - - if burn_rate: - burn_rate = burn_rate.burn_rate - temp_inventory_summary_obj[summary_obj.item.id] = { - "item_name": summary_obj.item.name, - "stock": summary_obj.quantity, - "unit": summary_obj.item.default_unit.name, - "is_low": summary_obj.is_low, - "burn_rate": burn_rate, - "start_stock": start_stock, - "end_stock": end_stock, - "total_consumed": total_consumed, - "total_added": total_added, - "modified_date": summary_obj.modified_date.astimezone().isoformat(), - } - capacity_summary[facility_obj.id]["inventory"] = temp_inventory_summary_obj - - for capacity_object in capacity_objects: - facility_id = capacity_object.facility_id - if facility_id not in capacity_summary: - # This facility is either deleted or not active - continue - if "availability" not in capacity_summary[facility_id]: - capacity_summary[facility_id]["availability"] = [] - capacity_summary[facility_id]["availability"].append( - FacilityCapacitySerializer(capacity_object).data - ) - - for i in capacity_summary: - facility_summary_obj = None - if FacilityRelatedSummary.objects.filter( - s_type="FacilityCapacity", - facility_id=i, - created_date__gte=current_date, - ).exists(): - facility_summary_obj = FacilityRelatedSummary.objects.get( - s_type="FacilityCapacity", - facility_id=i, - created_date__gte=current_date, - ) - else: - facility_summary_obj = FacilityRelatedSummary( - s_type="FacilityCapacity", facility_id=i - ) - facility_summary_obj.data = capacity_summary[i] - facility_summary_obj.save() - - return True diff --git a/care/facility/utils/summarization/patient_summary.py b/care/facility/utils/summarization/patient_summary.py deleted file mode 100644 index f54f79605b..0000000000 --- a/care/facility/utils/summarization/patient_summary.py +++ /dev/null @@ -1,103 +0,0 @@ -from django.db.models import Q -from django.utils.timezone import now - -from care.facility.models import Facility, FacilityRelatedSummary, PatientRegistration -from care.facility.models.patient_base import BedTypeChoices - - -def patient_summary(): - facility_objects = Facility.objects.all() - patient_summary = {} - for facility_object in facility_objects: - facility_id = facility_object.id - if facility_id not in patient_summary: - patient_summary[facility_id] = { - "facility_name": facility_object.name, - "district": facility_object.district.name, - "facility_external_id": str(facility_object.external_id), - } - - patients = PatientRegistration.objects.filter( - is_active=True, - last_consultation__discharge_date__isnull=True, - last_consultation__facility=facility_object, - ) - - # Get Total Counts - - for bed_type_choice in BedTypeChoices: - db_value, text = bed_type_choice - patient_filters = { - "last_consultation__" + "current_bed__bed__bed_type": db_value - } - count = patients.filter(**patient_filters).count() - clean_name = "total_patients_" + "_".join(text.lower().split()) - patient_summary[facility_id][clean_name] = count - - home_quarantine = Q(last_consultation__suggestion="HI") - - total_patients_home_quarantine = patients.filter(home_quarantine).count() - patient_summary[facility_id]["total_patients_home_quarantine"] = ( - total_patients_home_quarantine - ) - - # Apply Date Filters - - patients_today = patients.filter( - last_consultation__created_date__startswith=now().date() - ) - - # Get Todays Counts - - today_patients_home_quarantine = patients_today.filter( - home_quarantine - ).count() - - for bed_type_choice in BedTypeChoices: - db_value, text = bed_type_choice - patient_filters = { - "last_consultation__" + "current_bed__bed__bed_type": db_value - } - count = patients_today.filter(**patient_filters).count() - clean_name = "today_patients_" + "_".join(text.lower().split()) - patient_summary[facility_id][clean_name] = count - - # Update Anything Extra - patient_summary[facility_id]["today_patients_home_quarantine"] = ( - today_patients_home_quarantine - ) - - for i in list(patient_summary.keys()): - object_filter = Q(s_type="PatientSummary") & Q( - created_date__startswith=now().date() - ) - if ( - FacilityRelatedSummary.objects.filter(facility_id=i) - .filter(object_filter) - .exists() - ): - facility = FacilityRelatedSummary.objects.filter(object_filter).get( - facility_id=i - ) - facility.created_date = now() - facility.data.pop("modified_date") - if facility.data != patient_summary[i]: - facility.data = patient_summary[i] - latest_modification_date = now() - facility.data.update( - { - "modified_date": latest_modification_date.strftime( - "%d-%m-%Y %H:%M" - ) - } - ) - facility.save() - else: - modified_date = now() - patient_summary[i].update( - {"modified_date": modified_date.strftime("%d-%m-%Y %H:%M")} - ) - FacilityRelatedSummary( - s_type="PatientSummary", facility_id=i, data=patient_summary[i] - ).save() - return True diff --git a/care/facility/utils/summarization/tests_summary.py b/care/facility/utils/summarization/tests_summary.py deleted file mode 100644 index a9da8c8a61..0000000000 --- a/care/facility/utils/summarization/tests_summary.py +++ /dev/null @@ -1,64 +0,0 @@ -from django.core.exceptions import ObjectDoesNotExist -from django.utils import timezone - -from care.facility.models import Facility, FacilityRelatedSummary, PatientSample - - -def tests_summary(): - facilities = Facility.objects.all() - for facility in facilities: - facility_total_patients_count = ( - facility.consultations.all().distinct("patient_id").count() - ) - facility_patients_samples = PatientSample.objects.filter( - consultation__facility_id=facility.id - ) - total_tests_count = facility_patients_samples.count() - results_positive_count = facility_patients_samples.filter( - result=PatientSample.SAMPLE_TEST_RESULT_MAP["POSITIVE"] - ).count() - results_awaited_count = facility_patients_samples.filter( - result=PatientSample.SAMPLE_TEST_RESULT_MAP["AWAITING"] - ).count() - results_negative_count = facility_patients_samples.filter( - result=PatientSample.SAMPLE_TEST_RESULT_MAP["NEGATIVE"] - ).count() - test_discarded_count = facility_patients_samples.filter( - result=PatientSample.SAMPLE_TEST_RESULT_MAP["INVALID"] - ).count() - facility_tests_summarised_data = { - "facility_name": facility.name, - "district": facility.district.name, - "total_patients": facility_total_patients_count, - "total_tests": total_tests_count, - "result_positive": results_positive_count, - "result_awaited": results_awaited_count, - "result_negative": results_negative_count, - "test_discarded": test_discarded_count, - } - - try: - facility_test_summary = FacilityRelatedSummary.objects.get( - s_type="TestSummary", - created_date__startswith=timezone.now().date(), - facility=facility, - ) - facility_test_summary.created_date = timezone.now() - facility_test_summary.data.pop("modified_date") - if facility_test_summary.data != facility_tests_summarised_data: - facility_test_summary.data = facility_tests_summarised_data - latest_modification_date = timezone.now() - facility_test_summary.data["modified_date"] = ( - latest_modification_date.strftime("%d-%m-%Y %H:%M") - ) - facility_test_summary.save() - except ObjectDoesNotExist: - modified_date = timezone.now() - facility_tests_summarised_data["modified_date"] = modified_date.strftime( - "%d-%m-%Y %H:%M" - ) - FacilityRelatedSummary.objects.create( - s_type="TestSummary", - facility=facility, - data=facility_tests_summarised_data, - ) diff --git a/care/facility/utils/summarization/triage_summary.py b/care/facility/utils/summarization/triage_summary.py deleted file mode 100644 index df5f4a296c..0000000000 --- a/care/facility/utils/summarization/triage_summary.py +++ /dev/null @@ -1,85 +0,0 @@ -from django.db.models import Count, Sum -from django.utils.timezone import localtime, now - -from care.facility.models import ( - Facility, - FacilityPatientStatsHistory, - FacilityRelatedSummary, -) - - -def triage_summary(): - facilities = Facility.objects.all() - current_date = localtime(now()).replace(hour=0, minute=0, second=0, microsecond=0) - for facility in facilities: - facility_patient_data = FacilityPatientStatsHistory.objects.filter( - facility=facility - ).aggregate( - total_patients_visited=Sum("num_patients_visited"), - total_patients_home_quarantine=Sum("num_patients_home_quarantine"), - total_patients_isolation=Sum("num_patients_isolation"), - total_patients_referred=Sum("num_patient_referred"), - total_patients_confirmed_positive=Sum("num_patient_confirmed_positive"), - total_count=Count("id"), - ) - total_count = facility_patient_data.get("total_count", 0) - total_patients_home_quarantine = facility_patient_data.get( - "total_patients_home_quarantine", 0 - ) - total_patients_referred = facility_patient_data.get( - "total_patients_referred", 0 - ) - total_patients_isolation = facility_patient_data.get( - "total_patients_visited", 0 - ) - total_patients_visited = facility_patient_data.get( - "total_patients_isolation", 0 - ) - total_patients_confirmed_positive = facility_patient_data.get( - "num_patient_confirmed_positive", 0 - ) - if total_count: - avg_patients_home_quarantine = int( - total_patients_home_quarantine / total_count - ) - avg_patients_referred = int(total_patients_referred / total_count) - avg_patients_isolation = int(total_patients_isolation / total_count) - avg_patients_visited = int(total_patients_visited / total_count) - avg_patients_confirmed_positive = int( - total_patients_confirmed_positive / total_count - ) - else: - avg_patients_home_quarantine = 0 - avg_patients_referred = 0 - avg_patients_isolation = 0 - avg_patients_visited = 0 - avg_patients_confirmed_positive = 0 - - facility_triage_summarised_data = { - "facility_name": facility.name, - "district": facility.district.name, - "total_patients_home_quarantine": total_patients_home_quarantine, - "total_patients_referred": total_patients_referred, - "total_patients_isolation": total_patients_isolation, - "total_patients_visited": total_patients_visited, - "total_patients_confirmed_positive": total_patients_confirmed_positive, - "avg_patients_home_quarantine": avg_patients_home_quarantine, - "avg_patients_referred": avg_patients_referred, - "avg_patients_isolation": avg_patients_isolation, - "avg_patients_visited": avg_patients_visited, - "avg_patients_confirmed_positive": avg_patients_confirmed_positive, - } - - facility_triage_summary = FacilityRelatedSummary.objects.filter( - s_type="TriageSummary", facility=facility, created_date__gte=current_date - ).first() - - if facility_triage_summary: - facility_triage_summary.data = facility_triage_summarised_data - else: - facility_triage_summary = FacilityRelatedSummary( - s_type="TriageSummary", - facility=facility, - data=facility_triage_summarised_data, - ) - facility_triage_summary.save() diff --git a/config/api_router.py b/config/api_router.py index ab2cdc141f..dc41ea0cb8 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -53,7 +53,6 @@ from care.facility.api.viewsets.notification import NotificationViewSet from care.facility.api.viewsets.patient import ( FacilityDischargedPatientViewSet, - FacilityPatientStatsHistoryViewSet, PatientNotesEditViewSet, PatientNotesViewSet, PatientSearchViewSet, @@ -157,11 +156,6 @@ facility_nested_router.register( r"capacity", FacilityCapacityViewSet, basename="facility-capacity" ) -facility_nested_router.register( - r"patient_stats", - FacilityPatientStatsHistoryViewSet, - basename="facility-patient-stats", -) facility_nested_router.register( r"inventory", FacilityInventoryLogViewSet, basename="facility-inventory" ) From 20cd5cd978473f341d491c4b42d2c9911a9e53d0 Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Sun, 15 Dec 2024 23:35:47 +0530 Subject: [PATCH 11/17] Remove unused FacilityPatientStatsHistoryFilterSet --- care/facility/api/viewsets/patient.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/care/facility/api/viewsets/patient.py b/care/facility/api/viewsets/patient.py index 1028a752b1..7b023c1531 100644 --- a/care/facility/api/viewsets/patient.py +++ b/care/facility/api/viewsets/patient.py @@ -728,10 +728,6 @@ def get_queryset(self) -> QuerySet: ) -class FacilityPatientStatsHistoryFilterSet(filters.FilterSet): - entry_date = filters.DateFromToRangeFilter(field_name="entry_date") - - class PatientSearchSetPagination(PageNumberPagination): page_size = 200 From b2f2e7e2b2a1c27d27ca06375252cf8600e849d8 Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Mon, 16 Dec 2024 18:07:29 +0530 Subject: [PATCH 12/17] update gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index a8d4103c3d..60867c8af4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ * text=auto scripts/* text eol=lf +docker/minio/* text eol=lf From c1cc9d0037f6da244218eea7bd9d10e532da8d9b Mon Sep 17 00:00:00 2001 From: tatyabicchu Date: Tue, 17 Dec 2024 15:04:12 +0530 Subject: [PATCH 13/17] Fixes #2657 Stops and removes the containers and their volumes. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 12d25fc83b..46e7af4084 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,9 @@ up: down: docker compose -f docker-compose.yaml -f $(docker_config_file) down +teardown: + docker compose -f docker-compose.yaml -f $(docker_config_file) down -v + load-dummy-data: docker compose exec backend bash -c "python manage.py load_dummy_data" From ea14899a150017bbe88a1a5ac2cfc9f40ae2dc5a Mon Sep 17 00:00:00 2001 From: tatyabicchu Date: Tue, 17 Dec 2024 15:11:26 +0530 Subject: [PATCH 14/17] updated readme file for clarity --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 17455a29ea..ed20fe7431 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,16 @@ to load dummy data for testing run: ```bash make load-dummy-data ``` +Stops and removes the containers without affecting the volumes: + +```bash +make down +``` +Stops and removes the containers and their volumes: + +```bash +make teardown +``` #### Docker From fd98e432fdd5e97a21b68aad02f4a478b6b33a96 Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Wed, 18 Dec 2024 14:13:14 +0530 Subject: [PATCH 15/17] Update .gitattributes for shell scripts --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 60867c8af4..0998956ddf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,2 @@ * text=auto -scripts/* text eol=lf -docker/minio/* text eol=lf +*.sh text eol=lf From dec64751e6af816627f9d47e43fdb8717134930f Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Sun, 22 Dec 2024 23:29:05 +0530 Subject: [PATCH 16/17] Update gitignore for more celerybeat schedule files --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 84a84d610f..4723ff931c 100644 --- a/.gitignore +++ b/.gitignore @@ -344,7 +344,7 @@ care/media/* test_db -celerybeat-schedule +celerybeat-schedule* secrets.sh From e7423f25073b091d5e84e1b73d913f820dffdcae Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Thu, 26 Dec 2024 11:15:30 +0530 Subject: [PATCH 17/17] Fix lint --- .gitattributes | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 0998956ddf..efdba87644 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ * text=auto -*.sh text eol=lf +*.sh text eol=lf diff --git a/Makefile b/Makefile index 46e7af4084..64008da21b 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ down: teardown: docker compose -f docker-compose.yaml -f $(docker_config_file) down -v - + load-dummy-data: docker compose exec backend bash -c "python manage.py load_dummy_data"