First commit of claude's rework in django + vanillajs fronted
This commit is contained in:
301
apps/sessions/serializers.py
Normal file
301
apps/sessions/serializers.py
Normal file
@@ -0,0 +1,301 @@
|
||||
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',
|
||||
]
|
||||
Reference in New Issue
Block a user