fix - improve response time of org linked ranchers

This commit is contained in:
2026-01-04 10:39:02 +03:30
parent abf1c12e39
commit 627075735c
4 changed files with 62 additions and 36 deletions

View File

@@ -7,7 +7,7 @@ ENV_NAME=DEV
# Database secrets # Database secrets
DB_HOST=31.7.78.133 DB_HOST=31.7.78.133
DB_PORT=14352 DB_PORT=14352
DB_NAME=Development DB_NAME=Production
DB_USERNAME=postgres DB_USERNAME=postgres
DB_PASSWORD=pfLIVXupbDetvFMt2gUvxLXUL9b4HIOHaPcKXsBEZ1i8zl0iLUjmhUfXlGfJKcTV DB_PASSWORD=pfLIVXupbDetvFMt2gUvxLXUL9b4HIOHaPcKXsBEZ1i8zl0iLUjmhUfXlGfJKcTV

View File

@@ -16,6 +16,7 @@ class Herd(BaseModel):
'Rancher', 'Rancher',
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='herd', related_name='herd',
db_index=True,
null=True null=True
) )
cooperative = models.ForeignKey( cooperative = models.ForeignKey(
@@ -153,12 +154,14 @@ class RancherOrganizationLink(BaseModel):
Organization, Organization,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='rancher_links', related_name='rancher_links',
db_index=True,
null=True null=True
) )
rancher = models.ForeignKey( rancher = models.ForeignKey(
Rancher, Rancher,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='organization_links', related_name='organization_links',
db_index=True,
null=True null=True
) )

View File

@@ -1,8 +1,11 @@
from collections import defaultdict
from django.db.models import Count from django.db.models import Count
from apps.authentication.models import Organization from apps.authentication.models import Organization
from apps.authentication.services.service import get_all_org_child from apps.authentication.services.service import get_all_org_child
from apps.herd.models import RancherOrganizationLink from apps.herd.models import RancherOrganizationLink, Herd
from apps.livestock.models import LiveStock
# class RancherOrganizationService: # class RancherOrganizationService:
@@ -63,15 +66,18 @@ class RancherOrganizationService:
def orgs_linked_rancher(self, org: Organization, org_type_key: str): def orgs_linked_rancher(self, org: Organization, org_type_key: str):
# -------------------------------------------------- # --------------------------------------------------
# 1. Base organizations queryset # 1. Resolve organization ids
# -------------------------------------------------- # --------------------------------------------------
if org.type.key != 'ADM': if org.type.key != 'ADM':
child_orgs = get_all_org_child(org) org_ids = list(
org_ids = [o.id for o in child_orgs] o.id for o in get_all_org_child(org)
)
else: else:
org_ids = Organization.objects.filter( org_ids = list(
type__key=org_type_key Organization.objects.filter(
).values_list('id', flat=True) type__key=org_type_key
).values_list('id', flat=True)
)
organizations = ( organizations = (
Organization.objects Organization.objects
@@ -84,12 +90,12 @@ class RancherOrganizationService:
# 2. Rancher count per organization # 2. Rancher count per organization
# -------------------------------------------------- # --------------------------------------------------
rancher_counts = { rancher_counts = {
row['organization_id']: row['cnt'] r['organization_id']: r['cnt']
for row in ( for r in (
RancherOrganizationLink.objects RancherOrganizationLink.objects
.filter(organization_id__in=org_ids) .filter(organization_id__in=org_ids)
.values('organization_id') .values('organization_id')
.annotate(cnt=Count('rancher_id', distinct=True)) .annotate(cnt=Count('rancher_id'))
) )
} }
@@ -97,39 +103,54 @@ class RancherOrganizationService:
# 3. Herd count per organization # 3. Herd count per organization
# -------------------------------------------------- # --------------------------------------------------
herd_counts = { herd_counts = {
row['organization_id']: row['cnt'] r['organization_id']: r['cnt']
for row in ( for r in (
RancherOrganizationLink.objects RancherOrganizationLink.objects
.filter(organization_id__in=org_ids) .filter(organization_id__in=org_ids)
.values('organization_id') .values('organization_id')
.annotate(cnt=Count('rancher__herd', distinct=True)) .annotate(cnt=Count('rancher__herd'))
) )
} }
# -------------------------------------------------- # --------------------------------------------------
# 4. Livestock count per organization # 4. Livestock count (optimized path)
# LiveStockHerd -> Herd -> Org
# -------------------------------------------------- # --------------------------------------------------
livestock_counts = {
row['organization_id']: row['cnt'] # 4-1 livestock count per herd (NO JOIN, NO DISTINCT)
livestock_per_herd = {
row['herd_id']: row['cnt']
for row in ( for row in (
RancherOrganizationLink.objects LiveStock.objects
.filter(organization_id__in=org_ids) .values('herd_id')
.values('organization_id') .annotate(cnt=Count('id'))
.annotate(
cnt=Count(
'rancher__herd__live_stock_herd',
distinct=True
)
)
) )
} }
# 4-2 sum livestock per organization
livestock_counts = defaultdict(int)
herd_org_map = (
Herd.objects
.filter(
rancher__organization_links__organization_id__in=org_ids
)
.values(
'id',
'rancher__organization_links__organization_id'
)
)
for row in herd_org_map:
livestock_counts[
row['rancher__organization_links__organization_id']
] += livestock_per_herd.get(row['id'], 0)
# -------------------------------------------------- # --------------------------------------------------
# 5. Final response (merge in memory) # 5. Final response
# -------------------------------------------------- # --------------------------------------------------
response = [] return [
for org in organizations: {
response.append({
"id": org.id, "id": org.id,
"name": org.name, "name": org.name,
"org_service_area": [ "org_service_area": [
@@ -145,6 +166,6 @@ class RancherOrganizationService:
"rancher_count": rancher_counts.get(org.id, 0), "rancher_count": rancher_counts.get(org.id, 0),
"herd_count": herd_counts.get(org.id, 0), "herd_count": herd_counts.get(org.id, 0),
"livestock_count": livestock_counts.get(org.id, 0), "livestock_count": livestock_counts.get(org.id, 0),
}) }
for org in organizations
return response ]

View File

@@ -1,7 +1,8 @@
from django.db import models
from apps.core.models import BaseModel from apps.core.models import BaseModel
from apps.herd import models as herd_models from apps.herd import models as herd_models
from apps.tag import models as tag_models from apps.tag import models as tag_models
from django.db import models
class LiveStockSpecies(BaseModel): # noqa class LiveStockSpecies(BaseModel): # noqa
@@ -53,6 +54,7 @@ class LiveStock(BaseModel):
herd_models.Herd, herd_models.Herd,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="live_stock_herd", related_name="live_stock_herd",
db_index=True,
null=True null=True
) )
tag = models.ForeignKey( tag = models.ForeignKey(
@@ -113,9 +115,9 @@ class TemporaryLiveStock(BaseModel):
null=True null=True
) )
count = models.PositiveBigIntegerField(default=0) count = models.PositiveBigIntegerField(default=0)
def __str__(self): def __str__(self):
return f'temporary: {self.id} - rancher: {self.rancher.national_code}' return f'temporary: {self.id} - rancher: {self.rancher.national_code}'
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
return super(TemporaryLiveStock, self).save(*args, **kwargs) return super(TemporaryLiveStock, self).save(*args, **kwargs)