from rest_framework import serializers from .models import Prediction, SavedPoint, SavedRateProfile, PreditctionTemplate from datetime import datetime from .validators import ( validate_custom_curve, rate_clip, _rfc3339_to_timestamp, base64_to_curve ) class PredictionSerializer(serializers.ModelSerializer): class Meta: model = Prediction fields = ['id', 'created_at', 'updated_at', 'result'] PROFILE_STANDARD = "standard_profile" PROFILE_FLOAT = "float_profile" PROFILE_REVERSE = "reverse_profile" PROFILE_CUSTOM = "custom_profile" LATEST_DATASET_KEYWORD = "latest" SUPPORTED_PROFILES = [PROFILE_STANDARD, PROFILE_FLOAT, PROFILE_REVERSE, PROFILE_CUSTOM] class PredictionRequestSerializer(serializers.Serializer): launch_latitude = serializers.FloatField(min_value=-90, max_value=90) launch_longitude = serializers.FloatField(min_value=0, max_value=360) launch_datetime = serializers.DateTimeField() launch_altitude = serializers.FloatField(required=False) format = serializers.CharField(default="json") profile = serializers.ChoiceField(choices=SUPPORTED_PROFILES, default=PROFILE_STANDARD) dataset = serializers.CharField(default=LATEST_DATASET_KEYWORD) # --- профиль-dependent поля --- ascent_rate = serializers.FloatField(required=False, min_value=0.01) descent_rate = serializers.FloatField(required=False, min_value=0.01) burst_altitude = serializers.FloatField(required=False) float_altitude = serializers.FloatField(required=False) stop_datetime = serializers.DateTimeField(required=False) ascent_curve = serializers.CharField(required=False) descent_curve = serializers.CharField(required=False) interpolate = serializers.BooleanField(required=False, default=False) start_point = serializers.PrimaryKeyRelatedField( queryset=SavedPoint.objects.all(), required=False, allow_null=True ) rate_profile = serializers.PrimaryKeyRelatedField( queryset=SavedRateProfile.objects.all(), required=False, allow_null=True ) template = serializers.PrimaryKeyRelatedField( queryset=PreditctionTemplate.objects.all(), required=False, allow_null=True ) def validate(self, data): profile = data.get("profile", PROFILE_STANDARD) launch_alt = data.get("launch_altitude", 0) if profile == PROFILE_STANDARD: if 'burst_altitude' not in data: raise serializers.ValidationError("burst_altitude is required for standard profile.") if data['burst_altitude'] <= launch_alt: raise serializers.ValidationError("burst_altitude must be greater than launch_altitude.") elif profile == PROFILE_FLOAT: if 'float_altitude' not in data or data['float_altitude'] <= launch_alt: raise serializers.ValidationError("float_altitude must be greater than launch_altitude.") if 'stop_datetime' not in data or data['stop_datetime'] <= data['launch_datetime']: raise serializers.ValidationError("stop_datetime must be later than launch_datetime.") elif profile == PROFILE_CUSTOM: if 'ascent_curve' not in data or not validate_custom_curve(data['ascent_curve']): raise serializers.ValidationError("Invalid ascent_curve.") if 'descent_curve' not in data or not validate_custom_curve(data['descent_curve']): raise serializers.ValidationError("Invalid descent_curve.") if 'burst_altitude' not in data or data['burst_altitude'] <= launch_alt: raise serializers.ValidationError("burst_altitude must be greater than launch_altitude.") # кастомная логика clipping'а if 'ascent_rate' in data: data['ascent_rate'] = rate_clip(data['ascent_rate']) if 'descent_rate' in data: data['descent_rate'] = rate_clip(data['descent_rate']) return data def create(self, validated_data): if 'ascent_curve' in validated_data: validated_data['ascent_curve'] = base64_to_curve(validated_data['ascent_curve']) if 'descent_curve' in validated_data: validated_data['descent_curve'] = base64_to_curve(validated_data['descent_curve']) prediction = Prediction( user=validated_data.get('user'), request=validated_data.get('request', {}), result=validated_data.get('result', {}), start_point=validated_data.get('start_point'), template=validated_data.get('template'), rate_profile=validated_data.get('rate_profile') ) prediction.save() return prediction class PredictionListSerializer(serializers.ModelSerializer): class Meta: model = Prediction fields = ["id", "created_at", "updated_at", "start_point", "template", "rate_profile"] class PredictionDetailSerializer(serializers.ModelSerializer): class Meta: model = Prediction fields = ["id", "created_at", "updated_at", "result", "start_point", "template", "rate_profile"] from rest_framework import serializers from .models import TelemetryPacket class TelemetryPacketSerializer(serializers.ModelSerializer): class Meta: model = TelemetryPacket fields = ['id', 'timestamp', 'lat', 'lon', 'alt', 'payload'] read_only_fields = ['id'] class SavedPointSerializer(serializers.ModelSerializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) class Meta: model = SavedPoint fields = ['user', 'id', 'name', 'lat', 'lon', 'alt'] read_only_fields = ['id'] validators = [ serializers.UniqueTogetherValidator( queryset=SavedPoint.objects.all(), fields=['user', 'name'], message="A saved point with this name already exists for the user." ) ] class SavedRateProfileSerializer(serializers.ModelSerializer): class Meta: model = SavedRateProfile fields = ['id', 'name', 'rate_profile_data'] read_only_fields = ['id'] class PreditctionTemplateSerializer(serializers.ModelSerializer): class Meta: model = PreditctionTemplate fields = ['id', 'name', 'template_data', 'is_default'] read_only_fields = ['id']