diff --git a/api/cases/apis.py b/api/cases/apis.py index 499d4c7..42003fd 100644 --- a/api/cases/apis.py +++ b/api/cases/apis.py @@ -73,6 +73,7 @@ class FilterSerializer(serializers.Serializer): end_date = serializers.DateField(required=False) gov = serializers.IntegerField(required=False) name = serializers.CharField(required=False) + include_null = serializers.BooleanField(required=False) class OutputSerializer(serializers.Serializer): id = serializers.IntegerField() diff --git a/api/cases/filters.py b/api/cases/filters.py index af1c4f6..5b62243 100644 --- a/api/cases/filters.py +++ b/api/cases/filters.py @@ -1,4 +1,5 @@ import django_filters +from django.db.models.query import Q from .models import Case @@ -8,9 +9,7 @@ class CaseFilter(django_filters.FilterSet): type = django_filters.CharFilter(lookup_expr="iexact") gov = django_filters.NumberFilter(field_name="location__gov") - name = django_filters.CharFilter( - field_name="details__name", lookup_expr="icontains" - ) + name = django_filters.CharFilter(field_name="details__name", method="filter_name") start_age = django_filters.NumberFilter( field_name="details__age", lookup_expr="gte" @@ -24,6 +23,14 @@ class CaseFilter(django_filters.FilterSet): field_name="details__last_seen", lookup_expr="lte" ) + def filter_name(self, queryset, name, value): + if self.data.get("include_null"): + return queryset.filter( + Q(details__name__icontains=value) | Q(details__name__isnull=True) + ) + + return queryset.filter(details__name__icontains=value) + class Meta: model = Case fields = [ diff --git a/api/cases/services.py b/api/cases/services.py index d4fec44..fda31bd 100644 --- a/api/cases/services.py +++ b/api/cases/services.py @@ -123,10 +123,16 @@ def process_case(case: Case) -> List[Dict[int, int]]: def case_matching_binding(*, case: Case, matches_list: List[Dict[int, int]]) -> None: - """ - Bind the processed case with it's matches by instaniating CaseMatch objects - """ + """ """ if not matches_list: + create_notification( + case=case, + action=Notification.Action.PUBLISH, + title="لم نجد حالات مشابه هل تود فى نشر الحاله", + body="لم نعثر على اى حالات مشابهه يمكنك نشر بيانات المفقود فى نطاق اوسع لتزيد احتماليه العثور عليه", + level=Notification.Level.WARNING, + sent_to=case.user, + ) return cases_ids = [match["id"] for match in matches_list] @@ -141,6 +147,24 @@ def case_matching_binding(*, case: Case, matches_list: List[Dict[int, int]]) -> else: create_case_match(missing=match, found=case, score=score) + create_notification( + case=match, + action=Notification.Action.MATCHES, + title="تم العثور على حالات مشابه", + body="تم الوصول لبعض النتائج قم بتصفحها الان", + level=Notification.Level.SUCCESS, + sent_to=match.user, + ) + + create_notification( + case=case, + action=Notification.Action.MATCHES, + title="تم العثور على حالات مشابه", + body="تم الوصول لبعض النتائج قم بتصفحها الان", + level=Notification.Level.SUCCESS, + sent_to=case.user, + ) + def activate_case(case: Case): matches = process_case(case) diff --git a/api/integrations/aws/client.py b/api/integrations/aws/client.py index c8fc224..0381508 100644 --- a/api/integrations/aws/client.py +++ b/api/integrations/aws/client.py @@ -3,6 +3,7 @@ import boto3 from attrs import define +from botocore.config import Config from api.common.utils import assert_settings @@ -51,7 +52,9 @@ def s3_get_client(): service_name="s3", aws_access_key_id=credentials.access_key_id, aws_secret_access_key=credentials.secret_access_key, - region_name=credentials.region_name, + region_name="eu-south-1", + endpoint_url="https://s3.eu-south-1.amazonaws.com/", + config=Config(signature_version="s3v4"), ) diff --git a/api/notifications/apis.py b/api/notifications/apis.py index a7a4cdf..6603bd7 100644 --- a/api/notifications/apis.py +++ b/api/notifications/apis.py @@ -1,8 +1,10 @@ -from rest_framework import serializers +from rest_framework import serializers, status +from rest_framework.response import Response from rest_framework.views import APIView from api.apis.pagination import LimitOffsetPagination, get_paginated_response -from api.notifications.selectors import list_user_notification +from api.notifications.selectors import get_notification, list_user_notification +from api.notifications.services import read_notification class NotificationListApi(APIView): @@ -30,3 +32,12 @@ def get(self, request): request=request, view=self, ) + + +class NotificationReadApi(APIView): + def get(self, request, notification_id): + + notification = get_notification(pk=notification_id, fetched_by=request.user) + read_notification(notification) + + return Response(status=status.HTTP_200_OK) diff --git a/api/notifications/selectors.py b/api/notifications/selectors.py index 871ee79..550c0d5 100644 --- a/api/notifications/selectors.py +++ b/api/notifications/selectors.py @@ -1,5 +1,19 @@ +from django.core.exceptions import PermissionDenied +from django.db.models.query import QuerySet +from django.shortcuts import get_object_or_404 + +from api.notifications.models import Notification from api.users.models import User -def list_user_notification(*, user: User): +def list_user_notification(*, user: User) -> QuerySet[Notification]: return user.notifications.all() + + +def get_notification(*, pk: int, fetched_by: User) -> Notification: + notification = get_object_or_404(Notification, pk=pk) + + if not notification.sent_to == fetched_by: + raise PermissionDenied() + + return notification diff --git a/api/notifications/services.py b/api/notifications/services.py index 49b4160..844850c 100644 --- a/api/notifications/services.py +++ b/api/notifications/services.py @@ -1,5 +1,6 @@ from typing import Optional +from django.utils import timezone from fcm_django.models import FCMDevice from api.cases.models import Case @@ -47,3 +48,8 @@ def create_fcm_device( device.save() return device + + +def read_notification(notification: Notification): + notification.read_at = timezone.now() + notification.save() diff --git a/api/notifications/urls.py b/api/notifications/urls.py index a6368bd..def7e7e 100644 --- a/api/notifications/urls.py +++ b/api/notifications/urls.py @@ -1,8 +1,13 @@ from django.urls import path -from api.notifications.apis import NotificationListApi +from api.notifications.apis import NotificationListApi, NotificationReadApi app_name = "notifications" urlpatterns = [ path("", NotificationListApi.as_view(), name="notification_list"), + path( + "/read/", + NotificationReadApi.as_view(), + name="notification_read", + ), ] diff --git a/config/settings/base.py b/config/settings/base.py index 6f795d1..6b8da10 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -401,4 +401,4 @@ # https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl AWS_DEFAULT_ACL = env("AWS_DEFAULT_ACL", default="public-read") - AWS_PRESIGNED_EXPIRY = env.int("AWS_PRESIGNED_EXPIRY", default=10) # seconds + AWS_PRESIGNED_EXPIRY = env.int("AWS_PRESIGNED_EXPIRY", default=300) # seconds