from rest_framework import serializers from django.contrib.auth.password_validation import validate_password from .models import ComPortReading, User from vehicles.models import Vehicle, VehicleExtra from nomenclatures.models import Nomenclature, NomenclatureField class UserSerializer(serializers.ModelSerializer): password = serializers.CharField(write_only=True, required=False) class Meta: model = User fields = ['id', 'username', 'email', 'first_name', 'last_name', 'role', 'is_admin', 'is_active', 'date_joined', 'password'] read_only_fields = ['id', 'date_joined'] extra_kwargs = { 'password': {'write_only': True} } def create(self, validated_data): password = validated_data.pop('password', None) user = User(**validated_data) if password: user.set_password(password) user.save() return user def update(self, instance, validated_data): password = validated_data.pop('password', None) for attr, value in validated_data.items(): setattr(instance, attr, value) if password: instance.set_password(password) instance.save() return instance class UserDetailSerializer(serializers.ModelSerializer): """Serializer for current user details (excludes password)""" class Meta: model = User fields = ['id', 'username', 'email', 'first_name', 'last_name', 'role', 'is_admin', 'is_active', 'date_joined'] read_only_fields = ['id', 'date_joined'] class ChangePasswordSerializer(serializers.Serializer): """Serializer for password change endpoint""" old_password = serializers.CharField(required=True, write_only=True) new_password = serializers.CharField(required=True, write_only=True) def validate_old_password(self, value): user = self.context['request'].user if not user.check_password(value): raise serializers.ValidationError("Old password is incorrect") return value def validate_new_password(self, value): # Use Django's password validators validate_password(value, self.context['request'].user) return value def save(self, **kwargs): user = self.context['request'].user user.set_password(self.validated_data['new_password']) user.save() return user class ComPortReadingSerializer(serializers.ModelSerializer): class Meta: model = ComPortReading fields = ['id', 'port', 'data', 'timestamp', 'source_ip'] read_only_fields = ['id', 'timestamp'] class VehicleExtraSerializer(serializers.ModelSerializer): class Meta: model = VehicleExtra fields = ['id', 'data'] read_only_fields = ['id'] class VehicleSerializer(serializers.ModelSerializer): extra = VehicleExtraSerializer(required=False, allow_null=True) class Meta: model = Vehicle fields = ['id', 'vehicle_number', 'extra'] read_only_fields = ['id'] def create(self, validated_data): extra_data = validated_data.pop('extra', None) vehicle = Vehicle.objects.create(**validated_data) if extra_data: VehicleExtra.objects.create(vehicle=vehicle, **extra_data) return vehicle def update(self, instance, validated_data): extra_data = validated_data.pop('extra', None) # Update Vehicle fields instance.vehicle_number = validated_data.get('vehicle_number', instance.vehicle_number) instance.save() # Handle VehicleExtra update/creation if extra_data is not None: if hasattr(instance, 'extra'): # Update existing VehicleExtra for attr, value in extra_data.items(): setattr(instance.extra, attr, value) instance.extra.save() else: # Create new VehicleExtra VehicleExtra.objects.create(vehicle=instance, **extra_data) return instance def validate_vehicle_number(self, value): if not value or not value.strip(): raise serializers.ValidationError("Vehicle number cannot be empty") return value.strip() class NomenclatureFieldSerializer(serializers.ModelSerializer): class Meta: model = NomenclatureField fields = ['id', 'key', 'field_type'] read_only_fields = ['id'] def validate_key(self, value): if not value or not value.strip(): raise serializers.ValidationError("Field key cannot be empty") return value.strip() def validate_field_type(self, value): valid_types = [choice[0] for choice in NomenclatureField.FIELD_TYPES] if value not in valid_types: raise serializers.ValidationError( f"Invalid field type. Must be one of: {', '.join(valid_types)}" ) return value class NomenclatureSerializer(serializers.ModelSerializer): fields = NomenclatureFieldSerializer(many=True, required=False, source='nomenclaturefield_set') class Meta: model = Nomenclature fields = ['id', 'code', 'name', 'applies_to', 'fields'] read_only_fields = ['id'] def create(self, validated_data): fields_data = validated_data.pop('nomenclaturefield_set', []) nomenclature = Nomenclature.objects.create(**validated_data) for field_data in fields_data: NomenclatureField.objects.create(nomenclature=nomenclature, **field_data) return nomenclature def update(self, instance, validated_data): fields_data = validated_data.pop('nomenclaturefield_set', None) # Update Nomenclature fields instance.code = validated_data.get('code', instance.code) instance.name = validated_data.get('name', instance.name) instance.applies_to = validated_data.get('applies_to', instance.applies_to) instance.save() # Handle fields update - replace all fields if fields_data is not None: # Delete existing fields instance.nomenclaturefield_set.all().delete() # Create new fields for field_data in fields_data: NomenclatureField.objects.create(nomenclature=instance, **field_data) return instance def validate_code(self, value): if not value or not value.strip(): raise serializers.ValidationError("Code cannot be empty") return value.strip() def validate_applies_to(self, value): valid_choices = ['vehicle', 'container'] if value not in valid_choices: raise serializers.ValidationError( f"Invalid applies_to value. Must be one of: {', '.join(valid_choices)}" ) return value