99 lines
3.8 KiB
Python
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,
|
|
})
|