-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Operational learning stats API #2335
Changes from 1 commit
2a6d79f
543215c
ef38a4f
ca1c3d3
afff697
569eae9
641fb4d
da571fc
33c3626
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
import pytz | ||
from django.conf import settings | ||
from django.db import transaction | ||
from django.db.models import Prefetch, Q | ||
from django.db.models import Count, F, OuterRef, Prefetch, Q, Subquery | ||
from django.http import HttpResponse | ||
from django.shortcuts import get_object_or_404 | ||
from django.utils.translation import get_language as django_get_language | ||
|
@@ -20,7 +20,7 @@ | |
from rest_framework.response import Response | ||
from rest_framework.settings import api_settings | ||
|
||
from api.models import Country | ||
from api.models import Appeal, AppealType, Country | ||
from deployments.models import SectorTag | ||
from main.permissions import DenyGuestUserMutationPermission, DenyGuestUserPermission | ||
from main.utils import SpreadSheetContentNegotiation | ||
|
@@ -921,6 +921,82 @@ def summary(self, request): | |
) | ||
return response.Response(OpsLearningSummarySerializer(ops_learning_summary_instance).data) | ||
|
||
@action( | ||
detail=False, | ||
methods=["GET"], | ||
permission_classes=[DenyGuestUserMutationPermission, OpsLearningPermission], | ||
url_path="stats", | ||
) | ||
def stats(self, request): | ||
""" | ||
Get the Ops Learning stats based on the filters | ||
""" | ||
ops_data = ( | ||
super() | ||
.get_queryset() | ||
.filter(is_validated=True) | ||
.select_related("appeal_code") | ||
.prefetch_related( | ||
"appeal_code__appealdocument", | ||
"sector_validated", | ||
) | ||
.aggregate( | ||
operations_included=Count("appeal_code", distinct=True), | ||
learning_extracts=Count("id", distinct=True), | ||
sector_covered=Count("sector_validated", distinct=True), | ||
source_used=Count("appeal_code__appealdocument", distinct=True), | ||
) | ||
) | ||
|
||
learning_by_sector = ( | ||
SectorTag.objects.filter(title__isnull=False) | ||
.annotate(count=Count("validated_sectors", distinct=True)) | ||
susilnem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.values("title", "count") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We also need to add 'id', this will be used by client as unique key |
||
) | ||
|
||
sources_overtime = { | ||
str(appeal_type_label): OpsLearning.objects.filter(appeal_code__atype=appeal_type, is_validated=True) | ||
.annotate(date=F(("appeal_code__start_date"))) | ||
.values("date") | ||
.annotate(count=Count("appeal_code__appealdocument", distinct=True)) | ||
.order_by("date") | ||
for appeal_type, appeal_type_label in AppealType.choices | ||
} | ||
|
||
region_subquery = Appeal.objects.filter(code=OuterRef("appeal_code"), region__isnull=False).values("region__label")[:1] | ||
susilnem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
region_id = Appeal.objects.filter(code=OuterRef("appeal_code"), region__isnull=False).values("region__id")[:1] | ||
|
||
learning_by_region = ( | ||
OpsLearning.objects.filter(is_validated=True) | ||
.annotate(name=Subquery(region_subquery)) | ||
.values("name") | ||
.annotate(id=Subquery(region_id), count=Count("id", distinct=True)) | ||
.order_by("name") | ||
) | ||
|
||
country_subquery = Appeal.objects.filter(code=OuterRef("appeal_code"), country__isnull=False).values("country__name")[:1] | ||
country_id = Appeal.objects.filter(code=OuterRef("appeal_code"), country__isnull=False).values("country__id")[:1] | ||
|
||
learning_by_country = ( | ||
OpsLearning.objects.filter(is_validated=True) | ||
.annotate(name=Subquery(country_subquery)) | ||
.values("name") | ||
.annotate(id=Subquery(country_id), count=Count("id", distinct=True)) | ||
.order_by("name") | ||
) | ||
|
||
data = { | ||
"operations_included": ops_data["operations_included"], | ||
"learning_extracts": ops_data["learning_extracts"], | ||
"sectors_covered": ops_data["sector_covered"], | ||
"sources_used": ops_data["source_used"], | ||
"learning_by_region": learning_by_region, | ||
"learning_by_sector": learning_by_sector, | ||
"sources_overtime": sources_overtime, | ||
"learning_by_country": learning_by_country, | ||
} | ||
return response.Response(data) | ||
|
||
|
||
class PerDocumentUploadViewSet(viewsets.ModelViewSet): | ||
queryset = PerDocumentUpload.objects.all() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ | |
import factory | ||
from factory import fuzzy | ||
|
||
from api.factories.country import CountryFactory | ||
from api.models import Appeal, AppealDocument | ||
from deployments.factories.project import SectorTagFactory | ||
from per.models import ( | ||
AssessmentType, | ||
|
@@ -105,12 +107,22 @@ class Meta: | |
model = FormPrioritization | ||
|
||
|
||
class AppealFactory(factory.django.DjangoModelFactory): | ||
class Meta: | ||
model = Appeal | ||
|
||
country = factory.SubFactory(CountryFactory) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's not use |
||
|
||
|
||
class OpsLearningFactory(factory.django.DjangoModelFactory): | ||
learning = fuzzy.FuzzyText(length=50) | ||
|
||
class Meta: | ||
model = OpsLearning | ||
|
||
appeal_code = factory.SubFactory(AppealFactory) | ||
is_validated = fuzzy.FuzzyChoice([True, False]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's not use fuzzy choice for is_validated, we should manually define in the test cases as lots of endpoints depends on this |
||
|
||
|
||
class OpsLearningCacheResponseFactory(factory.django.DjangoModelFactory): | ||
used_filters_hash = fuzzy.FuzzyText(length=20) | ||
|
@@ -141,3 +153,10 @@ class OpsLearningComponentCacheResponseFactory(factory.django.DjangoModelFactory | |
|
||
class Meta: | ||
model = OpsLearningComponentCacheResponse | ||
|
||
|
||
class AppealDocumentFactory(factory.django.DjangoModelFactory): | ||
class Meta: | ||
model = AppealDocument | ||
|
||
appeal = factory.SubFactory(AppealFactory) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add
_qs
as postfix (learning_by_sector_qs
) for all querysets variables