initial commit

This commit is contained in:
straitz 2025-08-15 00:46:18 +09:00
commit c6961c03c3
33 changed files with 1782 additions and 0 deletions

View file

@ -0,0 +1,185 @@
from rest_framework import serializers
from .models import Prediction, SavedPoint, SavedRateProfile, PreditctionTemplate
from datetime import datetime
from django.contrib.auth.password_validation import validate_password
from django.core.validators import validate_email
from django.core.exceptions import ValidationError as DjangoValidationError
from django.contrib.auth import get_user_model
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']
User = get_user_model()
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', 'type', 'rate_profile_data']
read_only_fields = ['id']
class PreditctionTemplateSerializer(serializers.ModelSerializer):
class Meta:
model = PreditctionTemplate
fields = ['id', 'name', 'is_default', 'description', 'prediction_mode', 'model', 'dataset', 'flight_parameters']
read_only_fields = ['id']
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name']
extra_kwargs = {
'username': {'read_only': True}
}
def validate_email(self, value):
try:
validate_email(value)
except DjangoValidationError:
raise serializers.ValidationError("Invalid email format")
return value
class ChangePasswordSerializer(serializers.Serializer):
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
def validate_new_password(self, value):
validate_password(value)
return value
class DeleteAccountSerializer(serializers.Serializer):
password = serializers.CharField(required=True)