302 lines
11 KiB
Python
302 lines
11 KiB
Python
from django.db.models import Q
|
|
from rest_framework import serializers
|
|
|
|
from apps.common.serializer_helpers import ammo_detail, batch_detail
|
|
from apps.gears.models import Ammo, GearStatus, ReloadedAmmoBatch, Rig
|
|
from apps.tools.models import ChronographAnalysis, ShotGroup
|
|
|
|
from .models import (
|
|
CorrectionUnit,
|
|
FreePracticeSession,
|
|
PRSSession,
|
|
PRSStage,
|
|
SpeedShootingSession,
|
|
)
|
|
|
|
|
|
# ── Shared helpers ────────────────────────────────────────────────────────────
|
|
|
|
def _rig_detail(rig):
|
|
if rig is None:
|
|
return None
|
|
return {'id': rig.id, 'name': rig.name}
|
|
|
|
|
|
# ── Abstract write mixin ──────────────────────────────────────────────────────
|
|
|
|
def _analysis_detail(analysis):
|
|
if analysis is None:
|
|
return None
|
|
return {'id': analysis.id, 'name': analysis.name, 'date': str(analysis.date) if analysis.date else None}
|
|
|
|
|
|
class AbstractSessionWriteMixin:
|
|
"""
|
|
Shared __init__ for all session write serializers:
|
|
narrows FK querysets to the current user.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
request = self.context.get('request')
|
|
if request and request.user.is_authenticated:
|
|
self.fields['rig'].queryset = Rig.objects.filter(user=request.user)
|
|
self.fields['reloaded_batch'].queryset = ReloadedAmmoBatch.objects.filter(
|
|
recipe__user=request.user
|
|
)
|
|
self.fields['analysis'].queryset = ChronographAnalysis.objects.filter(
|
|
Q(user=request.user) | Q(user__isnull=True)
|
|
)
|
|
|
|
def validate(self, attrs):
|
|
user = self.context['request'].user
|
|
instance = self.Meta.model(user=user, **attrs)
|
|
instance.clean()
|
|
return attrs
|
|
|
|
|
|
# ── PRS session ───────────────────────────────────────────────────────────────
|
|
|
|
class PRSStageSerializer(serializers.ModelSerializer):
|
|
shot_group = serializers.PrimaryKeyRelatedField(
|
|
queryset=ShotGroup.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
request = self.context.get('request')
|
|
if request and request.user.is_authenticated:
|
|
self.fields['shot_group'].queryset = ShotGroup.objects.filter(
|
|
analysis__user=request.user
|
|
)
|
|
|
|
class Meta:
|
|
model = PRSStage
|
|
fields = [
|
|
'id', 'order', 'position',
|
|
'distance_m', 'target_width_cm', 'target_height_cm',
|
|
'max_time_s', 'shots_count', 'notes_prep',
|
|
'computed_elevation', 'computed_windage', 'correction_unit',
|
|
'actual_elevation', 'actual_windage',
|
|
'hits', 'score', 'time_taken_s',
|
|
'shot_group', 'notes_post',
|
|
]
|
|
read_only_fields = ['computed_elevation', 'computed_windage', 'correction_unit']
|
|
|
|
|
|
class PRSSessionListSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = PRSSession
|
|
fields = [
|
|
'id', 'name', 'competition_name', 'category',
|
|
'date', 'location', 'rig', 'is_public', 'created_at',
|
|
]
|
|
|
|
|
|
class PRSSessionWriteSerializer(AbstractSessionWriteMixin, serializers.ModelSerializer):
|
|
rig = serializers.PrimaryKeyRelatedField(
|
|
queryset=Rig.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
ammo = serializers.PrimaryKeyRelatedField(
|
|
queryset=Ammo.objects.filter(status=GearStatus.VERIFIED),
|
|
required=False, allow_null=True,
|
|
)
|
|
reloaded_batch = serializers.PrimaryKeyRelatedField(
|
|
queryset=ReloadedAmmoBatch.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
analysis = serializers.PrimaryKeyRelatedField(
|
|
queryset=ChronographAnalysis.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
|
|
class Meta:
|
|
model = PRSSession
|
|
fields = [
|
|
'id', 'name', 'competition_name', 'category',
|
|
'date', 'location',
|
|
'rig', 'ammo', 'reloaded_batch', 'analysis',
|
|
'temperature_c', 'wind_speed_ms', 'wind_direction_deg',
|
|
'humidity_pct', 'pressure_hpa', 'weather_notes',
|
|
'notes', 'is_public',
|
|
]
|
|
|
|
|
|
class PRSSessionDetailSerializer(serializers.ModelSerializer):
|
|
rig_detail = serializers.SerializerMethodField()
|
|
ammo_detail = serializers.SerializerMethodField()
|
|
reloaded_batch_detail = serializers.SerializerMethodField()
|
|
analysis_detail = serializers.SerializerMethodField()
|
|
stages = PRSStageSerializer(many=True, read_only=True)
|
|
|
|
def get_rig_detail(self, obj):
|
|
return _rig_detail(obj.rig)
|
|
|
|
def get_ammo_detail(self, obj):
|
|
return ammo_detail(obj.ammo)
|
|
|
|
def get_reloaded_batch_detail(self, obj):
|
|
return batch_detail(obj.reloaded_batch)
|
|
|
|
def get_analysis_detail(self, obj):
|
|
return _analysis_detail(obj.analysis)
|
|
|
|
class Meta:
|
|
model = PRSSession
|
|
fields = [
|
|
'id', 'name', 'competition_name', 'category',
|
|
'date', 'location',
|
|
'rig', 'rig_detail',
|
|
'ammo', 'ammo_detail',
|
|
'reloaded_batch', 'reloaded_batch_detail',
|
|
'analysis', 'analysis_detail',
|
|
'temperature_c', 'wind_speed_ms', 'wind_direction_deg',
|
|
'humidity_pct', 'pressure_hpa', 'weather_notes',
|
|
'notes', 'is_public', 'stages',
|
|
'created_at', 'updated_at',
|
|
]
|
|
|
|
|
|
# ── Free Practice session ─────────────────────────────────────────────────────
|
|
|
|
class FreePracticeSessionListSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = FreePracticeSession
|
|
fields = ['id', 'name', 'date', 'location', 'distance_m', 'rounds_fired', 'rig', 'is_public', 'created_at']
|
|
|
|
|
|
class FreePracticeSessionWriteSerializer(AbstractSessionWriteMixin, serializers.ModelSerializer):
|
|
rig = serializers.PrimaryKeyRelatedField(
|
|
queryset=Rig.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
ammo = serializers.PrimaryKeyRelatedField(
|
|
queryset=Ammo.objects.filter(status=GearStatus.VERIFIED),
|
|
required=False, allow_null=True,
|
|
)
|
|
reloaded_batch = serializers.PrimaryKeyRelatedField(
|
|
queryset=ReloadedAmmoBatch.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
analysis = serializers.PrimaryKeyRelatedField(
|
|
queryset=ChronographAnalysis.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
|
|
class Meta:
|
|
model = FreePracticeSession
|
|
fields = [
|
|
'id', 'name', 'date', 'location',
|
|
'rig', 'ammo', 'reloaded_batch', 'analysis',
|
|
'distance_m', 'target_description', 'rounds_fired',
|
|
'temperature_c', 'wind_speed_ms', 'wind_direction_deg',
|
|
'humidity_pct', 'pressure_hpa', 'weather_notes',
|
|
'notes', 'is_public',
|
|
]
|
|
|
|
|
|
class FreePracticeSessionDetailSerializer(serializers.ModelSerializer):
|
|
rig_detail = serializers.SerializerMethodField()
|
|
ammo_detail = serializers.SerializerMethodField()
|
|
reloaded_batch_detail = serializers.SerializerMethodField()
|
|
analysis_detail = serializers.SerializerMethodField()
|
|
|
|
def get_rig_detail(self, obj):
|
|
return _rig_detail(obj.rig)
|
|
|
|
def get_ammo_detail(self, obj):
|
|
return ammo_detail(obj.ammo)
|
|
|
|
def get_reloaded_batch_detail(self, obj):
|
|
return batch_detail(obj.reloaded_batch)
|
|
|
|
def get_analysis_detail(self, obj):
|
|
return _analysis_detail(obj.analysis)
|
|
|
|
class Meta:
|
|
model = FreePracticeSession
|
|
fields = [
|
|
'id', 'name', 'date', 'location',
|
|
'rig', 'rig_detail',
|
|
'ammo', 'ammo_detail',
|
|
'reloaded_batch', 'reloaded_batch_detail',
|
|
'analysis', 'analysis_detail',
|
|
'distance_m', 'target_description', 'rounds_fired',
|
|
'temperature_c', 'wind_speed_ms', 'wind_direction_deg',
|
|
'humidity_pct', 'pressure_hpa', 'weather_notes',
|
|
'notes', 'is_public', 'created_at', 'updated_at',
|
|
]
|
|
|
|
|
|
# ── Speed Shooting session ────────────────────────────────────────────────────
|
|
|
|
class SpeedShootingSessionListSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = SpeedShootingSession
|
|
fields = ['id', 'name', 'format', 'date', 'location', 'rounds_fired', 'rig', 'is_public', 'created_at']
|
|
|
|
|
|
class SpeedShootingSessionWriteSerializer(AbstractSessionWriteMixin, serializers.ModelSerializer):
|
|
rig = serializers.PrimaryKeyRelatedField(
|
|
queryset=Rig.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
ammo = serializers.PrimaryKeyRelatedField(
|
|
queryset=Ammo.objects.filter(status=GearStatus.VERIFIED),
|
|
required=False, allow_null=True,
|
|
)
|
|
reloaded_batch = serializers.PrimaryKeyRelatedField(
|
|
queryset=ReloadedAmmoBatch.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
analysis = serializers.PrimaryKeyRelatedField(
|
|
queryset=ChronographAnalysis.objects.none(),
|
|
required=False, allow_null=True,
|
|
)
|
|
|
|
class Meta:
|
|
model = SpeedShootingSession
|
|
fields = [
|
|
'id', 'name', 'format', 'date', 'location',
|
|
'rig', 'ammo', 'reloaded_batch', 'analysis',
|
|
'rounds_fired',
|
|
'temperature_c', 'wind_speed_ms', 'wind_direction_deg',
|
|
'humidity_pct', 'pressure_hpa', 'weather_notes',
|
|
'notes', 'is_public',
|
|
]
|
|
|
|
|
|
class SpeedShootingSessionDetailSerializer(serializers.ModelSerializer):
|
|
rig_detail = serializers.SerializerMethodField()
|
|
ammo_detail = serializers.SerializerMethodField()
|
|
reloaded_batch_detail = serializers.SerializerMethodField()
|
|
analysis_detail = serializers.SerializerMethodField()
|
|
|
|
def get_rig_detail(self, obj):
|
|
return _rig_detail(obj.rig)
|
|
|
|
def get_ammo_detail(self, obj):
|
|
return ammo_detail(obj.ammo)
|
|
|
|
def get_reloaded_batch_detail(self, obj):
|
|
return batch_detail(obj.reloaded_batch)
|
|
|
|
def get_analysis_detail(self, obj):
|
|
return _analysis_detail(obj.analysis)
|
|
|
|
class Meta:
|
|
model = SpeedShootingSession
|
|
fields = [
|
|
'id', 'name', 'format', 'date', 'location',
|
|
'rig', 'rig_detail',
|
|
'ammo', 'ammo_detail',
|
|
'reloaded_batch', 'reloaded_batch_detail',
|
|
'analysis', 'analysis_detail',
|
|
'rounds_fired',
|
|
'temperature_c', 'wind_speed_ms', 'wind_direction_deg',
|
|
'humidity_pct', 'pressure_hpa', 'weather_notes',
|
|
'notes', 'is_public', 'created_at', 'updated_at',
|
|
]
|