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 15, 2023
1 parent d19221d commit 5c1fc46
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 39 deletions.
164 changes: 134 additions & 30 deletions apps/assessment_registry/dashboard_schema.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,53 @@
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.db.models import Q

from deep.caches import CacheHelper
from utils.graphene.filters import IDListFilter
from utils.graphene.enums import EnumDescription
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 @@ -24,10 +57,10 @@ class StakeholderCountType(graphene.ObjectType):

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

def resolve_data_collection_technique_display(self, info):
return MethodologyAttribute.CollectionTechniqueType(
Expand All @@ -36,18 +69,67 @@ def resolve_data_collection_technique_display(self, info):


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)

@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 +138,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 +178,25 @@ 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()


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)
51 changes: 51 additions & 0 deletions apps/assessment_registry/filter_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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
43 changes: 37 additions & 6 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -485,14 +485,45 @@ type AnalyticalStatementType {

type AssessmentCountType {
coordinatedJoint: AssessmentRegistryCoordinationTypeEnum
coordinatedJointDisplay: String
coordinatedJointDisplay: EnumDescription
count: Int
}

input AssessmentDashboardFilterDataInputType {
createdAt: DateTime
createdAtGte: DateTime
createdAtLte: DateTime
modifiedAt: DateTime
modifiedAtGte: DateTime
modifiedAtLte: DateTime
createdBy: [ID!]
modifiedBy: [ID!]
stakeholder: [ID!]
leadOrganization: [ID!]
location: [ID!]
affectedGroup: [AssessmentRegistryAffectedGroupTypeEnum!]
family: [AssessmentRegistryFamilyTypeEnum!]
frequency: [AssessmentRegistryFrequencyTypeEnum!]
coordinationType: [AssessmentRegistryCoordinationTypeEnum!]
assessmentType: [AssessmentRegistryDetailTypeEnum!]
focuses: [AssessmentRegistryFocusTypeEnum!]
}

input AssessmentDashboardFilterInputType {
dateFrom: Date!
dateTo: Date!
assessment: AssessmentDashboardFilterDataInputType
}

type AssessmentDashboardStatisticsType {
totalAssessment: Int!
totalStakeholder: Int!
totalCollectionTechnique: Int!
assessmentCount: [AssessmentCountType]
stakeholderCount: [StakeholderCountType]
collectionTechniqueCount: [CollectionTechniqueCountType]
totalMultisectorAssessment: Int!
totalSinglesectorAssessment: Int!
}

type AssessmentListType {
Expand Down Expand Up @@ -1099,9 +1130,9 @@ type ChangeUserPassword {
}

type CollectionTechniqueCountType {
dataCollectionTechnique: AssessmentRegistryDataCollectionTechniqueTypeEnum
dataCollectionTechniqueDisplay: String
count: Int
dataCollectionTechnique: AssessmentRegistryDataCollectionTechniqueTypeEnum!
dataCollectionTechniqueDisplay: EnumDescription
count: Int!
}

enum ConnectorLeadExtractionStatusEnum {
Expand Down Expand Up @@ -2731,9 +2762,9 @@ type ProjectDetailType {
organizations: [ProjectOrganizationType!]
hasAnalysisFramework: Boolean!
hasAssessmentTemplate: Boolean!
assessmentDashboardStatistics: AssessmentDashboardStatisticsType
assessmentDashboardStatistics(filter: AssessmentDashboardFilterInputType): AssessmentDashboardStatisticsType
assessmentRegistry(id: ID!): AssessmentRegistryType
assessmentRegistries(createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID], modifiedBy: [ID!], project: [ID], lead: [ID], search: String, page: Int = 1, pageSize: Int): AssessmentRegistryListType
assessmentRegistries(createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID], modifiedBy: [ID!], dateFrom: Date, dateTo: Date, project: [ID], lead: [ID], search: String, page: Int = 1, pageSize: Int): AssessmentRegistryListType
assessmentRegOptions: AssessmentRegistryOptionsType
analysisOverview: AnalysisOverviewType
analysis(id: ID!): AnalysisType
Expand Down

0 comments on commit 5c1fc46

Please sign in to comment.