-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: root <[email protected]> Co-authored-by: Osama Yasser <[email protected]>
- Loading branch information
1 parent
7f6c4ab
commit f4d348e
Showing
114 changed files
with
5,348 additions
and
500 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -277,3 +277,9 @@ api/media/ | |
.env | ||
.envs/* | ||
!.envs/.local/ | ||
|
||
# VScode | ||
.vscode | ||
|
||
# Media files | ||
media |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Init |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class ApisConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "api.apis" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Init |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from collections import OrderedDict | ||
|
||
from rest_framework.pagination import LimitOffsetPagination as _LimitOffsetPagination | ||
from rest_framework.response import Response | ||
|
||
|
||
def get_paginated_response( | ||
*, pagination_class, serializer_class, queryset, request, view | ||
): | ||
paginator = pagination_class() | ||
|
||
page = paginator.paginate_queryset(queryset, request, view=view) | ||
|
||
if page is not None: | ||
serializer = serializer_class(page, many=True) | ||
return paginator.get_paginated_response(serializer.data) | ||
|
||
serializer = serializer_class(queryset, many=True) | ||
|
||
return Response(data=serializer.data) | ||
|
||
|
||
class LimitOffsetPagination(_LimitOffsetPagination): | ||
default_limit = 10 | ||
max_limit = 50 | ||
|
||
def get_paginated_data(self, data): | ||
return OrderedDict( | ||
[ | ||
("limit", self.limit), | ||
("offset", self.offset), | ||
("count", self.count), | ||
("next", self.get_next_link()), | ||
("previous", self.get_previous_link()), | ||
("results", data), | ||
] | ||
) | ||
|
||
def get_paginated_response(self, data): | ||
""" | ||
We redefine this method in order to return `limit` and `offset`. | ||
This is used by the frontend to construct the pagination itself. | ||
""" | ||
return Response( | ||
OrderedDict( | ||
[ | ||
("limit", self.limit), | ||
("offset", self.offset), | ||
("count", self.count), | ||
("next", self.get_next_link()), | ||
("previous", self.get_previous_link()), | ||
("results", data), | ||
] | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from django.urls import include, path | ||
|
||
app_name = "apis" | ||
urlpatterns = [ | ||
path("auth/", include("api.authentication.urls", "authentication")), | ||
path("users/", include("api.users.urls", "users")), | ||
path("cases/", include("api.cases.urls", "cases")), | ||
path("files/", include("api.files.urls", "files")), | ||
path("notifications/", include("api.notifications.urls", "notifications")), | ||
path("locations/", include("api.locations.urls", "locations")), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Init |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Admin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from rest_framework import permissions, serializers, status | ||
from rest_framework.response import Response | ||
from rest_framework.views import APIView | ||
|
||
from api.authentication.selectors import validate_phone | ||
from api.common.validators import is_phone | ||
|
||
|
||
class ValidatePhoneAPI(APIView): | ||
permission_classes = [permissions.AllowAny] | ||
|
||
class InputSerializer(serializers.Serializer): | ||
phone = serializers.CharField(validators=[is_phone]) | ||
|
||
def post(self, request): | ||
serializer = self.InputSerializer(data=request.data) | ||
serializer.is_valid(raise_exception=True) | ||
|
||
# Raises validation error if phone is taken | ||
validate_phone(**serializer.validated_data) | ||
|
||
return Response(status=status.HTTP_200_OK) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class AuthConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "api.authentication" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Init |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Models |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from typing import Union | ||
|
||
from rest_framework.exceptions import ValidationError | ||
|
||
from api.users.models import User | ||
|
||
|
||
def validate_phone(*, phone: str) -> Union[None, ValidationError]: | ||
if User.objects.filter(username=phone).exists(): | ||
raise ValidationError(f"Phone number: {phone} already taken") | ||
|
||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from django.urls import path | ||
from rest_framework_simplejwt.views import ( | ||
TokenObtainPairView, | ||
TokenRefreshView, | ||
TokenVerifyView, | ||
) | ||
|
||
from api.authentication.apis import ValidatePhoneAPI | ||
|
||
app_name = "auth" | ||
urlpatterns = [ | ||
path("token/", TokenObtainPairView.as_view(), name="obtain_token"), | ||
path("token/refresh/", TokenRefreshView.as_view(), name="refresh_token"), | ||
path("token/verify/", TokenVerifyView.as_view(), name="verify_token"), | ||
path("phone/validate/", ValidatePhoneAPI.as_view(), name="validate_phone"), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.contrib import admin | ||
|
||
from .models import Case, CaseDetails, CaseMatch, CasePhoto | ||
|
||
admin.site.register((Case, CaseDetails, CaseMatch, CasePhoto)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
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.cases.models import Case | ||
from api.cases.selectors import get_case, list_case, list_case_match | ||
from api.cases.services import create_case, publish_case | ||
from api.common.utils import inline_serializer | ||
|
||
|
||
class CreateCaseApi(APIView): | ||
class InputSerializer(serializers.Serializer): | ||
type = serializers.CharField() | ||
thumbnail = serializers.IntegerField() | ||
file_ids = serializers.ListField(child=serializers.IntegerField()) | ||
location = inline_serializer( | ||
fields={ | ||
"gov": serializers.IntegerField(), | ||
"city": serializers.IntegerField(), | ||
"address": serializers.CharField(required=False), | ||
"lon": serializers.DecimalField( | ||
max_digits=9, decimal_places=6, required=False | ||
), | ||
"lat": serializers.DecimalField( | ||
max_digits=8, decimal_places=6, required=False | ||
), | ||
} | ||
) | ||
details = inline_serializer( | ||
fields={ | ||
"name": serializers.CharField(required=False), | ||
"gender": serializers.CharField(required=False), | ||
"age": serializers.IntegerField(required=False), | ||
"last_seen": serializers.DateField(required=False), | ||
"description": serializers.CharField(required=False), | ||
"location": inline_serializer( | ||
required=False, | ||
fields={**location.fields}, | ||
), | ||
} | ||
) | ||
|
||
def post(self, request): | ||
serializer = self.InputSerializer(data=request.data) | ||
serializer.is_valid(raise_exception=True) | ||
create_case(user=request.user, **serializer.validated_data) | ||
|
||
return Response(status=status.HTTP_201_CREATED) | ||
|
||
|
||
class CaseListApi(APIView): | ||
class Pagination(LimitOffsetPagination): | ||
default_limit = 10 | ||
|
||
class FilterSerializer(serializers.Serializer): | ||
type = serializers.CharField(required=False) | ||
start_age = serializers.IntegerField(required=False) | ||
end_age = serializers.IntegerField(required=False) | ||
start_date = serializers.DateField(required=False) | ||
end_date = serializers.DateField(required=False) | ||
gov = serializers.IntegerField(required=False) | ||
name = serializers.CharField(required=False) | ||
|
||
class OutputSerializer(serializers.Serializer): | ||
id = serializers.IntegerField() | ||
type = serializers.CharField() | ||
name = serializers.CharField(source="details.name") | ||
thumbnail = serializers.URLField(source="thumbnail.url") | ||
last_seen = serializers.DateField(source="details.last_seen") | ||
posted_at = serializers.DateTimeField() | ||
location = inline_serializer( | ||
fields={ | ||
"gov": serializers.CharField(source="gov.name_ar"), | ||
"city": serializers.CharField(source="city.name_ar"), | ||
} | ||
) | ||
|
||
def get(self, request): | ||
# Make sure the filters are valid, if passed | ||
filters_serializer = self.FilterSerializer(data=request.query_params) | ||
filters_serializer.is_valid(raise_exception=True) | ||
|
||
cases = list_case(filters=filters_serializer.validated_data) | ||
|
||
return get_paginated_response( | ||
pagination_class=self.Pagination, | ||
serializer_class=self.OutputSerializer, | ||
queryset=cases, | ||
request=request, | ||
view=self, | ||
) | ||
|
||
|
||
class DetailsCaseApi(APIView): | ||
class OutputSerializer(serializers.Serializer): | ||
user = serializers.CharField(source="user.username") | ||
type = serializers.CharField() | ||
state = serializers.CharField(source="get_state_display") | ||
photos = serializers.ListField(source="photo_urls") | ||
location = inline_serializer( | ||
fields={ | ||
"gov": serializers.CharField(), | ||
"city": serializers.CharField(), | ||
"address": serializers.CharField(), | ||
"lon": serializers.DecimalField( | ||
max_digits=9, | ||
decimal_places=6, | ||
), | ||
"lat": serializers.DecimalField( | ||
max_digits=8, | ||
decimal_places=6, | ||
), | ||
} | ||
) | ||
details = inline_serializer( | ||
fields={ | ||
"name": serializers.CharField(), | ||
"gender": serializers.CharField(), | ||
"age": serializers.IntegerField(), | ||
"last_seen": serializers.DateField(), | ||
"description": serializers.CharField(), | ||
"location": location, | ||
} | ||
) | ||
|
||
def get(self, request, case_id): | ||
case = get_case(pk=case_id, fetched_by=request.user) | ||
|
||
serializer = self.OutputSerializer(case) | ||
|
||
return Response(serializer.data) | ||
|
||
|
||
class CaseMatchListApi(APIView): | ||
def get(self, request, case_id): | ||
|
||
# Fetching our case | ||
case = get_case(pk=case_id, fetched_by=request.user) | ||
|
||
# Selecting which cases to serialize depending on case type | ||
case_source = "missing" if case.type == Case.Types.FOUND else "found" | ||
|
||
# Writing our serializer here because of case source decision | ||
class OutputSerializer(serializers.Serializer): | ||
case = inline_serializer( | ||
fields={ | ||
"id": serializers.IntegerField(), | ||
"type": serializers.CharField(), | ||
"name": serializers.CharField(source="details.name"), | ||
"location": inline_serializer( | ||
fields={ | ||
"gov": serializers.CharField(source="gov.name_ar"), | ||
"city": serializers.CharField(source="city.name_ar"), | ||
}, | ||
), | ||
"thumbnail": serializers.URLField(source="thumbnail.url"), | ||
"last_seen": serializers.DateField(source="details.last_seen"), | ||
"posted_at": serializers.DateTimeField(), | ||
}, | ||
source=case_source, | ||
) | ||
score = serializers.IntegerField() | ||
|
||
# Listing all case matches | ||
matches = list_case_match(case=case, fetched_by=request.user) | ||
|
||
# Serializing the results | ||
serializer = OutputSerializer(matches, many=True) | ||
|
||
return Response(serializer.data) | ||
|
||
|
||
class CasePublishApi(APIView): | ||
def get(self, request, case_id): | ||
case = get_case(pk=case_id, fetched_by=request.user) | ||
publish_case(case=case, performed_by=request.user) | ||
return Response(status=status.HTTP_200_OK) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class CasesConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "api.cases" |
Oops, something went wrong.