Skip to content

Commit

Permalink
Add query for Assessment registry Count
Browse files Browse the repository at this point in the history
- total assessment count
- total stakeholder count
- collection technique count
- multisector and single sector assessment
- filter with affected groups
- filter with location,family,frequency,coordination_type,assessment_type and focus
  • Loading branch information
sudan45 committed Aug 16, 2023
1 parent d19221d commit 2436e22
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 44 deletions.
196 changes: 161 additions & 35 deletions apps/assessment_registry/dashboard_schema.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,55 @@
import itertools
import graphene
from .models import AssessmentRegistry, MethodologyAttribute
from dataclasses import dataclass
from collections import defaultdict

from django.db.models import Count
from django.db import models
from django.contrib.postgres.aggregates.general import ArrayAgg

from deep.caches import CacheHelper
from geo.models import Region,GeoArea
from utils.graphene.filters import IDListFilter
from utils.graphene.enums import EnumDescription
from utils.graphene.geo_scalars import PointScalar
from .enums import (
AssessmentRegistryCoordinationTypeEnum,
AssessmentRegistryDataCollectionTechniqueTypeEnum,
)
from django.db.models import Count
from .filter_set import (
AssessmentDashboardFilterDataInputType,
AssessmentDashboardFilterSet,
)
from .models import AssessmentRegistry, MethodologyAttribute


def get_global_filters(_filter: dict, date_field="created_at"):
return {
f"{date_field}__gte": _filter["date_from"],
f"{date_field}__lte": _filter["date_to"],
}


@dataclass
class AssessmentDashboardStat:
cache_key: str
assessment_registry_qs: models.QuerySet
methodology_attribute_qs: models.QuerySet


class AssessmentDashboardFilterInputType(graphene.InputObjectType):
date_from = graphene.Date(required=True)
date_to = graphene.Date(required=True)
assessment = AssessmentDashboardFilterDataInputType()


class AssessmentCountType(graphene.ObjectType):
coordinated_joint = graphene.Field(AssessmentRegistryCoordinationTypeEnum)
coordinated_joint_display = graphene.String()
coordinated_joint_display = EnumDescription(required=False)
count = graphene.Int()

def resolve_coordinated_joint_display(self, info):
return AssessmentRegistry.CoordinationType(
self.coordinated_joint).label
return AssessmentRegistry.CoordinationType(self.coordinated_joint).label


class StakeholderCountType(graphene.ObjectType):
Expand All @@ -23,31 +58,77 @@ class StakeholderCountType(graphene.ObjectType):


class CollectionTechniqueCountType(graphene.ObjectType):
data_collection_technique = graphene.Field(
AssessmentRegistryDataCollectionTechniqueTypeEnum
)
data_collection_technique_display = graphene.String()
count = graphene.Int()
data_collection_technique = graphene.Field(AssessmentRegistryDataCollectionTechniqueTypeEnum, required=True)
data_collection_technique_display = EnumDescription(required=False)
count = graphene.Int(required=True)

def resolve_data_collection_technique_display(self, info):
return MethodologyAttribute.CollectionTechniqueType(
self.data_collection_technique
).label
return MethodologyAttribute.CollectionTechniqueType(self.data_collection_technique).label


class AssessmentDashboardGeographicalArea(graphene.ObjectType):
id = graphene.ID(required=True)
centroid = PointScalar()
assessment_ids = graphene.List(graphene.NonNull(graphene.ID))


class AssessmentDashboardStatisticsType(graphene.ObjectType):
total_assessment = graphene.Int(required=True)
total_stakeholder = graphene.Int(required=True)
total_collection_technique = graphene.Int(required=True)
assessment_count = graphene.List(AssessmentCountType)
stakeholder_count = graphene.List(StakeholderCountType)
collection_technique_count = graphene.List(CollectionTechniqueCountType)
model = AssessmentRegistry
total_multisector_assessment = graphene.Int(required=True)
total_singlesector_assessment = graphene.Int(required=True)
assessment_geographic_areas = graphene.List(AssessmentDashboardGeographicalArea)

@staticmethod
def custom_resolver(root, info, _filter):
assessment_qs = AssessmentRegistry.objects.filter(
project=info.context.active_project,
**get_global_filters(_filter),
)
methodology_attribute_qs = MethodologyAttribute.objects.filter(
assessment_registry__project_id=info.context.active_project,
**get_global_filters(_filter),
)
assessment_qs_filter = AssessmentDashboardFilterSet(queryset=assessment_qs, data=_filter.get("assessment")).qs
cache_key = CacheHelper.generate_hash(_filter.__dict__)
return AssessmentDashboardStat(
cache_key=cache_key,
assessment_registry_qs=assessment_qs_filter,
methodology_attribute_qs=methodology_attribute_qs,
)

@staticmethod
def resolve_total_assessment(root: AssessmentDashboardStat, info) -> int:
return root.assessment_registry_qs.count()

@staticmethod
def resolve_total_stakeholder(root: AssessmentDashboardStat, info) -> int:
# TODO: when stakeholder manage in a single field
qs = root.assessment_registry_qs.values_list(
"international_partners__title",
"donors__title",
"lead_organizations__title",
"national_partners__title",
"governments__title",
)
return len(set(list(itertools.chain(*qs))))

@staticmethod
def resolve_total_collection_technique(root: AssessmentDashboardStat, info) -> int:
return root.methodology_attribute_qs.values("data_collection_technique").distinct().count()

def resolve_assessment_count(self, info):
@staticmethod
def resolve_assessment_count(root: AssessmentDashboardStat, info):
assessment = (
self.model.objects.filter(project=info.context.active_project)
.values("coordinated_joint")
root.assessment_registry_qs.values("coordinated_joint")
.annotate(count=Count("coordinated_joint"))
.order_by("coordinated_joint")
)

return [
AssessmentCountType(
coordinated_joint=assessment["coordinated_joint"],
Expand All @@ -56,27 +137,35 @@ def resolve_assessment_count(self, info):
for assessment in assessment
]

def resolve_stakeholder_count(self, info):
stakeholder = (
self.model.objects.filter(project=info.context.active_project)
.values("lead_organizations", "lead_organizations__title")
.annotate(count=Count("lead_organizations"))
.order_by("lead_organizations")
)
@staticmethod
def resolve_stakeholder_count(root: AssessmentDashboardStat, info):
# TODO: when stakeholder manage in a single field
stakeholder_counts = defaultdict(int)
organization_type_fields = [
"lead_organizations__organization_type__title",
"international_partners__organization_type__title",
"donors__organization_type__title",
"national_partners__organization_type__title",
"governments__organization_type__title",
]

for field in organization_type_fields:
stakeholders = root.assessment_registry_qs.values(field)
for stakeholder in stakeholders:
if organization_type_title := stakeholder.get(field):
stakeholder_counts[organization_type_title] += 1
return [
StakeholderCountType(
stakeholder=stakeholder["lead_organizations__title"],
count=stakeholder["count"],
stakeholder=org_type_title,
count=count,
)
for stakeholder in stakeholder
for org_type_title, count in stakeholder_counts.items()
]

def resolve_collection_technique_count(self, info):
@staticmethod
def resolve_collection_technique_count(root: AssessmentDashboardStat, info):
data_collection_technique = (
MethodologyAttribute.objects.filter(
assessment_registry__project_id=info.context.active_project
)
.values("data_collection_technique")
root.methodology_attribute_qs.values("data_collection_technique")
.annotate(count=Count("data_collection_technique"))
.order_by("data_collection_technique")
)
Expand All @@ -88,11 +177,48 @@ def resolve_collection_technique_count(self, info):
for technique in data_collection_technique
]

@staticmethod
def resolve_total_multisector_assessment(root: AssessmentDashboardStat, info) -> int:
return root.assessment_registry_qs.filter(sectors__len__gte=2).count()

@staticmethod
def resolve_total_singlesector_assessment(root: AssessmentDashboardStat, info) -> int:
return root.assessment_registry_qs.filter(sectors__len=1).count()

@staticmethod
def resolve_assessment_geographic_areas(root:AssessmentDashboardStat,info):
return GeoArea.objects.annotate(
assessment_ids=ArrayAgg(
'project',
distinct=True,
ordering='project',
filter=models.Q(project__in=root.projects_qs),
),
).filter(project_ids__isnull=False).values(
'id',
'centroid',
'project_ids',
)
return Region.objects.annotate(
project_ids=ArrayAgg(
'project',
distinct=True,
ordering='project',
filter=models.Q(project__in=root.projects_qs),
),
).filter(project_ids__isnull=False).values(
'id',
'centroid',
'project_ids',
)


class Query:
assessment_dashboard_statistics = graphene.Field(
AssessmentDashboardStatisticsType)
AssessmentDashboardStatisticsType,
filter=AssessmentDashboardFilterInputType(),
)

@staticmethod
def resolve_assessment_dashboard_statistics(root, info, **kwargs):
return AssessmentDashboardStatisticsType
def resolve_assessment_dashboard_statistics(root, info, filter):
return AssessmentDashboardStatisticsType.custom_resolver(root, info, filter)
55 changes: 55 additions & 0 deletions apps/assessment_registry/filter_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import django_filters

from deep.filter_set import generate_type_for_filter_set, OrderEnumMixin
from user_resource.filters import UserResourceGqlFilterSet
from .models import AssessmentRegistry
from utils.graphene.filters import IDListFilter, IDFilter, MultipleInputFilter
from .enums import (
AssessmentRegistryAffectedGroupTypeEnum,
AssessmentRegistryCoordinationTypeEnum,
AssessmentRegistryDetailTypeEnum,
AssessmentRegistryFamilyTypeEnum,
AssessmentRegistryFrequencyTypeEnum,
AssessmentRegistryFocusTypeEnum,
)


class AssessmentDashboardFilterSet(OrderEnumMixin, UserResourceGqlFilterSet):
stakeholder = IDListFilter(method="filter_stakeholder")
lead_organization = IDListFilter(method="filter_lead_organization")
location = IDListFilter(method="filter_location")
affected_group = MultipleInputFilter(AssessmentRegistryAffectedGroupTypeEnum, method="filter_affected_group")
family = MultipleInputFilter(AssessmentRegistryFamilyTypeEnum)
frequency = MultipleInputFilter(AssessmentRegistryFrequencyTypeEnum)
coordination_type = MultipleInputFilter(AssessmentRegistryCoordinationTypeEnum, field_name="coordinated_joint")
assessment_type = MultipleInputFilter(AssessmentRegistryDetailTypeEnum, field_name="details_type")
focuses = MultipleInputFilter(AssessmentRegistryFocusTypeEnum, method="filter_focuses")

class Meta:
model = AssessmentRegistry
fields = ()

def filter_stakeholder(self, qs, _, value):
pass # TODO: when stakeholder is managed in single field

def filter_lead_organization(self, qs, _, value):
pass # TODO: when stakeholder is managed in single field

def filter_location(self, qs, _, value):
return (
qs if value is None else qs.filter(locations__in=list(map(int, value)))
) ##change value (list of decimal) into int list

def filter_affected_group(self, qs, _, value):
return qs if value is None else qs.filter(affected_groups__overlap=value)

def filter_focuses(self, qs, _, value):
return qs if value is None else qs.filter(focuses__overlap=value)


AssessmentDashboardFilterDataType, AssessmentDashboardFilterDataInputType = generate_type_for_filter_set(
AssessmentDashboardFilterSet,
"project.schema.ProjectListType",
"AssessmentDashboardFilterDataType",
"AssessmentDashboardFilterDataInputType",
)
2 changes: 2 additions & 0 deletions apps/assessment_registry/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@


class AssessmentRegistryGQFilterSet(UserResourceGqlFilterSet):
date_from=django_filters.DateFilter(required=False)
date_to=django_filters.DateFilter(required=False)
project = django_filters.ModelMultipleChoiceFilter(
queryset=Project.objects.all(),
field_name='lead__project',
Expand Down
5 changes: 2 additions & 3 deletions apps/project/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@
from assessment_registry.schema import Query as AssessmentRegistryQuery
from unified_connector.schema import UnifiedConnectorQueryType
from assisted_tagging.schema import AssistedTaggingQueryType
from assessment_registry import dashboard_schema as assessment_registry_dashboard

from assessment_registry.dashboard_schema import Query as AssessmentRegistryDashboardQuery
from lead.models import Lead
from entry.models import Entry
from geo.models import Region
Expand Down Expand Up @@ -399,7 +398,7 @@ class ProjectDetailType(
AryQuery,
AnalysisQuery,
AssessmentRegistryQuery,
assessment_registry_dashboard.Query,
AssessmentRegistryDashboardQuery,
# -- End --Project scopped entities
ProjectType,
):
Expand Down
Loading

0 comments on commit 2436e22

Please sign in to comment.