from rest_framework import serializers from .models import JobApplication, JobUpdate class SanitizedCharField(serializers.CharField): """CharField that strips control characters on deserialization.""" def to_internal_value(self, data: object) -> object: value = super().to_internal_value(data) if isinstance(value, str): value = value.strip() if "\x00" in value: raise serializers.ValidationError("Input contains invalid characters.") import re value = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f]", "", value) return value class JobUpdateSerializer(serializers.ModelSerializer): company_name = serializers.CharField(source="job_application.company_name", read_only=True) position_title = serializers.CharField(source="job_application.position_title", read_only=True) job_id = serializers.IntegerField(source="job_application.id", read_only=True) class Meta: model = JobUpdate fields = [ "id", "job_id", "company_name", "position_title", "from_status", "to_status", "notes", "created_at", "metrics", ] class JobApplicationSerializer(serializers.ModelSerializer): user_id = serializers.IntegerField(read_only=True) notes = SanitizedCharField(required=False, allow_blank=True, default="") company_name = SanitizedCharField(max_length=255) position_title = SanitizedCharField(max_length=255) class Meta: model = JobApplication fields = [ "id", "user_id", "company_name", "position_title", "status", "notes", "created_at", "updated_at", ] read_only_fields = ["user_id"] def create(self, validated_data): validated_data["user"] = self.context["request"].user return super().create(validated_data) class DashboardMetricsSerializer(serializers.Serializer): total_applications = serializers.IntegerField() status_breakdown = serializers.DictField(child=serializers.IntegerField()) interviews_count = serializers.IntegerField() offers_count = serializers.IntegerField() rejection_rate = serializers.FloatField()