diff --git a/attendance/templates/attendance/geofaceconfig/geo_face_config.html b/attendance/templates/attendance/geofaceconfig/geo_face_config.html
new file mode 100644
index 000000000..2956c62f0
--- /dev/null
+++ b/attendance/templates/attendance/geofaceconfig/geo_face_config.html
@@ -0,0 +1,14 @@
+
+{% extends "settings.html" %}
+{% block settings %}
+
+
+
+
+
+
+
+
+
+
+{% endblock settings %}
\ No newline at end of file
diff --git a/attendance/urls.py b/attendance/urls.py
index 6d392df82..56e5fd8b2 100644
--- a/attendance/urls.py
+++ b/attendance/urls.py
@@ -9,6 +9,7 @@ from django.urls import path
import attendance.views.clock_in_out
import attendance.views.dashboard
+import attendance.views.geofaceconfig
import attendance.views.penalty
import attendance.views.requests
import attendance.views.search
@@ -527,4 +528,9 @@ urlpatterns = [
"field_name_pre": "ip_address",
},
),
+ path(
+ "settings/geo-face-config/",
+ attendance.views.geofaceconfig.geofaceconfig,
+ name="geo-face-config",
+ ),
]
diff --git a/attendance/views/geofaceconfig.py b/attendance/views/geofaceconfig.py
new file mode 100644
index 000000000..ee11fd982
--- /dev/null
+++ b/attendance/views/geofaceconfig.py
@@ -0,0 +1,4 @@
+from django.shortcuts import render
+
+def geofaceconfig(request):
+ return render(request, "attendance/geofaceconfig/geo_face_config.html")
\ No newline at end of file
diff --git a/facedetection/forms.py b/facedetection/forms.py
new file mode 100644
index 000000000..470a91850
--- /dev/null
+++ b/facedetection/forms.py
@@ -0,0 +1,20 @@
+
+from base.forms import ModelForm
+from facedetection.models import FaceDetection
+from django.template.loader import render_to_string
+
+class FaceDetectionSetupForm(ModelForm):
+ verbose_name = "Facedetection Configuration"
+
+ class Meta:
+ model = FaceDetection
+ exclude = ["company_id"]
+
+ def as_p(self):
+ """
+ Render the form fields as HTML table rows with Bootstrap styling.
+ """
+ context = {"form": self}
+ table_html = render_to_string("common_form.html", context)
+ return table_html
+
\ No newline at end of file
diff --git a/facedetection/serializers.py b/facedetection/serializers.py
index b2dda4847..706b16f27 100644
--- a/facedetection/serializers.py
+++ b/facedetection/serializers.py
@@ -3,6 +3,12 @@ from rest_framework import serializers
from .models import *
+class FaceDetectionSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = FaceDetection
+ fields = "__all__"
+
+
class EmployeeFaceDetectionSerializer(serializers.ModelSerializer):
class Meta:
model = EmployeeFaceDetection
diff --git a/facedetection/templates/face_config.html b/facedetection/templates/face_config.html
new file mode 100644
index 000000000..c0b05ad70
--- /dev/null
+++ b/facedetection/templates/face_config.html
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/facedetection/urls.py b/facedetection/urls.py
index 75338dcd0..54e9737db 100644
--- a/facedetection/urls.py
+++ b/facedetection/urls.py
@@ -2,4 +2,8 @@ from django.urls import path
from .views import *
-urlpatterns = [path("setup/", FaceDetectionGetPostAPIView.as_view())]
+urlpatterns = [
+ path("config/", FaceDetectionConfigAPIView.as_view()),
+ path("setup/", EmployeeFaceDetectionGetPostAPIView.as_view()),
+ path("", face_detection_config, name="face-config"),
+]
diff --git a/facedetection/views.py b/facedetection/views.py
index e605c0eae..16609db6e 100644
--- a/facedetection/views.py
+++ b/facedetection/views.py
@@ -1,30 +1,166 @@
+from django.contrib import messages
+from django.contrib.auth.decorators import login_required, permission_required
from django.http import QueryDict
-from django.shortcuts import render
+from django.shortcuts import redirect, render
from django.utils.decorators import method_decorator
+from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
+from facedetection.forms import FaceDetectionSetupForm
+from horilla.decorators import hx_request_required
+
from .serializers import *
-class FaceDetectionGetPostAPIView(APIView):
+class FaceDetectionConfigAPIView(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_facedetection(self, request):
+ company = self.get_company(request)
+ try:
+ facedetection = FaceDetection.objects.get(company_id=company)
+ return facedetection
+ except Exception as e:
+ raise serializers.ValidationError(e)
+
+ @method_decorator(
+ permission_required("facedetection.view_facedetection", raise_exception=True),
+ name="dispatch",
+ )
+ def get(self, request):
+ serializer = FaceDetectionSerializer(self.get_facedetection(request))
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+ @method_decorator(
+ permission_required("facedetection.add_facedetection", raise_exception=True),
+ name="dispatch",
+ )
+ def post(self, request):
+ data = request.data
+ if isinstance(data, QueryDict):
+ data = data.dict()
+ data["company_id"] = self.get_company(request).id
+ serializer = FaceDetectionSerializer(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)
+
+ @method_decorator(
+ permission_required("facedetection.change_facedetection", raise_exception=True),
+ name="dispatch",
+ )
+ def put(self, request):
+ data = request.data
+ if isinstance(data, QueryDict):
+ data = data.dict()
+ data["company_id"] = self.get_company(request).id
+ serializer = FaceDetectionSerializer(self.get_facedetection(request), data=data)
+ 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)
+
+ @method_decorator(
+ permission_required("facedetection.delete_facedetection", raise_exception=True),
+ name="dispatch",
+ )
+ def delete(self, request):
+ self.get_facedetection(request).delete()
+ return Response(
+ {"message": "Facedetection deleted successfully"}, status=status.HTTP_200_OK
+ )
+
+
+class EmployeeFaceDetectionGetPostAPIView(APIView):
permission_classes = [IsAuthenticated]
@method_decorator(csrf_exempt)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
+ 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_facedetection(self, request):
+ company = self.get_company(request)
+ try:
+ facedetection = FaceDetection.objects.get(company_id=company)
+ return facedetection
+ except Exception as e:
+ raise serializers.ValidationError(e)
+
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)
+ if self.get_facedetection(request).start:
+ 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)
+ raise serializers.ValidationError("Facedetection not yet started..")
+
+
+def get_company(request):
+ try:
+ company = request.user.employee_get.get_company()
+ return company
+ except Exception as e:
+ raise serializers.ValidationError(e)
+
+
+def get_facedetection(request):
+ company = get_company(request)
+ try:
+ location = FaceDetection.objects.get(company_id=company)
+ return location
+ except Exception as e:
+ raise serializers.ValidationError(e)
+
+
+@login_required
+@permission_required("geofencing.add_localbackup")
+@hx_request_required
+def face_detection_config(request):
+ print(">>>>>>>>>>>>>>>>>>>>>")
+ try:
+ form = FaceDetectionSetupForm(instance=get_facedetection(request))
+ except:
+ form = FaceDetectionSetupForm()
+
+ if request.method == "POST":
+ try:
+ form = FaceDetectionSetupForm(
+ request.POST, instance=get_facedetection(request)
+ )
+ except:
+ form = FaceDetectionSetupForm(request.POST)
+ if form.is_valid():
+ facedetection = form.save(
+ commit=False,
+ )
+ facedetection.company_id = get_company(request)
+ facedetection.save()
+ messages.success(request, _("facedetection config created successfully."))
+ else:
+ messages.info(request, "Not valid")
+ return render(request, "face_config.html", {"form": form})
diff --git a/geofencing/forms.py b/geofencing/forms.py
new file mode 100644
index 000000000..f926680a5
--- /dev/null
+++ b/geofencing/forms.py
@@ -0,0 +1,20 @@
+
+from base.forms import ModelForm
+from .models import GeoFencing
+from django.template.loader import render_to_string
+
+class GeoFencingSetupForm(ModelForm):
+ verbose_name = "Geofence Configuration"
+
+ class Meta:
+ model = GeoFencing
+ exclude = ["company_id"]
+
+ def as_p(self):
+ """
+ Render the form fields as HTML table rows with Bootstrap styling.
+ """
+ context = {"form": self}
+ table_html = render_to_string("common_form.html", context)
+ return table_html
+
\ No newline at end of file
diff --git a/geofencing/templates/geo_config.html b/geofencing/templates/geo_config.html
new file mode 100644
index 000000000..b2ea3a134
--- /dev/null
+++ b/geofencing/templates/geo_config.html
@@ -0,0 +1,5 @@
+
diff --git a/geofencing/urls.py b/geofencing/urls.py
index 85d049bfd..86066b0dd 100644
--- a/geofencing/urls.py
+++ b/geofencing/urls.py
@@ -6,4 +6,6 @@ urlpatterns = [
path("setup/", GeoFencingSetupGetPostAPIView.as_view()),
path("setup//", GeoFencingSetupPutDeleteAPIView.as_view()),
path("setup-check/", GeoFencingSetUpPermissionCheck.as_view()),
+ path("location-check/", GeoFencingEmployeeLocationCheckAPIView.as_view()),
+ path("config/", geo_location_config, name="geo-config"),
]
diff --git a/geofencing/views.py b/geofencing/views.py
index 90ce0196e..dc3dd962c 100644
--- a/geofencing/views.py
+++ b/geofencing/views.py
@@ -1,6 +1,9 @@
-from django.contrib.auth.decorators import permission_required
+from django.contrib import messages
+from django.contrib.auth.decorators import login_required, permission_required
from django.http import QueryDict
+from django.shortcuts import redirect, render
from django.utils.decorators import method_decorator
+from django.utils.translation import gettext_lazy as _
from geopy.distance import geodesic
from rest_framework import status
from rest_framework.pagination import PageNumberPagination
@@ -8,6 +11,8 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
+from geofencing.forms import GeoFencingSetupForm
+
from .models import GeoFencing
from .serializers import *
@@ -86,7 +91,7 @@ class GeoFencingSetupPutDeleteAPIView(APIView):
location.delete()
return Response(
{"message": "GeoFencing location deleted successfully"},
- status=status.HTTP_204_NO_CONTENT,
+ status=status.HTTP_200_OK,
)
raise serializers.ValidationError("Access Denied..")
@@ -111,22 +116,28 @@ class GeoFencingEmployeeLocationCheckAPIView(APIView):
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
+ company_location = self.get_company_location(request)
+ if company_location.start:
+ if serializer.is_valid():
+ geofence_center = (
+ company_location.latitude,
+ company_location.longitude,
)
- return Response(
- {"message": "Outside the geofence"}, status=status.HTTP_400_BAD_REQUEST
- )
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+ 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)
+ raise serializers.ValidationError("Geofencing is not yet started..")
class GeoFencingSetUpPermissionCheck(APIView):
@@ -137,3 +148,44 @@ class GeoFencingSetUpPermissionCheck(APIView):
if geo_fencing.get(request).status_code == 200:
return Response(status=200)
return Response(status=400)
+
+
+def get_company(request):
+ try:
+ company = request.user.employee_get.get_company()
+ return company
+ except Exception as e:
+ raise serializers.ValidationError(e)
+
+
+def get_company_location(request):
+ company = get_company(request)
+ try:
+ location = GeoFencing.objects.get(company_id=company)
+ return location
+ except Exception as e:
+ raise serializers.ValidationError(e)
+
+
+@login_required
+@permission_required("geofencing.add_localbackup")
+def geo_location_config(request):
+ try:
+ form = GeoFencingSetupForm(instance=get_company_location(request))
+ except:
+ form = GeoFencingSetupForm()
+ if request.method == "POST":
+ try:
+ form = GeoFencingSetupForm(
+ request.POST, instance=get_company_location(request)
+ )
+ except:
+ form = GeoFencingSetupForm(request.POST)
+ if form.is_valid():
+ geofencing = form.save(commit=False)
+ geofencing.company_id = get_company(request)
+ geofencing.save()
+ messages.success(request, _("Geofencing config created successfully."))
+ else:
+ messages.info(request, "Not valid")
+ return render(request, "geo_config.html", {"form": form})
diff --git a/static/index/index.js b/static/index/index.js
index 4b572f1ca..032f0f1cc 100644
--- a/static/index/index.js
+++ b/static/index/index.js
@@ -40,6 +40,15 @@ function addToSelectedId(newIds, storeKey) {
$(`#${storeKey}`).attr("data-ids", JSON.stringify(ids));
}
+function togglePublicComments() {
+ if ($('#id_disable_comments').is(':checked')) {
+ $('#id_public_comments').prop('checked', false);
+ $('#id_public_comments_parent_div').hide();
+ } else {
+ $('#id_public_comments_parent_div').show();
+ }
+}
+
function attendanceDateChange(selectElement) {
var selectedDate = selectElement.val();
let parentForm = selectElement.parents().closest("form");
@@ -324,7 +333,7 @@ function checkSequence(element) {
if (
stageOrder.indexOf(parseInt(stageId)) !=
- stageOrder.indexOf(parseInt(preStageId)) + 1 &&
+ stageOrder.indexOf(parseInt(preStageId)) + 1 &&
stage.type != "cancelled"
) {
Swal.fire({
diff --git a/templates/403.html b/templates/403.html
index 39e227bf1..2d65f2b7b 100644
--- a/templates/403.html
+++ b/templates/403.html
@@ -152,4 +152,4 @@