Skip to content

Commit

Permalink
Add index for historical data (#101)
Browse files Browse the repository at this point in the history
- Change filter structure to use index
- Fix ID-name issues for filterAlertCountry
- Add historical alerts node
  • Loading branch information
thenav56 authored Nov 27, 2024
1 parent 9a645ce commit 67a36af
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 49 deletions.
61 changes: 28 additions & 33 deletions apps/cap_feed/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,15 @@
from .models import Admin1, Alert, AlertInfo, Country, Feed, Region


# NOTE: Make sure to sync changes here with apps/subscription/serializers.py:UserAlertSubscriptionFilterSerializer
@strawberry_django.filters.filter(Alert, lookups=True)
class AlertFilter:
@strawberry_django.filters.filter(AlertInfo, lookups=True)
class AlertInfoFilter:
id: strawberry.auto
country: strawberry.auto
sent: strawberry.auto

@strawberry_django.filter_field
def region(
self,
queryset: models.QuerySet,
value: strawberry.ID,
prefix: str,
) -> tuple[models.QuerySet, models.Q]:
return queryset, models.Q(**{f"{prefix}country__region": value})

@strawberry_django.filter_field
def admin1(
self,
queryset: models.QuerySet,
value: strawberry.ID,
prefix: str,
) -> tuple[models.QuerySet, models.Q]:
return queryset, models.Q(**{f"{prefix}admin1s": value})

def _info_enum_fields(self, field, queryset, value, prefix) -> tuple[models.QuerySet, models.Q]:
if value:
alias_field = f"_infos_{field}_list"
queryset = queryset.alias(
**{
# NOTE: To avoid duplicate alerts when joining infos
alias_field: ArrayAgg(f"{prefix}infos__{field}"),
}
)
return queryset, models.Q(**{f"{prefix}{alias_field}__overlap": value})
# NOTE: With this field, disctinct should be used by the client
print(f"{prefix}{field}__in")
return queryset, models.Q(**{f"{prefix}{field}__in": value})
return queryset, models.Q()

@strawberry_django.filter_field
Expand Down Expand Up @@ -86,9 +60,30 @@ def category(
return self._info_enum_fields("category", queryset, value, prefix)


@strawberry_django.filters.filter(AlertInfo, lookups=True)
class AlertInfoFilter:
@strawberry_django.filters.filter(Alert, lookups=True)
class AlertFilter:
id: strawberry.auto
country: strawberry.auto
sent: strawberry.auto
infos: AlertInfoFilter | None

@strawberry_django.filter_field
def region(
self,
queryset: models.QuerySet,
value: strawberry.ID,
prefix: str,
) -> tuple[models.QuerySet, models.Q]:
return queryset, models.Q(**{f"{prefix}country__region": value})

@strawberry_django.filter_field
def admin1(
self,
queryset: models.QuerySet,
value: strawberry.ID,
prefix: str,
) -> tuple[models.QuerySet, models.Q]:
return queryset, models.Q(**{f"{prefix}admin1s": value})


@strawberry_django.filters.filter(Feed, lookups=True)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 4.2.13 on 2024-11-26 12:10

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('cap_feed', '0010_alter_alert_is_processed_by_subscription_and_more'),
]

operations = [
migrations.AlterField(
model_name='alertinfo',
name='category',
field=models.CharField(choices=[('Geo', 'Geo'), ('Met', 'Met'), ('Safety', 'Safety'), ('Security', 'Security'), ('Rescue', 'Rescue'), ('Fire', 'Fire'), ('Health', 'Health'), ('Env', 'Env'), ('Transport', 'Transport'), ('Infra', 'Infra'), ('CBRNE', 'CBRNE'), ('Other', 'Other')], db_index=True),
),
migrations.AlterField(
model_name='alertinfo',
name='certainty',
field=models.CharField(choices=[('Observed', 'Observed'), ('Likely', 'Likely'), ('Possible', 'Possible'), ('Unlikely', 'Unlikely'), ('Unknown', 'Unknown')], db_index=True),
),
migrations.AlterField(
model_name='alertinfo',
name='severity',
field=models.CharField(choices=[('Extreme', 'Extreme'), ('Severe', 'Severe'), ('Moderate', 'Moderate'), ('Minor', 'Minor'), ('Unknown', 'Unknown')], db_index=True),
),
migrations.AlterField(
model_name='alertinfo',
name='urgency',
field=models.CharField(choices=[('Immediate', 'Immediate'), ('Expected', 'Expected'), ('Future', 'Future'), ('Past', 'Past'), ('Unknown', 'Unknown')], db_index=True),
),
]
8 changes: 4 additions & 4 deletions apps/cap_feed/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,12 +308,12 @@ class Certainty(models.TextChoices):
alert = models.ForeignKey(Alert, on_delete=models.CASCADE, related_name='infos')

language = models.CharField(blank=True, default='en-US')
category = models.CharField(choices=Category.choices)
category = models.CharField(choices=Category.choices, db_index=True)
event = models.CharField()
response_type = models.CharField(choices=ResponseType.choices, blank=True, null=True, default=None)
urgency = models.CharField(choices=Urgency.choices)
severity = models.CharField(choices=Severity.choices)
certainty = models.CharField(choices=Certainty.choices)
urgency = models.CharField(choices=Urgency.choices, db_index=True)
severity = models.CharField(choices=Severity.choices, db_index=True)
certainty = models.CharField(choices=Certainty.choices, db_index=True)
audience = models.CharField(blank=True, null=True, default=None)
event_code = models.CharField(blank=True, null=True, default=None)
# effective = models.DateTimeField(default=Alert.objects.get(pk=alert).sent)
Expand Down
25 changes: 24 additions & 1 deletion apps/cap_feed/queries.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import typing

import strawberry
import strawberry_django
from django.db import models
from strawberry_django.filters import apply as apply_filters
from strawberry_django.pagination import OffsetPaginationInput

from main.graphql.context import Info
from utils.strawberry.paginations import CountList, pagination_field
from utils.strawberry.paginations import (
CountList,
count_list_resolver,
pagination_field,
)

from .filters import (
Admin1Filter,
Expand Down Expand Up @@ -138,6 +145,22 @@ async def alert(self, info: Info, pk: strawberry.ID) -> AlertType | None:
async def alert_info(self, info: Info, pk: strawberry.ID) -> AlertInfoType | None:
return await AlertInfoType.get_queryset(None, None, info).filter(pk=pk).afirst()

@strawberry_django.field
async def historical_alerts(
self,
info: Info,
filters: typing.Optional[AlertFilter] = strawberry.UNSET,
pagination: typing.Optional[OffsetPaginationInput] = strawberry.UNSET,
) -> CountList[AlertType]:
queryset = get_alert_queryset(None, is_active=False)
return count_list_resolver(
info,
queryset,
AlertType,
filters=filters, # type: ignore[reportArgumentType]
pagination=pagination, # type: ignore[reportArgumentType]
)


@strawberry.type
class PrivateQuery:
Expand Down
11 changes: 8 additions & 3 deletions apps/subscription/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from apps.cap_feed.filters import AlertFilter
from apps.cap_feed.orders import AlertOrder
from apps.cap_feed.types import AlertType
from apps.cap_feed.types import AlertType, get_alert_queryset
from main.graphql.context import Info
from utils.strawberry.paginations import (
CountList,
Expand Down Expand Up @@ -40,10 +40,15 @@ async def subscripted_alerts(
order: typing.Optional[AlertOrder] = strawberry.UNSET,
pagination: typing.Optional[OffsetPaginationInput] = strawberry.UNSET,
) -> CountList[AlertType]:
queryset = AlertType.get_queryset(None, None, info).filter(
# XXX: Add DISTINCT as default to avoid duplicate alerts
if filters is strawberry.UNSET:
filters = AlertFilter(DISTINCT=True) # type: ignore[reportCallIssue]
else:
filters.DISTINCT = True # type: ignore[reportCallIssue]

queryset = get_alert_queryset(None, is_active=False).filter(
subscriptions__in=UserAlertSubscription.objects.filter(user=info.context.request.user).all(),
)
# TODO: Handle duplicates from filters(frontend side) or manually
return count_list_resolver(
info,
queryset,
Expand Down
8 changes: 4 additions & 4 deletions apps/subscription/tests/test_mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class Mutation:
# Filters
# -- ForeignKey
filterAlertCountry
filterAlertCountryDisplay {
filterAlertCountryId
filterAlertCountry {
id
name
}
Expand Down Expand Up @@ -78,8 +78,8 @@ class Mutation:
# Filters
# -- ForeignKey
filterAlertCountry
filterAlertCountryDisplay {
filterAlertCountryId
filterAlertCountry {
id
name
}
Expand Down
10 changes: 6 additions & 4 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,13 @@ input AlertFilter {
id: IDBaseFilterLookup
country: DjangoModelFilterInput
sent: DatetimeDatetimeFilterLookup
infos: AlertInfoFilter
AND: AlertFilter
OR: AlertFilter
NOT: AlertFilter
DISTINCT: Boolean
region: ID
admin1: ID
urgency: [AlertInfoUrgencyEnum!]
severity: [AlertInfoSeverityEnum!]
certainty: [AlertInfoCertaintyEnum!]
category: [AlertInfoCategoryEnum!]
}

type AlertInfoAreaCircleType {
Expand Down Expand Up @@ -106,6 +103,10 @@ input AlertInfoFilter {
OR: AlertInfoFilter
NOT: AlertInfoFilter
DISTINCT: Boolean
urgency: [AlertInfoUrgencyEnum!]
severity: [AlertInfoSeverityEnum!]
certainty: [AlertInfoCertaintyEnum!]
category: [AlertInfoCategoryEnum!]
}

input AlertInfoOrder {
Expand Down Expand Up @@ -644,6 +645,7 @@ type PublicQuery {
feed(pk: ID!): FeedType
alert(pk: ID!): AlertType
alertInfo(pk: ID!): AlertInfoType
historicalAlerts(filters: AlertFilter, pagination: OffsetPaginationInput): AlertTypeCountList!
id: ID!
}

Expand Down

0 comments on commit 67a36af

Please sign in to comment.