From 0113e84548833fde06ba04031a709e0e70c85c39 Mon Sep 17 00:00:00 2001 From: Horilla Date: Mon, 28 Apr 2025 17:39:24 +0530 Subject: [PATCH] [ADD] GEOFENCING & FACEDETECTION: Add modules for geofencing and facedetection app api's for mobile app (beta) --- facedetection/__init__.py | 0 facedetection/admin.py | 7 ++ facedetection/apps.py | 15 ++++ facedetection/migrations/__init__.py | 0 facedetection/models.py | 20 +++++ facedetection/serializers.py | 8 ++ facedetection/tests.py | 3 + facedetection/urls.py | 7 ++ facedetection/views.py | 30 +++++++ geofencing/__init__.py | 0 geofencing/admin.py | 5 ++ geofencing/apps.py | 15 ++++ geofencing/migrations/__init__.py | 0 geofencing/models.py | 31 +++++++ geofencing/serializers.py | 27 ++++++ geofencing/tests.py | 3 + geofencing/urls.py | 9 ++ geofencing/views.py | 128 +++++++++++++++++++++++++++ horilla_api/__init__.py | 4 + 19 files changed, 312 insertions(+) create mode 100644 facedetection/__init__.py create mode 100644 facedetection/admin.py create mode 100644 facedetection/apps.py create mode 100644 facedetection/migrations/__init__.py create mode 100644 facedetection/models.py create mode 100644 facedetection/serializers.py create mode 100644 facedetection/tests.py create mode 100644 facedetection/urls.py create mode 100644 facedetection/views.py create mode 100644 geofencing/__init__.py create mode 100644 geofencing/admin.py create mode 100644 geofencing/apps.py create mode 100644 geofencing/migrations/__init__.py create mode 100644 geofencing/models.py create mode 100644 geofencing/serializers.py create mode 100644 geofencing/tests.py create mode 100644 geofencing/urls.py create mode 100644 geofencing/views.py diff --git a/facedetection/__init__.py b/facedetection/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/facedetection/admin.py b/facedetection/admin.py new file mode 100644 index 000000000..9acbbea5b --- /dev/null +++ b/facedetection/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from .models import * + +# Register your models here. + +admin.site.register(FaceDetection) +admin.site.register(EmployeeFaceDetection) \ No newline at end of file diff --git a/facedetection/apps.py b/facedetection/apps.py new file mode 100644 index 000000000..6d9bdabfb --- /dev/null +++ b/facedetection/apps.py @@ -0,0 +1,15 @@ +from django.apps import AppConfig + + +class FacedetectionConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'facedetection' + + def ready(self): + from django.urls import include, path + from horilla.urls import urlpatterns + + urlpatterns.append( + path("api/facedetection/", include("facedetection.urls")), + ) + super().ready() \ No newline at end of file diff --git a/facedetection/migrations/__init__.py b/facedetection/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/facedetection/models.py b/facedetection/models.py new file mode 100644 index 000000000..5662f1f40 --- /dev/null +++ b/facedetection/models.py @@ -0,0 +1,20 @@ +from django.db import models + +# Create your models here. + +class FaceDetection(models.Model): + company_id = models.OneToOneField("base.Company", related_name="face_detection", on_delete=models.CASCADE) + start = models.BooleanField(default=False) + +class EmployeeFaceDetection(models.Model): + employee_id = models.OneToOneField("employee.Employee", related_name="face_detection", on_delete=models.CASCADE) + image = models.ImageField() + + + + + + + + + diff --git a/facedetection/serializers.py b/facedetection/serializers.py new file mode 100644 index 000000000..6b295dc3e --- /dev/null +++ b/facedetection/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers +from .models import * + + +class EmployeeFaceDetectionSerializer(serializers.ModelSerializer): + class Meta: + model = EmployeeFaceDetection + fields = '__all__' \ No newline at end of file diff --git a/facedetection/tests.py b/facedetection/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/facedetection/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/facedetection/urls.py b/facedetection/urls.py new file mode 100644 index 000000000..4158c56ec --- /dev/null +++ b/facedetection/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from .views import * + + +urlpatterns = [ + path("setup/", FaceDetectionGetPostAPIView.as_view()) +] diff --git a/facedetection/views.py b/facedetection/views.py new file mode 100644 index 000000000..4360e0d4c --- /dev/null +++ b/facedetection/views.py @@ -0,0 +1,30 @@ +from django.shortcuts import render +from rest_framework.views import APIView +from .serializers import * +from rest_framework.response import Response +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator +from django.http import QueryDict + + + +class FaceDetectionGetPostAPIView(APIView): + permission_classes = [IsAuthenticated] + + @method_decorator(csrf_exempt) + def dispatch(self, *args, **kwargs): + return super().dispatch(*args, **kwargs) + + def post(self, request): + employee_id = request.user.employee_get.id + data = request.data + if isinstance(data, QueryDict): + data = data.dict() + data["employee_id"] = employee_id + serializer = EmployeeFaceDetectionSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/geofencing/__init__.py b/geofencing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/geofencing/admin.py b/geofencing/admin.py new file mode 100644 index 000000000..c73ca1051 --- /dev/null +++ b/geofencing/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import GeoFencing +# Register your models here. + +admin.site.register(GeoFencing) diff --git a/geofencing/apps.py b/geofencing/apps.py new file mode 100644 index 000000000..d0278445b --- /dev/null +++ b/geofencing/apps.py @@ -0,0 +1,15 @@ +from django.apps import AppConfig + + +class GeofencingConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'geofencing' + + def ready(self): + from django.urls import include, path + from horilla.urls import urlpatterns + + urlpatterns.append( + path("api/geofencing/", include("geofencing.urls")), + ) + super().ready() diff --git a/geofencing/migrations/__init__.py b/geofencing/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/geofencing/models.py b/geofencing/models.py new file mode 100644 index 000000000..e5e34df1a --- /dev/null +++ b/geofencing/models.py @@ -0,0 +1,31 @@ +from django.db import models +from base.models import Company +from django.core.exceptions import ValidationError +from geopy.geocoders import Nominatim + + + +class GeoFencing(models.Model): + latitude = models.FloatField(max_length=100) + longitude = models.FloatField(max_length=100) + radius_in_meters = models.IntegerField() + company_id = models.OneToOneField("base.Company", related_name="geo_fencing", on_delete=models.CASCADE, blank=True, null=True) + start = models.BooleanField(default=False) + + + def clean(self): + geolocator = Nominatim(user_agent="geo_checker") # Use a unique user-agent + try: + location = geolocator.reverse((self.latitude, self.longitude), exactly_one=True) + if location: + pass + else: + raise ValidationError("Invalid Location") + except Exception as e: + raise ValidationError(e) + return super().clean() + + + def save_base(self, raw = ..., force_insert = ..., force_update = ..., using = ..., update_fields = ...): + self.clean() + return super().save_base(raw, force_insert, force_update, using, update_fields) \ No newline at end of file diff --git a/geofencing/serializers.py b/geofencing/serializers.py new file mode 100644 index 000000000..412579265 --- /dev/null +++ b/geofencing/serializers.py @@ -0,0 +1,27 @@ +from rest_framework import serializers +from .models import GeoFencing +from geopy.geocoders import Nominatim + +class GeoFencingSetupSerializer(serializers.ModelSerializer): + class Meta: + model = GeoFencing + fields = '__all__' + + def validate(self, data): + geolocator = Nominatim(user_agent="geo_checker") # Use a unique user-agent + try: + latitude = data.get("latitude") + longitude = data.get("longitude") + location = geolocator.reverse((latitude, longitude), exactly_one=True) + if not location: + raise serializers.ValidationError("Invalid Location") + except Exception as e: + raise serializers.ValidationError(e) + return data + + +class EmployeeLocationSerializer(serializers.ModelSerializer): + class Meta: + model = GeoFencing + fields = ['latitude', 'longitude'] + diff --git a/geofencing/tests.py b/geofencing/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/geofencing/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/geofencing/urls.py b/geofencing/urls.py new file mode 100644 index 000000000..8453f9069 --- /dev/null +++ b/geofencing/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from .views import * + + +urlpatterns = [ + path("setup/", GeoFencingSetupGetPostAPIView.as_view()), + path("setup//", GeoFencingSetupPutDeleteAPIView.as_view()), + path("setup-check/", GeoFencingSetUpPermissionCheck.as_view()), +] diff --git a/geofencing/views.py b/geofencing/views.py new file mode 100644 index 000000000..c5e9bcacd --- /dev/null +++ b/geofencing/views.py @@ -0,0 +1,128 @@ +from geopy.distance import geodesic +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from .models import GeoFencing +from .serializers import * +from rest_framework.permissions import IsAuthenticated +from rest_framework.pagination import PageNumberPagination +from django.contrib.auth.decorators import permission_required +from django.utils.decorators import method_decorator +from django.http import QueryDict + + + +class GeoFencingSetupGetPostAPIView(APIView): + permission_classes = [IsAuthenticated] + + @method_decorator( + permission_required("geofencing.view_geofencing", raise_exception=True), name="dispatch" + ) + def get(self, request): + if request.user.is_superuser: + location = GeoFencing.objects.all() + else: + company = request.user.employee_get.get_company() + location = company.geo_fencing.all() + paginator = PageNumberPagination() + page = paginator.paginate_queryset(location, request) + serializer = GeoFencingSetupSerializer(page, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + + + @method_decorator( + permission_required("geofencing.add_geofencing", raise_exception=True), name="dispatch" + ) + def post(self, request): + data = request.data + if not request.user.is_superuser: + if isinstance(data, QueryDict): + data = data.dict() + company = request.user.employee_get.get_company() + if company: + data["company_id"] = company.id + serializer = GeoFencingSetupSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class GeoFencingSetupPutDeleteAPIView(APIView): + permission_classes = [IsAuthenticated] + + + def get_location(self, pk): + try: + return GeoFencing.objects.get(pk=pk) + except Exception as e: + raise serializers.ValidationError(e) + + + @method_decorator( + permission_required("geofencing.change_geofencing", raise_exception=True), name="dispatch" + ) + def put(self, request, pk): + location = self.get_location(pk) + company = request.user.employee_get.get_company() + if request.user.is_superuser or company == location.company_id: + serializer = GeoFencingSetupSerializer(location, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + raise serializers.ValidationError("Access Denied..") + + + @method_decorator( + permission_required("geofencing.delete_geofencing", raise_exception=True), name="dispatch" + ) + def delete(self, request, pk): + location = self.get_location(pk) + company = request.user.employee_get.get_company() + if request.user.is_superuser or company == location.company_id: + location.delete() + return Response({"message": "GeoFencing location deleted successfully"}, status=status.HTTP_204_NO_CONTENT) + raise serializers.ValidationError("Access Denied..") + + + +class GeoFencingEmployeeLocationCheckAPIView(APIView): + permission_classes = [IsAuthenticated] + + def get_company(self, request): + try: + company = request.user.employee_get.get_company() + return company + except Exception as e: + raise serializers.ValidationError(e) + + def get_company_location(self, request): + company = self.get_company(request) + try: + location = GeoFencing.objects.get(company_id=company) + return location + except Exception as e: + raise serializers.ValidationError(e) + + def post(self, request): + serializer = EmployeeLocationSerializer(data=request.data) + if serializer.is_valid(): + company_location = self.get_company_location(request) + geofence_center = (company_location.latitude, company_location.longitude) + employee_location = (request.data.get("latitude"), request.data.get("longitude")) + distance = geodesic(geofence_center, employee_location).meters + if distance <= company_location.radius_in_meters: + return Response({"message": "Inside the geofence"}, status=status.HTTP_200_OK) + return Response({"message": "Outside the geofence"}, status=status.HTTP_400_BAD_REQUEST) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class GeoFencingSetUpPermissionCheck(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request): + geo_fencing = GeoFencingSetupGetPostAPIView() + if geo_fencing.get(request).status_code == 200: + return Response(status=200) + return Response(status=400) \ No newline at end of file diff --git a/horilla_api/__init__.py b/horilla_api/__init__.py index e69de29bb..cb93e1035 100644 --- a/horilla_api/__init__.py +++ b/horilla_api/__init__.py @@ -0,0 +1,4 @@ +from horilla.settings import INSTALLED_APPS + +INSTALLED_APPS.append("geofencing") +INSTALLED_APPS.append("facedetection")