from datetime import time as datetime_time from uuid import uuid4 from django.contrib.auth import get_user_model from django.db import transaction from rest_framework import serializers from users.serializers import UserSerializer from .models import TimeEntry User = get_user_model() class TimeEntrySerializer(serializers.ModelSerializer): user = UserSerializer(read_only=True) is_complete = serializers.ReadOnlyField() is_incomplete = serializers.ReadOnlyField() class Meta: model = TimeEntry fields = [ 'id', 'user', 'group_id', 'date', 'start_time', 'end_time', 'created_at', 'updated_at', 'duration', 'day_of_week', 'is_complete', 'is_incomplete', ] read_only_fields = [ 'id', 'user', 'created_at', 'updated_at', 'duration', 'day_of_week', 'is_complete', 'is_incomplete', ] def create(self, validated_data): validated_data['user'] = self.context['request'].user return super().create(validated_data) def update(self, instance, validated_data): validated_data.pop('user', None) return super().update(instance, validated_data) class TimeEntryCreateSerializer(serializers.ModelSerializer): user_id = serializers.PrimaryKeyRelatedField( source='user', queryset=User.objects.all(), write_only=True, required=False, ) end_date = serializers.DateField( write_only=True, required=False, ) class Meta: model = TimeEntry fields = [ 'user_id', 'date', 'end_date', 'start_time', 'end_time', ] def validate(self, attrs): start_time = attrs.get('start_time') end_time = attrs.get('end_time') date = attrs.get('date') end_date = attrs.get('end_date') or date if not start_time and not end_time: raise serializers.ValidationError( 'Une heure d’entrée ou de sortie est requise.' ) if end_date < date: raise serializers.ValidationError( 'La date de sortie doit être postérieure ou égale à la date d’entrée.' ) # Une entrée et une sortie le même jour doivent être cohérentes. if ( date == end_date and start_time and end_time and end_time <= start_time ): raise serializers.ValidationError( 'L’heure de sortie doit être après l’entrée.' ) # Pour un pointage sur plusieurs jours, les deux extrémités # doivent être connues. if ( date != end_date and ( not start_time or not end_time ) ): raise serializers.ValidationError( 'Un pointage sur plusieurs jours nécessite une heure d’entrée et une heure de sortie.' ) attrs['end_date'] = end_date return attrs def create(self, validated_data): user = validated_data.pop('user', None) end_date = validated_data.pop('end_date') if user is None: user = self.context['request'].user start_date = validated_data['date'] start_time = validated_data.get('start_time') end_time = validated_data.get('end_time') # Pointage classique sur une seule journée. if start_date == end_date: return TimeEntry.objects.create( user=user, date=start_date, start_time=start_time, end_time=end_time, ) # Pointage multi-jours : # # Exemple : # 20/09 14:00 -> 22/09 10:00 # # 20/09 14:00 -> 23:59:59 # 21/09 00:00 -> 23:59:59 # 22/09 00:00 -> 10:00 group_id = uuid4() created_entries = [] current_date = start_date with transaction.atomic(): while current_date <= end_date: if current_date == start_date: entry_start = start_time entry_end = datetime_time( 23, 59, 59, ) elif current_date == end_date: entry_start = datetime_time.min entry_end = end_time else: entry_start = datetime_time.min entry_end = datetime_time( 23, 59, 59, ) created_entries.append( TimeEntry.objects.create( user=user, group_id=group_id, date=current_date, start_time=entry_start, end_time=entry_end, ) ) from datetime import timedelta current_date += timedelta(days=1) return created_entries[0] class TimeEntryUpdateSerializer(serializers.ModelSerializer): class Meta: model = TimeEntry fields = [ 'start_time', 'end_time', ] def validate(self, attrs): start_time = attrs.get( 'start_time', self.instance.start_time, ) end_time = attrs.get( 'end_time', self.instance.end_time, ) if not start_time and not end_time: raise serializers.ValidationError( 'Une heure d’entrée ou de sortie est requise.' ) if ( start_time and end_time and end_time <= start_time ): raise serializers.ValidationError( 'L’heure de sortie doit être après l’entrée.' ) return attrs class WeeklySummarySerializer(serializers.Serializer): user = serializers.CharField() username = serializers.CharField() total_hours = serializers.FloatField() entries = serializers.ListField( child=serializers.DictField(), )