From c68f64b69c7e7fb10d2722f0fdb0a90973b7e5cd Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Thu, 13 Jul 2023 15:05:55 +0530
Subject: [PATCH 01/16] Rename Coronasafe to OHC
---
care/facility/api/serializers/patient_otp.py | 2 +-
care/templates/base.html | 2 +-
care/templates/email/user_reset_password.html | 2 +-
care/templates/email/user_reset_password.txt | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/care/facility/api/serializers/patient_otp.py b/care/facility/api/serializers/patient_otp.py
index 9d6951e883..7457ac8454 100644
--- a/care/facility/api/serializers/patient_otp.py
+++ b/care/facility/api/serializers/patient_otp.py
@@ -26,7 +26,7 @@ def send_sms(otp, phone_number):
sendSMS(
phone_number,
(
- f"CoronaSafe Network Patient Management System Login, OTP is {otp} . "
+ f"Open Healthcare Network Patient Management System Login, OTP is {otp} . "
"Please do not share this Confidential Login Token with anyone else"
),
)
diff --git a/care/templates/base.html b/care/templates/base.html
index 9270456239..53b13042d1 100644
--- a/care/templates/base.html
+++ b/care/templates/base.html
@@ -104,7 +104,7 @@
alt="Digital Public Goods logo" />
- CoronaSafe Network is an open-source digital public good designed by
+ Open Healthcare Network is an open-source digital public good designed by
a multi-disciplinary team of innovators and volunteers who are working on a model to support
Government efforts.
(Github)
diff --git a/care/templates/email/user_reset_password.html b/care/templates/email/user_reset_password.html
index 7c5e29a087..8eb0100c40 100644
--- a/care/templates/email/user_reset_password.html
+++ b/care/templates/email/user_reset_password.html
@@ -1,5 +1,5 @@
Hi,
-Greetings from Coronasafe Network,
+Greetings from Open Healthcare Network,
Please click the following link to reset your password for your account with username {{username}}
Click Here
diff --git a/care/templates/email/user_reset_password.txt b/care/templates/email/user_reset_password.txt
index df328882a5..2a2e8a703f 100644
--- a/care/templates/email/user_reset_password.txt
+++ b/care/templates/email/user_reset_password.txt
@@ -1,5 +1,5 @@
Hi,
-Greetings from Coronasafe Network,
+Greetings from Open Healthcare Network,
Please click the following link to reset your password
{{reset_password_url}}
From 3de195418ae6b4cada34c09c315b523628869841 Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Fri, 14 Jul 2023 10:58:14 +0530
Subject: [PATCH 02/16] Store asset uptime
---
care/facility/models/asset.py | 22 ++++++++++++++++
care/facility/tasks/asset_monitor.py | 38 ++++++++++++++++++++++++++++
2 files changed, 60 insertions(+)
create mode 100644 care/facility/tasks/asset_monitor.py
diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py
index ac14fba3a0..ccae9b4108 100644
--- a/care/facility/models/asset.py
+++ b/care/facility/models/asset.py
@@ -105,6 +105,28 @@ def __str__(self):
return self.name
+class AssetAvailabilityRecord(BaseModel):
+ class AvailabilityStatus(enum.Enum):
+ NOT_MONITORED = 0
+ OPERATIONAL = 1
+ DOWN = 2
+ UNDER_MAINTENANCE = 3
+
+ AvailabilityStatusChoices = [(e.value, e.name) for e in AvailabilityStatus]
+
+ asset = models.ForeignKey(Asset, on_delete=models.PROTECT, null=False, blank=False)
+ status = models.IntegerField(
+ choices=AvailabilityStatusChoices, default=AvailabilityStatus.UNKNOWN
+ )
+ timestamp = models.DateTimeField(null=False, blank=False)
+
+ class Meta:
+ ordering = ["-timestamp"]
+
+ def __str__(self):
+ return f"{self.asset.name} - {self.status} - {self.time}"
+
+
class UserDefaultAssetLocation(BaseModel):
user = models.ForeignKey(User, on_delete=models.PROTECT, null=False, blank=False)
location = models.ForeignKey(
diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py
new file mode 100644
index 0000000000..959be7b564
--- /dev/null
+++ b/care/facility/tasks/asset_monitor.py
@@ -0,0 +1,38 @@
+from datetime import datetime
+
+from celery import shared_task
+
+from care.facility.models.asset import Asset, AssetAvailabilityRecord
+from care.utils.assetintegration.asset_classes import AssetClasses
+from care.utils.assetintegration.base import BaseAssetIntegration
+
+
+@shared_task
+def check_asset_status():
+ assets = Asset.objects.filter(is_working=True)
+
+ for asset in assets:
+ asset_class: BaseAssetIntegration = AssetClasses[asset.asset_class].value(
+ {
+ **asset.meta,
+ "middleware_hostname": asset.current_location.facility.middleware_address,
+ }
+ )
+ result = asset_class.api_get(asset_class.get_url("status"))
+
+ if result and result.get("status"):
+ status = result.get("status", "-1")
+ if status == "-1":
+ continue
+
+ new_status = AssetAvailabilityRecord.AvailabilityStatus(int(status))
+ last_record = (
+ AssetAvailabilityRecord.objects.filter(asset=asset)
+ .order_by("-timestamp")
+ .first()
+ )
+
+ if not last_record or last_record.status != new_status.value:
+ AssetAvailabilityRecord.objects.create(
+ asset=asset, status=new_status.value, timestamp=datetime.now()
+ )
From bbe249b8c84c2300d0fa274b79c36fe920f81e11 Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Fri, 14 Jul 2023 18:37:32 +0530
Subject: [PATCH 03/16] Adapt to new API format
---
.../0371_assetavailabilityrecord.py | 64 ++++++++++++++++
care/facility/models/asset.py | 10 +--
care/facility/tasks/__init__.py | 14 +++-
care/facility/tasks/asset_monitor.py | 74 ++++++++++++++-----
care/utils/assetintegration/asset_statuses.py | 8 ++
5 files changed, 140 insertions(+), 30 deletions(-)
create mode 100644 care/facility/migrations/0371_assetavailabilityrecord.py
create mode 100644 care/utils/assetintegration/asset_statuses.py
diff --git a/care/facility/migrations/0371_assetavailabilityrecord.py b/care/facility/migrations/0371_assetavailabilityrecord.py
new file mode 100644
index 0000000000..09c68419ea
--- /dev/null
+++ b/care/facility/migrations/0371_assetavailabilityrecord.py
@@ -0,0 +1,64 @@
+# Generated by Django 4.2.2 on 2023-07-14 12:32
+
+import uuid
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("facility", "0370_merge_20230705_1500"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="AssetAvailabilityRecord",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "external_id",
+ models.UUIDField(db_index=True, default=uuid.uuid4, unique=True),
+ ),
+ (
+ "created_date",
+ models.DateTimeField(auto_now_add=True, db_index=True, null=True),
+ ),
+ (
+ "modified_date",
+ models.DateTimeField(auto_now=True, db_index=True, null=True),
+ ),
+ ("deleted", models.BooleanField(db_index=True, default=False)),
+ (
+ "status",
+ models.IntegerField(
+ choices=[
+ (0, "NOT_MONITORED"),
+ (1, "OPERATIONAL"),
+ (2, "DOWN"),
+ (3, "UNDER_MAINTENANCE"),
+ ],
+ default=0,
+ ),
+ ),
+ ("timestamp", models.DateTimeField()),
+ (
+ "asset",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.PROTECT, to="facility.asset"
+ ),
+ ),
+ ],
+ options={
+ "ordering": ["-timestamp"],
+ },
+ ),
+ ]
diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py
index ccae9b4108..2173c2054b 100644
--- a/care/facility/models/asset.py
+++ b/care/facility/models/asset.py
@@ -9,6 +9,7 @@
from care.facility.models.mixins.permissions.asset import AssetsPermissionMixin
from care.users.models import User, phone_number_regex_11
from care.utils.assetintegration.asset_classes import AssetClasses
+from care.utils.assetintegration.asset_statuses import AvailabilityStatus
from care.utils.models.base import BaseModel
from care.utils.models.validators import JSONFieldSchemaValidator
@@ -106,17 +107,12 @@ def __str__(self):
class AssetAvailabilityRecord(BaseModel):
- class AvailabilityStatus(enum.Enum):
- NOT_MONITORED = 0
- OPERATIONAL = 1
- DOWN = 2
- UNDER_MAINTENANCE = 3
-
AvailabilityStatusChoices = [(e.value, e.name) for e in AvailabilityStatus]
asset = models.ForeignKey(Asset, on_delete=models.PROTECT, null=False, blank=False)
status = models.IntegerField(
- choices=AvailabilityStatusChoices, default=AvailabilityStatus.UNKNOWN
+ choices=AvailabilityStatusChoices,
+ default=AvailabilityStatus.NOT_MONITORED.value,
)
timestamp = models.DateTimeField(null=False, blank=False)
diff --git a/care/facility/tasks/__init__.py b/care/facility/tasks/__init__.py
index 7ebf63cdaa..1a9383d32d 100644
--- a/care/facility/tasks/__init__.py
+++ b/care/facility/tasks/__init__.py
@@ -1,6 +1,7 @@
from celery import current_app
from celery.schedules import crontab
+from care.facility.tasks.asset_monitor import check_asset_status
from care.facility.tasks.cleanup import delete_old_notifications
from care.facility.tasks.summarisation import (
summarise_district_patient,
@@ -19,12 +20,12 @@ def setup_periodic_tasks(sender, **kwargs):
name="delete_old_notifications",
)
sender.add_periodic_task(
- crontab(hour="*/4", minute=59),
+ crontab(hour="*/4", minute="59"),
summarise_triage.s(),
name="summarise_triage",
)
sender.add_periodic_task(
- crontab(hour=23, minute=59),
+ crontab(hour="23", minute="59"),
summarise_tests.s(),
name="summarise_tests",
)
@@ -34,12 +35,17 @@ def setup_periodic_tasks(sender, **kwargs):
name="summarise_facility_capacity",
)
sender.add_periodic_task(
- crontab(hour="*/1", minute=59),
+ crontab(hour="*/1", minute="59"),
summarise_patient.s(),
name="summarise_patient",
)
sender.add_periodic_task(
- crontab(hour="*/1", minute=59),
+ crontab(hour="*/1", minute="59"),
summarise_district_patient.s(),
name="summarise_district_patient",
)
+ sender.add_periodic_task(
+ crontab(minute="*/30"),
+ check_asset_status.s(),
+ name="check_asset_status",
+ )
diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py
index 959be7b564..ce59ce120f 100644
--- a/care/facility/tasks/asset_monitor.py
+++ b/care/facility/tasks/asset_monitor.py
@@ -1,38 +1,74 @@
from datetime import datetime
+from typing import Any
from celery import shared_task
from care.facility.models.asset import Asset, AssetAvailabilityRecord
from care.utils.assetintegration.asset_classes import AssetClasses
+from care.utils.assetintegration.asset_statuses import AvailabilityStatus
from care.utils.assetintegration.base import BaseAssetIntegration
@shared_task
def check_asset_status():
- assets = Asset.objects.filter(is_working=True)
+ print("Checking Asset Status", datetime.now())
+ assets = Asset.objects.all()
+ middleware_status_cache = {}
for asset in assets:
+ if not asset.asset_class or not asset.meta.get("local_ip_address", None):
+ continue
+ hostname = asset.meta.get(
+ "middleware_hostname",
+ asset.current_location.facility.middleware_address,
+ )
asset_class: BaseAssetIntegration = AssetClasses[asset.asset_class].value(
{
**asset.meta,
- "middleware_hostname": asset.current_location.facility.middleware_address,
+ "middleware_hostname": hostname,
}
)
- result = asset_class.api_get(asset_class.get_url("status"))
-
- if result and result.get("status"):
- status = result.get("status", "-1")
- if status == "-1":
- continue
-
- new_status = AssetAvailabilityRecord.AvailabilityStatus(int(status))
- last_record = (
- AssetAvailabilityRecord.objects.filter(asset=asset)
- .order_by("-timestamp")
- .first()
- )
-
- if not last_record or last_record.status != new_status.value:
- AssetAvailabilityRecord.objects.create(
- asset=asset, status=new_status.value, timestamp=datetime.now()
+ try:
+ result: Any = {}
+
+ if hostname in middleware_status_cache:
+ result = middleware_status_cache[hostname]
+ else:
+ result = asset_class.api_get(asset_class.get_url("/devices/status"))
+ middleware_status_cache[hostname] = result
+
+ new_status = None
+ for status_record in result:
+ if asset.meta.get("local_ip_address") in status_record.get(
+ "status", {}
+ ):
+ new_status = status_record["status"][
+ asset.meta.get("local_ip_address")
+ ]
+ else:
+ new_status = "not_monitored"
+
+ last_record = (
+ AssetAvailabilityRecord.objects.filter(asset=asset)
+ .order_by("-timestamp")
+ .first()
)
+
+ if new_status == "up":
+ new_status = AvailabilityStatus.OPERATIONAL
+ elif new_status == "down":
+ new_status = AvailabilityStatus.DOWN
+ elif new_status == "maintenance":
+ new_status = AvailabilityStatus.UNDER_MAINTENANCE
+ else:
+ new_status = AvailabilityStatus.NOT_MONITORED
+
+ if not last_record or last_record.status != new_status.value:
+ AssetAvailabilityRecord.objects.create(
+ asset=asset,
+ status=new_status.value,
+ timestamp=status_record.get("time", datetime.now()),
+ )
+
+ except Exception as e:
+ print("Error in Asset Status Check", e)
diff --git a/care/utils/assetintegration/asset_statuses.py b/care/utils/assetintegration/asset_statuses.py
new file mode 100644
index 0000000000..8a068df865
--- /dev/null
+++ b/care/utils/assetintegration/asset_statuses.py
@@ -0,0 +1,8 @@
+import enum
+
+
+class AvailabilityStatus(enum.Enum):
+ NOT_MONITORED = 0
+ OPERATIONAL = 1
+ DOWN = 2
+ UNDER_MAINTENANCE = 3
From f78b5fc632cfe3a90d327bac11b1019b50181b97 Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Fri, 14 Jul 2023 19:13:03 +0530
Subject: [PATCH 04/16] Add endpoint to fetch availability records
---
care/facility/api/serializers/asset.py | 11 +++++++++++
care/facility/api/viewsets/asset.py | 13 +++++++++++++
config/api_router.py | 2 ++
3 files changed, 26 insertions(+)
diff --git a/care/facility/api/serializers/asset.py b/care/facility/api/serializers/asset.py
index fbb9f44970..fe47aa6076 100644
--- a/care/facility/api/serializers/asset.py
+++ b/care/facility/api/serializers/asset.py
@@ -18,6 +18,7 @@
from care.facility.api.serializers.facility import FacilityBareMinimumSerializer
from care.facility.models.asset import (
Asset,
+ AssetAvailabilityRecord,
AssetLocation,
AssetTransaction,
UserDefaultAssetLocation,
@@ -165,6 +166,16 @@ class Meta:
exclude = ("deleted", "external_id")
+class AssetAvailabilitySerializer(ModelSerializer):
+ id = UUIDField(source="external_id", read_only=True)
+ asset = AssetBareMinimumSerializer(read_only=True)
+
+ class Meta:
+ model = AssetAvailabilityRecord
+ fields = ("asset", "status", "timestamp")
+ exclude = ("deleted", "external_id")
+
+
class UserDefaultAssetLocationSerializer(ModelSerializer):
location_object = AssetLocationSerializer(source="location", read_only=True)
diff --git a/care/facility/api/viewsets/asset.py b/care/facility/api/viewsets/asset.py
index 57ec16da51..2b5fc6e6b1 100644
--- a/care/facility/api/viewsets/asset.py
+++ b/care/facility/api/viewsets/asset.py
@@ -23,6 +23,7 @@
from rest_framework.viewsets import GenericViewSet
from care.facility.api.serializers.asset import (
+ AssetAvailabilitySerializer,
AssetLocationSerializer,
AssetSerializer,
AssetTransactionSerializer,
@@ -32,6 +33,7 @@
)
from care.facility.models.asset import (
Asset,
+ AssetAvailabilityRecord,
AssetLocation,
AssetTransaction,
UserDefaultAssetLocation,
@@ -128,6 +130,17 @@ def retrieve(self, request, *args, **kwargs):
return Response(hit)
+class AssetAvailabilityFilter(filters.FilterSet):
+ external_id = filters.CharFilter(field_name="asset__external_id")
+
+
+class AssetAvailabilityViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
+ queryset = AssetAvailabilityRecord.objects.all().select_related("asset")
+ serializer_class = AssetAvailabilitySerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filterset_class = AssetAvailabilityFilter
+
+
class AssetViewSet(
ListModelMixin,
RetrieveModelMixin,
diff --git a/config/api_router.py b/config/api_router.py
index 918ae39302..13d85f0da2 100644
--- a/config/api_router.py
+++ b/config/api_router.py
@@ -8,6 +8,7 @@
AmbulanceViewSet,
)
from care.facility.api.viewsets.asset import (
+ AssetAvailabilityViewSet,
AssetLocationViewSet,
AssetPublicViewSet,
AssetTransactionViewSet,
@@ -185,6 +186,7 @@
router.register("asset", AssetViewSet)
router.register("asset_transaction", AssetTransactionViewSet)
+router.register("asset_availability", AssetAvailabilityViewSet)
patient_nested_router = NestedSimpleRouter(router, r"patient", lookup="patient")
patient_nested_router.register(r"test_sample", PatientSampleViewSet)
From 5f5efaf8b55acd1fc390a54181e61df95607e3f5 Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Fri, 14 Jul 2023 19:13:18 +0530
Subject: [PATCH 05/16] Merge migration conflict
---
care/facility/migrations/0372_merge_20230714_1912.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 care/facility/migrations/0372_merge_20230714_1912.py
diff --git a/care/facility/migrations/0372_merge_20230714_1912.py b/care/facility/migrations/0372_merge_20230714_1912.py
new file mode 100644
index 0000000000..a8a2c5d409
--- /dev/null
+++ b/care/facility/migrations/0372_merge_20230714_1912.py
@@ -0,0 +1,12 @@
+# Generated by Django 4.2.2 on 2023-07-14 13:42
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("facility", "0371_assetavailabilityrecord"),
+ ("facility", "0371_metaicd11diagnosis_chapter_and_more"),
+ ]
+
+ operations = []
From 3d3aec1cca4acb0a3bfffff5472c2f2a6bec216f Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Mon, 17 Jul 2023 16:53:32 +0530
Subject: [PATCH 06/16] Fix crash when running celery task for asset uptime
---
care/facility/api/serializers/asset.py | 1 -
...alter_assetavailabilityrecord_timestamp.py | 17 +++++++++
...etavailabilityrecord_timestamp_and_more.py | 21 ++++++++++
care/facility/models/asset.py | 14 ++++++-
care/facility/tasks/asset_monitor.py | 38 ++++++++++++-------
care/utils/assetintegration/base.py | 4 +-
tests/test_facility_tasks.py | 14 +++++++
7 files changed, 92 insertions(+), 17 deletions(-)
create mode 100644 care/facility/migrations/0373_alter_assetavailabilityrecord_timestamp.py
create mode 100644 care/facility/migrations/0374_alter_assetavailabilityrecord_timestamp_and_more.py
create mode 100644 tests/test_facility_tasks.py
diff --git a/care/facility/api/serializers/asset.py b/care/facility/api/serializers/asset.py
index fe47aa6076..7e672c4a01 100644
--- a/care/facility/api/serializers/asset.py
+++ b/care/facility/api/serializers/asset.py
@@ -172,7 +172,6 @@ class AssetAvailabilitySerializer(ModelSerializer):
class Meta:
model = AssetAvailabilityRecord
- fields = ("asset", "status", "timestamp")
exclude = ("deleted", "external_id")
diff --git a/care/facility/migrations/0373_alter_assetavailabilityrecord_timestamp.py b/care/facility/migrations/0373_alter_assetavailabilityrecord_timestamp.py
new file mode 100644
index 0000000000..a6737ebdaa
--- /dev/null
+++ b/care/facility/migrations/0373_alter_assetavailabilityrecord_timestamp.py
@@ -0,0 +1,17 @@
+# Generated by Django 4.2.2 on 2023-07-17 06:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("facility", "0372_merge_20230714_1912"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="assetavailabilityrecord",
+ name="timestamp",
+ field=models.DateTimeField(unique=True),
+ ),
+ ]
diff --git a/care/facility/migrations/0374_alter_assetavailabilityrecord_timestamp_and_more.py b/care/facility/migrations/0374_alter_assetavailabilityrecord_timestamp_and_more.py
new file mode 100644
index 0000000000..a920527d1d
--- /dev/null
+++ b/care/facility/migrations/0374_alter_assetavailabilityrecord_timestamp_and_more.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.2 on 2023-07-17 06:53
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("facility", "0373_alter_assetavailabilityrecord_timestamp"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="assetavailabilityrecord",
+ name="timestamp",
+ field=models.DateTimeField(),
+ ),
+ migrations.AlterUniqueTogether(
+ name="assetavailabilityrecord",
+ unique_together={("asset", "timestamp")},
+ ),
+ ]
diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py
index 2173c2054b..3b3fe4cc27 100644
--- a/care/facility/models/asset.py
+++ b/care/facility/models/asset.py
@@ -107,6 +107,17 @@ def __str__(self):
class AssetAvailabilityRecord(BaseModel):
+ """
+ Model to store the availability status of an asset at a particular timestamp.
+
+ Fields:
+ - asset: ForeignKey to Asset model
+ - status: IntegerField with choices from AvailabilityStatus enum
+ - timestamp: DateTimeField to store the timestamp of the availability record
+
+ Note: A pair of asset and timestamp together should be unique, not just the timestamp alone.
+ """
+
AvailabilityStatusChoices = [(e.value, e.name) for e in AvailabilityStatus]
asset = models.ForeignKey(Asset, on_delete=models.PROTECT, null=False, blank=False)
@@ -117,10 +128,11 @@ class AssetAvailabilityRecord(BaseModel):
timestamp = models.DateTimeField(null=False, blank=False)
class Meta:
+ unique_together = (("asset", "timestamp"),)
ordering = ["-timestamp"]
def __str__(self):
- return f"{self.asset.name} - {self.status} - {self.time}"
+ return f"{self.asset.name} - {self.status} - {self.timestamp}"
class UserDefaultAssetLocation(BaseModel):
diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py
index ce59ce120f..3c83cf6a06 100644
--- a/care/facility/tasks/asset_monitor.py
+++ b/care/facility/tasks/asset_monitor.py
@@ -18,24 +18,32 @@ def check_asset_status():
for asset in assets:
if not asset.asset_class or not asset.meta.get("local_ip_address", None):
continue
- hostname = asset.meta.get(
- "middleware_hostname",
- asset.current_location.facility.middleware_address,
- )
- asset_class: BaseAssetIntegration = AssetClasses[asset.asset_class].value(
- {
- **asset.meta,
- "middleware_hostname": hostname,
- }
- )
try:
+ hostname = asset.meta.get(
+ "middleware_hostname",
+ asset.current_location.facility.middleware_address,
+ )
+ asset_class: BaseAssetIntegration = AssetClasses[asset.asset_class].value(
+ {
+ **asset.meta,
+ "middleware_hostname": hostname,
+ }
+ )
result: Any = {}
if hostname in middleware_status_cache:
result = middleware_status_cache[hostname]
else:
- result = asset_class.api_get(asset_class.get_url("/devices/status"))
- middleware_status_cache[hostname] = result
+ try:
+ result = asset_class.api_get(asset_class.get_url("devices/status"))
+ middleware_status_cache[hostname] = result
+ except Exception as e:
+ print("Error in Asset Status Check", e)
+ middleware_status_cache[hostname] = None
+ continue
+
+ if not result:
+ continue
new_status = None
for status_record in result:
@@ -63,7 +71,11 @@ def check_asset_status():
else:
new_status = AvailabilityStatus.NOT_MONITORED
- if not last_record or last_record.status != new_status.value:
+ if not last_record or (
+ datetime.fromisoformat(status_record.get("time"))
+ > last_record.timestamp
+ and last_record.status != new_status.value
+ ):
AssetAvailabilityRecord.objects.create(
asset=asset,
status=new_status.value,
diff --git a/care/utils/assetintegration/base.py b/care/utils/assetintegration/base.py
index f703c277ac..92d318c3a5 100644
--- a/care/utils/assetintegration/base.py
+++ b/care/utils/assetintegration/base.py
@@ -46,9 +46,9 @@ def api_get(self, url, data=None):
headers={"Authorization": (self.auth_header_type + generate_jwt())},
)
try:
- response = req.json()
if req.status_code >= 400:
- raise APIException(response, req.status_code)
+ raise APIException(req.text, req.status_code)
+ response = req.json()
return response
except json.decoder.JSONDecodeError:
return {"error": "Invalid Response"}
diff --git a/tests/test_facility_tasks.py b/tests/test_facility_tasks.py
new file mode 100644
index 0000000000..19b6639e74
--- /dev/null
+++ b/tests/test_facility_tasks.py
@@ -0,0 +1,14 @@
+from unittest.mock import patch
+
+from celery import Celery
+
+from care.facility.tasks import setup_periodic_tasks
+
+
+@patch("care.facility.tasks.asset_monitor.check_asset_status.delay")
+def test_setup_periodic_tasks(mock_check_asset_status):
+ app = Celery()
+ setup_periodic_tasks(app)
+
+ # Ensure that check_asset_status is called immediately
+ mock_check_asset_status.assert_called_once()
From 509fffaee2ff5001be3586f1dd8dd8f5159943ce Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Mon, 17 Jul 2023 21:42:18 +0530
Subject: [PATCH 07/16] Update care/facility/models/asset.py
Co-authored-by: Aakash Singh
---
care/facility/models/asset.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py
index 3b3fe4cc27..1bdc3740b1 100644
--- a/care/facility/models/asset.py
+++ b/care/facility/models/asset.py
@@ -118,11 +118,9 @@ class AssetAvailabilityRecord(BaseModel):
Note: A pair of asset and timestamp together should be unique, not just the timestamp alone.
"""
- AvailabilityStatusChoices = [(e.value, e.name) for e in AvailabilityStatus]
-
asset = models.ForeignKey(Asset, on_delete=models.PROTECT, null=False, blank=False)
status = models.IntegerField(
- choices=AvailabilityStatusChoices,
+ choices=AvailabilityStatus.choices,
default=AvailabilityStatus.NOT_MONITORED.value,
)
timestamp = models.DateTimeField(null=False, blank=False)
From 3513644d47cc7bb6d959e5b94ed2268716f77f55 Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Mon, 17 Jul 2023 21:42:34 +0530
Subject: [PATCH 08/16] Update care/facility/tasks/asset_monitor.py
Co-authored-by: Aakash Singh
---
care/facility/tasks/asset_monitor.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py
index 3c83cf6a06..0185963d89 100644
--- a/care/facility/tasks/asset_monitor.py
+++ b/care/facility/tasks/asset_monitor.py
@@ -23,18 +23,18 @@ def check_asset_status():
"middleware_hostname",
asset.current_location.facility.middleware_address,
)
- asset_class: BaseAssetIntegration = AssetClasses[asset.asset_class].value(
- {
- **asset.meta,
- "middleware_hostname": hostname,
- }
- )
result: Any = {}
if hostname in middleware_status_cache:
result = middleware_status_cache[hostname]
else:
try:
+ asset_class: BaseAssetIntegration = AssetClasses[asset.asset_class].value(
+ {
+ **asset.meta,
+ "middleware_hostname": hostname,
+ }
+ )
result = asset_class.api_get(asset_class.get_url("devices/status"))
middleware_status_cache[hostname] = result
except Exception as e:
From 6ac142dc9e8dcb6336afd5ac1d731738fcc20ccc Mon Sep 17 00:00:00 2001
From: Aakash Singh
Date: Mon, 17 Jul 2023 21:55:55 +0530
Subject: [PATCH 09/16] use IntegerChoices instead of Enum for assetstatus
---
...75_alter_assetavailabilityrecord_status.py | 25 +++++++++++++++++++
care/facility/models/asset.py | 8 +++++-
care/facility/tasks/asset_monitor.py | 11 +++++---
care/utils/assetintegration/asset_statuses.py | 8 ------
4 files changed, 40 insertions(+), 12 deletions(-)
create mode 100644 care/facility/migrations/0375_alter_assetavailabilityrecord_status.py
delete mode 100644 care/utils/assetintegration/asset_statuses.py
diff --git a/care/facility/migrations/0375_alter_assetavailabilityrecord_status.py b/care/facility/migrations/0375_alter_assetavailabilityrecord_status.py
new file mode 100644
index 0000000000..c361c64b95
--- /dev/null
+++ b/care/facility/migrations/0375_alter_assetavailabilityrecord_status.py
@@ -0,0 +1,25 @@
+# Generated by Django 4.2.2 on 2023-07-17 16:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("facility", "0374_alter_assetavailabilityrecord_timestamp_and_more"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="assetavailabilityrecord",
+ name="status",
+ field=models.IntegerField(
+ choices=[
+ (0, "Not Monitored"),
+ (1, "Operational"),
+ (2, "Down"),
+ (3, "Under Maintenance"),
+ ],
+ default=0,
+ ),
+ ),
+ ]
diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py
index 1bdc3740b1..063538f821 100644
--- a/care/facility/models/asset.py
+++ b/care/facility/models/asset.py
@@ -9,7 +9,6 @@
from care.facility.models.mixins.permissions.asset import AssetsPermissionMixin
from care.users.models import User, phone_number_regex_11
from care.utils.assetintegration.asset_classes import AssetClasses
-from care.utils.assetintegration.asset_statuses import AvailabilityStatus
from care.utils.models.base import BaseModel
from care.utils.models.validators import JSONFieldSchemaValidator
@@ -18,6 +17,13 @@ def get_random_asset_id():
return str(uuid.uuid4())
+class AvailabilityStatus(models.IntegerChoices):
+ NOT_MONITORED = 0
+ OPERATIONAL = 1
+ DOWN = 2
+ UNDER_MAINTENANCE = 3
+
+
class AssetLocation(BaseModel, AssetsPermissionMixin):
"""
This model is also used to store rooms that the assets are in, Since these rooms are mapped to
diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py
index 0185963d89..4b31c7f86b 100644
--- a/care/facility/tasks/asset_monitor.py
+++ b/care/facility/tasks/asset_monitor.py
@@ -3,9 +3,12 @@
from celery import shared_task
-from care.facility.models.asset import Asset, AssetAvailabilityRecord
+from care.facility.models.asset import (
+ Asset,
+ AssetAvailabilityRecord,
+ AvailabilityStatus,
+)
from care.utils.assetintegration.asset_classes import AssetClasses
-from care.utils.assetintegration.asset_statuses import AvailabilityStatus
from care.utils.assetintegration.base import BaseAssetIntegration
@@ -29,7 +32,9 @@ def check_asset_status():
result = middleware_status_cache[hostname]
else:
try:
- asset_class: BaseAssetIntegration = AssetClasses[asset.asset_class].value(
+ asset_class: BaseAssetIntegration = AssetClasses[
+ asset.asset_class
+ ].value(
{
**asset.meta,
"middleware_hostname": hostname,
diff --git a/care/utils/assetintegration/asset_statuses.py b/care/utils/assetintegration/asset_statuses.py
deleted file mode 100644
index 8a068df865..0000000000
--- a/care/utils/assetintegration/asset_statuses.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import enum
-
-
-class AvailabilityStatus(enum.Enum):
- NOT_MONITORED = 0
- OPERATIONAL = 1
- DOWN = 2
- UNDER_MAINTENANCE = 3
From ebd5fde0dbdc53e56f6835d72626a84d52118cef Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 18 Jul 2023 08:23:52 +0530
Subject: [PATCH 10/16] Merge migrations
---
...ord.py => 0372_assetavailabilityrecord.py} | 5 +++--
.../migrations/0372_merge_20230714_1912.py | 12 -----------
...alter_assetavailabilityrecord_timestamp.py | 17 ---------------
...etavailabilityrecord_timestamp_and_more.py | 21 -------------------
4 files changed, 3 insertions(+), 52 deletions(-)
rename care/facility/migrations/{0371_assetavailabilityrecord.py => 0372_assetavailabilityrecord.py} (91%)
delete mode 100644 care/facility/migrations/0372_merge_20230714_1912.py
delete mode 100644 care/facility/migrations/0373_alter_assetavailabilityrecord_timestamp.py
delete mode 100644 care/facility/migrations/0374_alter_assetavailabilityrecord_timestamp_and_more.py
diff --git a/care/facility/migrations/0371_assetavailabilityrecord.py b/care/facility/migrations/0372_assetavailabilityrecord.py
similarity index 91%
rename from care/facility/migrations/0371_assetavailabilityrecord.py
rename to care/facility/migrations/0372_assetavailabilityrecord.py
index 09c68419ea..5c5fc167ab 100644
--- a/care/facility/migrations/0371_assetavailabilityrecord.py
+++ b/care/facility/migrations/0372_assetavailabilityrecord.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.2 on 2023-07-14 12:32
+# Generated by Django 4.2.2 on 2023-07-18 02:53
import uuid
@@ -8,7 +8,7 @@
class Migration(migrations.Migration):
dependencies = [
- ("facility", "0370_merge_20230705_1500"),
+ ("facility", "0371_metaicd11diagnosis_chapter_and_more"),
]
operations = [
@@ -59,6 +59,7 @@ class Migration(migrations.Migration):
],
options={
"ordering": ["-timestamp"],
+ "unique_together": {("asset", "timestamp")},
},
),
]
diff --git a/care/facility/migrations/0372_merge_20230714_1912.py b/care/facility/migrations/0372_merge_20230714_1912.py
deleted file mode 100644
index a8a2c5d409..0000000000
--- a/care/facility/migrations/0372_merge_20230714_1912.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Generated by Django 4.2.2 on 2023-07-14 13:42
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("facility", "0371_assetavailabilityrecord"),
- ("facility", "0371_metaicd11diagnosis_chapter_and_more"),
- ]
-
- operations = []
diff --git a/care/facility/migrations/0373_alter_assetavailabilityrecord_timestamp.py b/care/facility/migrations/0373_alter_assetavailabilityrecord_timestamp.py
deleted file mode 100644
index a6737ebdaa..0000000000
--- a/care/facility/migrations/0373_alter_assetavailabilityrecord_timestamp.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 4.2.2 on 2023-07-17 06:26
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("facility", "0372_merge_20230714_1912"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="assetavailabilityrecord",
- name="timestamp",
- field=models.DateTimeField(unique=True),
- ),
- ]
diff --git a/care/facility/migrations/0374_alter_assetavailabilityrecord_timestamp_and_more.py b/care/facility/migrations/0374_alter_assetavailabilityrecord_timestamp_and_more.py
deleted file mode 100644
index a920527d1d..0000000000
--- a/care/facility/migrations/0374_alter_assetavailabilityrecord_timestamp_and_more.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 4.2.2 on 2023-07-17 06:53
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("facility", "0373_alter_assetavailabilityrecord_timestamp"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="assetavailabilityrecord",
- name="timestamp",
- field=models.DateTimeField(),
- ),
- migrations.AlterUniqueTogether(
- name="assetavailabilityrecord",
- unique_together={("asset", "timestamp")},
- ),
- ]
From b2f8ebae837f60f5ff8bc4b68b493ab8b4335394 Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 18 Jul 2023 08:41:00 +0530
Subject: [PATCH 11/16] Switch to string values for AvailabilityRecord
---
care/facility/models/asset.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py
index 063538f821..1447ccb470 100644
--- a/care/facility/models/asset.py
+++ b/care/facility/models/asset.py
@@ -17,11 +17,11 @@ def get_random_asset_id():
return str(uuid.uuid4())
-class AvailabilityStatus(models.IntegerChoices):
- NOT_MONITORED = 0
- OPERATIONAL = 1
- DOWN = 2
- UNDER_MAINTENANCE = 3
+class AvailabilityStatus(models.TextChoices):
+ NOT_MONITORED = "Not Monitored"
+ OPERATIONAL = "Operational"
+ DOWN = "Down"
+ UNDER_MAINTENANCE = "Under Maintenance"
class AssetLocation(BaseModel, AssetsPermissionMixin):
@@ -118,16 +118,16 @@ class AssetAvailabilityRecord(BaseModel):
Fields:
- asset: ForeignKey to Asset model
- - status: IntegerField with choices from AvailabilityStatus enum
+ - status: TextField with choices from AvailabilityStatus
- timestamp: DateTimeField to store the timestamp of the availability record
Note: A pair of asset and timestamp together should be unique, not just the timestamp alone.
"""
asset = models.ForeignKey(Asset, on_delete=models.PROTECT, null=False, blank=False)
- status = models.IntegerField(
+ status = models.TextField(
choices=AvailabilityStatus.choices,
- default=AvailabilityStatus.NOT_MONITORED.value,
+ default=AvailabilityStatus.NOT_MONITORED,
)
timestamp = models.DateTimeField(null=False, blank=False)
From cbf4e303e390d72e1f312ffc3df255cb77dff352 Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 18 Jul 2023 08:41:17 +0530
Subject: [PATCH 12/16] use logging instead of print
---
care/facility/tasks/asset_monitor.py | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py
index 4b31c7f86b..841937ec0f 100644
--- a/care/facility/tasks/asset_monitor.py
+++ b/care/facility/tasks/asset_monitor.py
@@ -1,3 +1,4 @@
+import logging
from datetime import datetime
from typing import Any
@@ -11,10 +12,14 @@
from care.utils.assetintegration.asset_classes import AssetClasses
from care.utils.assetintegration.base import BaseAssetIntegration
+logger = logging.getLogger(__name__)
+
@shared_task
def check_asset_status():
print("Checking Asset Status", datetime.now())
+ logger.info(f"Checking Asset Status: {datetime.now()}")
+
assets = Asset.objects.all()
middleware_status_cache = {}
@@ -42,8 +47,8 @@ def check_asset_status():
)
result = asset_class.api_get(asset_class.get_url("devices/status"))
middleware_status_cache[hostname] = result
- except Exception as e:
- print("Error in Asset Status Check", e)
+ except Exception:
+ logger.exception("Error in Asset Status Check - Fetching Status")
middleware_status_cache[hostname] = None
continue
@@ -87,5 +92,5 @@ def check_asset_status():
timestamp=status_record.get("time", datetime.now()),
)
- except Exception as e:
- print("Error in Asset Status Check", e)
+ except Exception:
+ logger.exception("Error in Asset Status Check")
From 9aef0f1a4df7525fb950d93a215daae4d219a419 Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 18 Jul 2023 10:32:05 +0530
Subject: [PATCH 13/16] Switch to string values
---
.../0372_assetavailabilityrecord.py | 15 +++++------
...75_alter_assetavailabilityrecord_status.py | 25 -------------------
care/facility/models/asset.py | 5 ++--
3 files changed, 11 insertions(+), 34 deletions(-)
delete mode 100644 care/facility/migrations/0375_alter_assetavailabilityrecord_status.py
diff --git a/care/facility/migrations/0372_assetavailabilityrecord.py b/care/facility/migrations/0372_assetavailabilityrecord.py
index 5c5fc167ab..2d3dc2daf4 100644
--- a/care/facility/migrations/0372_assetavailabilityrecord.py
+++ b/care/facility/migrations/0372_assetavailabilityrecord.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.2 on 2023-07-18 02:53
+# Generated by Django 4.2.2 on 2023-07-18 05:00
import uuid
@@ -39,14 +39,15 @@ class Migration(migrations.Migration):
("deleted", models.BooleanField(db_index=True, default=False)),
(
"status",
- models.IntegerField(
+ models.CharField(
choices=[
- (0, "NOT_MONITORED"),
- (1, "OPERATIONAL"),
- (2, "DOWN"),
- (3, "UNDER_MAINTENANCE"),
+ ("Not Monitored", "Not Monitored"),
+ ("Operational", "Operational"),
+ ("Down", "Down"),
+ ("Under Maintenance", "Under Maintenance"),
],
- default=0,
+ default="Not Monitored",
+ max_length=20,
),
),
("timestamp", models.DateTimeField()),
diff --git a/care/facility/migrations/0375_alter_assetavailabilityrecord_status.py b/care/facility/migrations/0375_alter_assetavailabilityrecord_status.py
deleted file mode 100644
index c361c64b95..0000000000
--- a/care/facility/migrations/0375_alter_assetavailabilityrecord_status.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 4.2.2 on 2023-07-17 16:26
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("facility", "0374_alter_assetavailabilityrecord_timestamp_and_more"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="assetavailabilityrecord",
- name="status",
- field=models.IntegerField(
- choices=[
- (0, "Not Monitored"),
- (1, "Operational"),
- (2, "Down"),
- (3, "Under Maintenance"),
- ],
- default=0,
- ),
- ),
- ]
diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py
index 1447ccb470..fdeaff4263 100644
--- a/care/facility/models/asset.py
+++ b/care/facility/models/asset.py
@@ -118,16 +118,17 @@ class AssetAvailabilityRecord(BaseModel):
Fields:
- asset: ForeignKey to Asset model
- - status: TextField with choices from AvailabilityStatus
+ - status: CharField with choices from AvailabilityStatus
- timestamp: DateTimeField to store the timestamp of the availability record
Note: A pair of asset and timestamp together should be unique, not just the timestamp alone.
"""
asset = models.ForeignKey(Asset, on_delete=models.PROTECT, null=False, blank=False)
- status = models.TextField(
+ status = models.CharField(
choices=AvailabilityStatus.choices,
default=AvailabilityStatus.NOT_MONITORED,
+ max_length=20,
)
timestamp = models.DateTimeField(null=False, blank=False)
From 0ed002de4437e5ffe63ebb9309d10a8739b0f137 Mon Sep 17 00:00:00 2001
From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 18 Jul 2023 12:06:46 +0530
Subject: [PATCH 14/16] Add tests
---
care/facility/tasks/asset_monitor.py | 7 ++-
.../tests/test_asset_availability_api.py | 56 +++++++++++++++++++
care/facility/tests/test_medibase_api.py | 16 +++---
.../tests/test_patient_daily_rounds_api.py | 2 +-
care/users/tests/test_facility_user_create.py | 4 +-
tests/test_facility_tasks.py | 14 -----
6 files changed, 71 insertions(+), 28 deletions(-)
create mode 100644 care/facility/tests/test_asset_availability_api.py
delete mode 100644 tests/test_facility_tasks.py
diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py
index 841937ec0f..6274bd3c85 100644
--- a/care/facility/tasks/asset_monitor.py
+++ b/care/facility/tasks/asset_monitor.py
@@ -3,6 +3,7 @@
from typing import Any
from celery import shared_task
+from django.utils import timezone
from care.facility.models.asset import (
Asset,
@@ -17,8 +18,8 @@
@shared_task
def check_asset_status():
- print("Checking Asset Status", datetime.now())
- logger.info(f"Checking Asset Status: {datetime.now()}")
+ print("Checking Asset Status", timezone.now())
+ logger.info(f"Checking Asset Status: {timezone.now()}")
assets = Asset.objects.all()
middleware_status_cache = {}
@@ -89,7 +90,7 @@ def check_asset_status():
AssetAvailabilityRecord.objects.create(
asset=asset,
status=new_status.value,
- timestamp=status_record.get("time", datetime.now()),
+ timestamp=status_record.get("time", timezone.now()),
)
except Exception:
diff --git a/care/facility/tests/test_asset_availability_api.py b/care/facility/tests/test_asset_availability_api.py
new file mode 100644
index 0000000000..068982d001
--- /dev/null
+++ b/care/facility/tests/test_asset_availability_api.py
@@ -0,0 +1,56 @@
+from django.utils import timezone
+from rest_framework import status
+from rest_framework.test import APIRequestFactory, APITestCase
+
+from care.facility.api.viewsets.asset import AssetAvailabilityViewSet
+from care.facility.models import Asset, AssetAvailabilityRecord, AssetLocation
+from care.facility.models.asset import AvailabilityStatus
+from care.facility.tests.mixins import TestClassMixin
+from care.utils.tests.test_base import TestBase
+
+
+class AssetAvailabilityViewSetTestCase(TestBase, TestClassMixin, APITestCase):
+ def setUp(self):
+ self.factory = APIRequestFactory()
+ state = self.create_state()
+ district = self.create_district(state=state)
+ self.user = self.create_user(district=district, username="test user")
+ facility = self.create_facility(district=district, user=self.user)
+ self.asset_from_location = AssetLocation.objects.create(
+ name="asset from location", location_type=1, facility=facility
+ )
+ self.asset_to_location = AssetLocation.objects.create(
+ name="asset to location", location_type=1, facility=facility
+ )
+ self.asset = Asset.objects.create(
+ name="Test Asset", current_location=self.asset_from_location, asset_type=50
+ )
+
+ self.asset_availability = AssetAvailabilityRecord.objects.create(
+ asset=self.asset,
+ status=AvailabilityStatus.OPERATIONAL.value,
+ timestamp=timezone.now(),
+ )
+
+ def test_list_asset_availability(self):
+ response = self.new_request(
+ ("/api/v1/asset_availability/",),
+ {"get": "list"},
+ AssetAvailabilityViewSet,
+ True,
+ )
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(
+ response.data["results"][0]["status"], AvailabilityStatus.OPERATIONAL.value
+ )
+
+ def test_retrieve_asset_availability(self):
+ response = self.new_request(
+ (f"/api/v1/asset_availability/{self.asset_availability.id}/",),
+ {"get": "retrieve"},
+ AssetAvailabilityViewSet,
+ True,
+ {"pk": self.asset_availability.id},
+ )
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(response.data["status"], AvailabilityStatus.OPERATIONAL.value)
diff --git a/care/facility/tests/test_medibase_api.py b/care/facility/tests/test_medibase_api.py
index 34ce13d7d5..ec6b53afad 100644
--- a/care/facility/tests/test_medibase_api.py
+++ b/care/facility/tests/test_medibase_api.py
@@ -9,17 +9,17 @@ def get_url(self, query=None):
def test_search_by_name_exact_word(self):
response = self.client.get(self.get_url(query="dolo"))
- self.assertEquals(response.status_code, status.HTTP_200_OK)
- self.assertEquals(response.data[0]["name"], "DOLO")
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(response.data[0]["name"], "DOLO")
def test_search_by_generic_exact_word(self):
response = self.client.get(self.get_url(query="pAraCetAmoL"))
- self.assertEquals(response.status_code, status.HTTP_200_OK)
- self.assertEquals(response.data[0]["generic"], "paracetamol")
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(response.data[0]["generic"], "paracetamol")
def test_search_by_name_and_generic_exact_word(self):
response = self.client.get(self.get_url(query="panadol paracetamol"))
- self.assertEquals(response.status_code, status.HTTP_200_OK)
- self.assertEquals(response.data[0]["name"], "PANADOL")
- self.assertEquals(response.data[0]["generic"], "paracetamol")
- self.assertEquals(response.data[0]["company"], "GSK")
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(response.data[0]["name"], "PANADOL")
+ self.assertEqual(response.data[0]["generic"], "paracetamol")
+ self.assertEqual(response.data[0]["company"], "GSK")
diff --git a/care/facility/tests/test_patient_daily_rounds_api.py b/care/facility/tests/test_patient_daily_rounds_api.py
index 4b675274c6..22733cfadb 100644
--- a/care/facility/tests/test_patient_daily_rounds_api.py
+++ b/care/facility/tests/test_patient_daily_rounds_api.py
@@ -10,4 +10,4 @@ def get_url(self, external_consultation_id=None):
def test_external_consultation_does_not_exists_returns_404(self):
sample_uuid = "e4a3d84a-d678-4992-9287-114f029046d8"
response = self.client.get(self.get_url(sample_uuid))
- self.assertEquals(response.status_code, status.HTTP_404_NOT_FOUND)
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
diff --git a/care/users/tests/test_facility_user_create.py b/care/users/tests/test_facility_user_create.py
index 75849c3180..66ae51d4f6 100644
--- a/care/users/tests/test_facility_user_create.py
+++ b/care/users/tests/test_facility_user_create.py
@@ -55,7 +55,7 @@ def test_create_facility_user__should_fail__when_higher_level(self):
response = self.client.post(self.get_url(), data=data, format="json")
# Test Creation
- self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_create_facility_user__should_fail__when_different_location(self):
new_district = self.clone_object(self.district)
@@ -64,4 +64,4 @@ def test_create_facility_user__should_fail__when_different_location(self):
response = self.client.post(self.get_url(), data=data, format="json")
# Test Creation
- self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
diff --git a/tests/test_facility_tasks.py b/tests/test_facility_tasks.py
deleted file mode 100644
index 19b6639e74..0000000000
--- a/tests/test_facility_tasks.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from unittest.mock import patch
-
-from celery import Celery
-
-from care.facility.tasks import setup_periodic_tasks
-
-
-@patch("care.facility.tasks.asset_monitor.check_asset_status.delay")
-def test_setup_periodic_tasks(mock_check_asset_status):
- app = Celery()
- setup_periodic_tasks(app)
-
- # Ensure that check_asset_status is called immediately
- mock_check_asset_status.assert_called_once()
From b37baf3b3484eda91a2024c0545e3c66f32c4ab9 Mon Sep 17 00:00:00 2001
From: Aakash Singh
Date: Tue, 18 Jul 2023 13:06:35 +0530
Subject: [PATCH 15/16] fix tests
---
care/facility/tests/test_asset_api.py | 20 ++++++++-------
.../tests/test_asset_availability_api.py | 25 ++++++++++---------
2 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/care/facility/tests/test_asset_api.py b/care/facility/tests/test_asset_api.py
index dbb8c27d42..36cb493e49 100644
--- a/care/facility/tests/test_asset_api.py
+++ b/care/facility/tests/test_asset_api.py
@@ -11,17 +11,19 @@
class AssetViewSetTestCase(TestBase, TestClassMixin, APITestCase):
asset_id = None
- def setUp(self):
- self.factory = APIRequestFactory()
- state = self.create_state()
- district = self.create_district(state=state)
- self.user = self.create_user(district=district, username="test user")
- facility = self.create_facility(district=district, user=self.user)
- self.asset1_location = AssetLocation.objects.create(
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.factory = APIRequestFactory()
+ state = cls.create_state()
+ district = cls.create_district(state=state)
+ cls.user = cls.create_user(district=district, username="test user")
+ facility = cls.create_facility(district=district, user=cls.user)
+ cls.asset1_location = AssetLocation.objects.create(
name="asset1 location", location_type=1, facility=facility
)
- self.asset = Asset.objects.create(
- name="Test Asset", current_location=self.asset1_location, asset_type=50
+ cls.asset = Asset.objects.create(
+ name="Test Asset", current_location=cls.asset1_location, asset_type=50
)
def test_list_assets(self):
diff --git a/care/facility/tests/test_asset_availability_api.py b/care/facility/tests/test_asset_availability_api.py
index 068982d001..65b7d36e40 100644
--- a/care/facility/tests/test_asset_availability_api.py
+++ b/care/facility/tests/test_asset_availability_api.py
@@ -10,24 +10,25 @@
class AssetAvailabilityViewSetTestCase(TestBase, TestClassMixin, APITestCase):
- def setUp(self):
- self.factory = APIRequestFactory()
- state = self.create_state()
- district = self.create_district(state=state)
- self.user = self.create_user(district=district, username="test user")
- facility = self.create_facility(district=district, user=self.user)
- self.asset_from_location = AssetLocation.objects.create(
+ @classmethod
+ def setUp(cls):
+ cls.factory = APIRequestFactory()
+ state = cls.create_state()
+ district = cls.create_district(state=state)
+ cls.user = cls.create_user(district=district, username="test user")
+ facility = cls.create_facility(district=district, user=cls.user)
+ cls.asset_from_location = AssetLocation.objects.create(
name="asset from location", location_type=1, facility=facility
)
- self.asset_to_location = AssetLocation.objects.create(
+ cls.asset_to_location = AssetLocation.objects.create(
name="asset to location", location_type=1, facility=facility
)
- self.asset = Asset.objects.create(
- name="Test Asset", current_location=self.asset_from_location, asset_type=50
+ cls.asset = Asset.objects.create(
+ name="Test Asset", current_location=cls.asset_from_location, asset_type=50
)
- self.asset_availability = AssetAvailabilityRecord.objects.create(
- asset=self.asset,
+ cls.asset_availability = AssetAvailabilityRecord.objects.create(
+ asset=cls.asset,
status=AvailabilityStatus.OPERATIONAL.value,
timestamp=timezone.now(),
)
From e38999c986c6759b5347d8c0644f0a616350a7ee Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 18 Jul 2023 13:14:44 +0530
Subject: [PATCH 16/16] Remove print statement
---
care/facility/tasks/asset_monitor.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py
index 6274bd3c85..82a0bcd0ee 100644
--- a/care/facility/tasks/asset_monitor.py
+++ b/care/facility/tasks/asset_monitor.py
@@ -18,7 +18,6 @@
@shared_task
def check_asset_status():
- print("Checking Asset Status", timezone.now())
logger.info(f"Checking Asset Status: {timezone.now()}")
assets = Asset.objects.all()