打卡接口
This commit is contained in:
parent
50ed7ea9ec
commit
509404994a
|
@ -0,0 +1,9 @@
|
||||||
|
from django_filters import rest_framework as filters
|
||||||
|
from apps.hrm.models import ClockRecord
|
||||||
|
|
||||||
|
class ClockRecordFilterSet(filters.FilterSet):
|
||||||
|
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
|
||||||
|
create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
|
||||||
|
class Meta:
|
||||||
|
model = ClockRecord
|
||||||
|
fields = ['create_by', 'create_time_start', 'create_time_end']
|
|
@ -36,8 +36,12 @@ class Employee(CommonAModel):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
# class Attendance(CommonADModel):
|
class ClockRecord(CommonADModel):
|
||||||
# """
|
"""
|
||||||
# 出勤记录
|
打卡记录
|
||||||
# """
|
"""
|
||||||
|
ClOCK_WORK1 = 10
|
||||||
|
type_choice = (
|
||||||
|
(ClOCK_WORK1, '上班打卡'),
|
||||||
|
)
|
||||||
|
type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_WORK1)
|
|
@ -1,7 +1,7 @@
|
||||||
from apps.system.models import User
|
from apps.system.models import User
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import Employee
|
from .models import ClockRecord, Employee
|
||||||
from apps.system.serializers import UserListSerializer, UserSimpleSerializer
|
from apps.system.serializers import UserListSerializer, UserSimpleSerializer
|
||||||
from django.db.models.query import Prefetch
|
from django.db.models.query import Prefetch
|
||||||
|
|
||||||
|
@ -23,3 +23,13 @@ class EmployeeSerializer(ModelSerializer):
|
||||||
|
|
||||||
class FaceLoginSerializer(serializers.Serializer):
|
class FaceLoginSerializer(serializers.Serializer):
|
||||||
base64 = serializers.CharField()
|
base64 = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class FaceClockCreateSerializer(serializers.Serializer):
|
||||||
|
base64 = serializers.CharField()
|
||||||
|
|
||||||
|
class ClockRecordListSerializer(serializers.ModelSerializer):
|
||||||
|
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
|
||||||
|
class Meta:
|
||||||
|
model = ClockRecord
|
||||||
|
fields = '__all__'
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
from django.conf import settings
|
||||||
|
import uuid
|
||||||
|
import face_recognition
|
||||||
|
import os
|
||||||
|
from apps.hrm.models import Employee
|
||||||
|
from apps.system.models import User
|
||||||
|
|
||||||
|
class HRMService:
|
||||||
|
|
||||||
|
def face_compare_from_base64(cls, base64_data):
|
||||||
|
filename = str(uuid.uuid4())
|
||||||
|
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
|
||||||
|
with open(filepath, 'wb') as f:
|
||||||
|
f.write(base64_data)
|
||||||
|
try:
|
||||||
|
unknown_picture = face_recognition.load_image_file(filepath)
|
||||||
|
unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
|
||||||
|
os.remove(filepath)
|
||||||
|
except:
|
||||||
|
os.remove(filepath)
|
||||||
|
return None, '头像解码失败'
|
||||||
|
|
||||||
|
# 匹配人脸库
|
||||||
|
user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data')
|
||||||
|
user_l = []
|
||||||
|
face_l = []
|
||||||
|
for i in user_faces:
|
||||||
|
user_l.append(i['user'])
|
||||||
|
face_l.append(i['face_data'])
|
||||||
|
|
||||||
|
results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5)
|
||||||
|
for index, value in enumerate(results):
|
||||||
|
if value:
|
||||||
|
# 识别成功
|
||||||
|
user = User.objects.get(id=user_l[index])
|
||||||
|
return user, ''
|
||||||
|
return None, '识别失败'
|
|
@ -1,11 +1,12 @@
|
||||||
from django.db.models import base
|
from django.db.models import base
|
||||||
from rest_framework import urlpatterns
|
from rest_framework import urlpatterns
|
||||||
from apps.hrm.views import EmployeeViewSet, FaceLogin
|
from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('employee', EmployeeViewSet, basename='employee')
|
router.register('employee', EmployeeViewSet, basename='employee')
|
||||||
|
router.register('clock_record', ClockRecordViewSet, basename='clock_record')
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('facelogin/', FaceLogin.as_view()),
|
path('facelogin/', FaceLogin.as_view()),
|
||||||
path('', include(router.urls)),
|
path('', include(router.urls)),
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from pytz import timezone
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||||
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin
|
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin
|
||||||
|
from apps.hrm.filters import ClockRecordFilterSet
|
||||||
|
from apps.hrm.services import HRMService
|
||||||
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||||
from apps.hrm.models import Employee
|
from apps.hrm.models import ClockRecord, Employee
|
||||||
from apps.hrm.serializers import EmployeeSerializer, FaceLoginSerializer
|
from apps.hrm.serializers import ClockRecordListSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer
|
||||||
import face_recognition
|
import face_recognition
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
@ -12,8 +15,9 @@ import logging
|
||||||
from rest_framework.generics import CreateAPIView
|
from rest_framework.generics import CreateAPIView
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework_simplejwt.tokens import RefreshToken
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
|
from rest_framework import exceptions
|
||||||
from apps.system.models import User
|
from apps.system.models import User
|
||||||
|
from apps.system.serializers import UserSimpleSerializer
|
||||||
logger = logging.getLogger('log')
|
logger = logging.getLogger('log')
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,9 +54,51 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
|
||||||
except:
|
except:
|
||||||
logger.error('人脸识别出错')
|
logger.error('人脸识别出错')
|
||||||
|
|
||||||
import uuid
|
|
||||||
|
class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
|
||||||
|
"""
|
||||||
|
打卡记录
|
||||||
|
"""
|
||||||
|
perms_map = {'get':'*', 'post':'*'}
|
||||||
|
authentication_classes = []
|
||||||
|
queryset = ClockRecord.objects.select_related('create_by').all()
|
||||||
|
serializer_class = ClockRecordListSerializer
|
||||||
|
filterset_class = ClockRecordFilterSet
|
||||||
|
ordering = ['-pk']
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
if self.action == 'create':
|
||||||
|
return FaceClockCreateSerializer
|
||||||
|
return super().get_serializer_class()
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+')))
|
||||||
|
user, msg = HRMService.face_compare_from_base64(base64_data)
|
||||||
|
if user:
|
||||||
|
now = timezone.now()
|
||||||
|
if 7<=now.hour<=9:
|
||||||
|
ins, created = ClockRecord.objects.get_or_create(
|
||||||
|
create_by=request.user, create_time__hour__in = [7,8],
|
||||||
|
create_time__year=now.year, create_time__month=now.month, create_time__day=now.day,
|
||||||
|
defaults={
|
||||||
|
'type':ClockRecord.ClOCK_WORK1,
|
||||||
|
'create_by':user,
|
||||||
|
'create_time':now
|
||||||
|
})
|
||||||
|
if not created:
|
||||||
|
ins.create_time = now
|
||||||
|
ins.save()
|
||||||
|
# 设为在岗
|
||||||
|
user.is_atwork = True
|
||||||
|
user.save()
|
||||||
|
return Response(UserSimpleSerializer(instance=user).data)
|
||||||
|
return Response('打卡失败', status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import os
|
|
||||||
|
|
||||||
def tran64(s):
|
def tran64(s):
|
||||||
missing_padding = len(s) % 4
|
missing_padding = len(s) % 4
|
||||||
|
@ -70,41 +116,13 @@ class FaceLogin(CreateAPIView):
|
||||||
"""
|
"""
|
||||||
人脸识别登录
|
人脸识别登录
|
||||||
"""
|
"""
|
||||||
# serializer = FaceLoginSerializer(data=request.data)
|
base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+')))
|
||||||
# serializer.is_valid(raise_exception=True)
|
user, msg = HRMService.face_compare_from_base64(base64_data)
|
||||||
filename = str(uuid.uuid4())
|
if user:
|
||||||
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
|
|
||||||
with open(filepath, 'wb') as f:
|
|
||||||
data = tran64(request.data.get('base64').replace(' ', '+'))
|
|
||||||
f.write(base64.urlsafe_b64decode(data))
|
|
||||||
# picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png')
|
|
||||||
# my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
|
||||||
#results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2)
|
|
||||||
try:
|
|
||||||
unknown_picture = face_recognition.load_image_file(filepath)
|
|
||||||
unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
|
|
||||||
os.remove(filepath)
|
|
||||||
except:
|
|
||||||
os.remove(filepath)
|
|
||||||
return Response('头像解码失败', status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# 匹配人脸库
|
|
||||||
user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data')
|
|
||||||
user_l = []
|
|
||||||
face_l = []
|
|
||||||
for i in user_faces:
|
|
||||||
user_l.append(i['user'])
|
|
||||||
face_l.append(i['face_data'])
|
|
||||||
|
|
||||||
results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5)
|
|
||||||
for index, value in enumerate(results):
|
|
||||||
if value:
|
|
||||||
# 识别成功
|
|
||||||
user = User.objects.get(id=user_l[index])
|
|
||||||
refresh = RefreshToken.for_user(user)
|
refresh = RefreshToken.for_user(user)
|
||||||
return Response({
|
return Response({
|
||||||
'refresh': str(refresh),
|
'refresh': str(refresh),
|
||||||
'access': str(refresh.access_token),
|
'access': str(refresh.access_token),
|
||||||
'username':user.username
|
'username':user.username
|
||||||
})
|
})
|
||||||
return Response('未找到对应用户', status=status.HTTP_400_BAD_REQUEST)
|
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
|
|
@ -9,6 +9,7 @@ from apps.mtm.serializers import MaterialSimpleSerializer
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WareHouseSerializer(serializers.ModelSerializer):
|
class WareHouseSerializer(serializers.ModelSerializer):
|
||||||
create_by_ = UserSimpleSerializer('create_by', read_only=True)
|
create_by_ = UserSimpleSerializer('create_by', read_only=True)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.2.9 on 2022-01-21 05:41
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('system', '0003_auto_20210812_0909'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='is_atwork',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='当前在岗'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -116,6 +116,7 @@ class User(AbstractUser):
|
||||||
superior = models.ForeignKey(
|
superior = models.ForeignKey(
|
||||||
'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管')
|
'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管')
|
||||||
roles = models.ManyToManyField(Role, blank=True, verbose_name='角色')
|
roles = models.ManyToManyField(Role, blank=True, verbose_name='角色')
|
||||||
|
is_atwork = models.BooleanField('当前在岗', default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = '用户信息'
|
verbose_name = '用户信息'
|
||||||
|
|
Loading…
Reference in New Issue