Files
ShooterHub/config/views.py
2026-04-02 11:24:30 +02:00

99 lines
3.8 KiB
Python

"""
Public feed endpoint — returns the latest publicly shared items from each
content type. No authentication required.
"""
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from apps.photos.models import GroupPhoto
from apps.sessions.models import FreePracticeSession, PRSSession, SpeedShootingSession
from apps.tools.models import ChronographAnalysis
from apps.gears.models import ReloadRecipe
def _session_row(s, kind):
label = getattr(s, 'competition_name', None) or s.name or '(unnamed)'
return {
'id': s.id,
'type': kind,
'label': label,
'date': str(s.date) if s.date else None,
'location': s.location or None,
}
@api_view(['GET'])
@permission_classes([AllowAny])
def public_feed(request):
limit = min(int(request.query_params.get('limit', 8)), 20)
# ── Sessions (merge all types) ──────────────────────────────────────────
prs = list(PRSSession.objects.filter(is_public=True).order_by('-date')[:limit])
fp = list(FreePracticeSession.objects.filter(is_public=True).order_by('-date')[:limit])
ss = list(SpeedShootingSession.objects.filter(is_public=True).order_by('-date')[:limit])
sessions_raw = (
[_session_row(s, 'PRS') for s in prs] +
[_session_row(s, 'Practice') for s in fp] +
[_session_row(s, 'Speed') for s in ss]
)
sessions = sorted(sessions_raw, key=lambda x: x['date'] or '', reverse=True)[:limit]
# ── Analyses ────────────────────────────────────────────────────────────
analyses_qs = (
ChronographAnalysis.objects
.filter(is_public=True)
.order_by('-date')[:limit]
)
analyses = [
{
'id': a.id,
'name': a.name,
'date': str(a.date) if a.date else None,
}
for a in analyses_qs
]
# ── Photos ──────────────────────────────────────────────────────────────
photos_qs = (
GroupPhoto.objects
.filter(is_public=True)
.select_related('photo', 'analysis')
.order_by('-photo__uploaded_at')[:limit]
)
photos = []
for gp in photos_qs:
an = getattr(gp, 'analysis', None)
photos.append({
'id': gp.id,
'photo_id': gp.photo_id,
'caption': gp.caption or None,
'group_size_mm': str(an.group_size_mm) if an and an.group_size_mm is not None else None,
'group_size_moa': str(an.group_size_moa) if an and an.group_size_moa is not None else None,
})
# ── Reload recipes ──────────────────────────────────────────────────────
recipes_qs = (
ReloadRecipe.objects
.filter(is_public=True)
.select_related('caliber', 'bullet', 'primer', 'brass')
.order_by('-created_at')[:limit]
)
recipes = []
for r in recipes_qs:
recipes.append({
'id': r.id,
'name': r.name,
'caliber': r.caliber.name if r.caliber_id else None,
'bullet': str(r.bullet) if r.bullet_id else None,
'date': str(r.created_at.date()),
})
return Response({
'sessions': sessions,
'analyses': analyses,
'photos': photos,
'recipes': recipes,
})