You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
7.9 KiB
Python

from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from .models import ComPortReading, User
from .serializers import ComPortReadingSerializer, UserSerializer, UserDetailSerializer, ChangePasswordSerializer, VehicleSerializer, NomenclatureSerializer
from vehicles.models import Vehicle
from nomenclatures.models import Nomenclature
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint for user management.
list: Get all users
create: Create a new user
retrieve: Get a specific user
update: Update a user
destroy: Delete a user
me: Get current authenticated user
change_password: Change password for current user
"""
queryset = User.objects.all()
serializer_class = UserSerializer
filterset_fields = ['role', 'is_admin', 'is_active']
ordering = ['username']
@action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])
def me(self, request):
"""Get current authenticated user details"""
serializer = UserDetailSerializer(request.user)
return Response(serializer.data)
@action(detail=False, methods=['post'], permission_classes=[IsAuthenticated],
url_path='change-password', url_name='change_password')
def change_password(self, request):
"""Change password for current user"""
serializer = ChangePasswordSerializer(
data=request.data,
context={'request': request}
)
if serializer.is_valid():
serializer.save()
return Response({
'detail': 'Password updated successfully'
}, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ComPortReadingViewSet(viewsets.ModelViewSet):
"""
API endpoint for COM port readings.
list: Get all readings with optional filtering
create: Create a new reading (allows unauthenticated for serial bridge)
retrieve: Get a specific reading
destroy: Delete a reading
latest: Get the latest reading
by_port: Get readings for a specific port
"""
queryset = ComPortReading.objects.all()
serializer_class = ComPortReadingSerializer
filterset_fields = ['port']
ordering = ['-timestamp']
def get_permissions(self):
"""Allow unauthenticated POST for serial bridge"""
if self.action == 'create':
return [AllowAny()]
return [IsAuthenticated()]
@action(detail=False, methods=['get'])
def latest(self, request):
"""Get the latest reading"""
reading = ComPortReading.objects.first()
if reading:
serializer = self.get_serializer(reading)
return Response(serializer.data)
return Response({'detail': 'No readings yet'}, status=status.HTTP_404_NOT_FOUND)
@action(detail=False, methods=['get'])
def by_port(self, request):
"""Get readings for a specific port"""
port = request.query_params.get('port', None)
if not port:
return Response(
{'detail': 'port parameter is required'},
status=status.HTTP_400_BAD_REQUEST
)
readings = ComPortReading.objects.filter(port=port)
serializer = self.get_serializer(readings, many=True)
return Response(serializer.data)
def get_client_ip(self, request):
"""Get client IP address"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def perform_create(self, serializer):
"""Save the reading with client IP"""
serializer.save(source_ip=self.get_client_ip(self.request))
class VehicleViewSet(viewsets.ModelViewSet):
"""
API endpoint for vehicle management.
list: Get all vehicles with pagination
create: Create a new vehicle with optional extra data
retrieve: Get a specific vehicle by ID
update: Full update of vehicle and extra data
partial_update: Partial update of vehicle
destroy: Delete a vehicle (cascades to VehicleExtra)
by_number: Get vehicle by vehicle_number
"""
queryset = Vehicle.objects.select_related('extra').all()
serializer_class = VehicleSerializer
permission_classes = [IsAuthenticated]
filterset_fields = ['vehicle_number']
search_fields = ['vehicle_number']
ordering = ['vehicle_number']
@action(detail=False, methods=['get'], url_path='by-number')
def by_number(self, request):
"""Get vehicle by vehicle_number query parameter"""
vehicle_number = request.query_params.get('vehicle_number', None)
if not vehicle_number:
return Response(
{'detail': 'vehicle_number parameter is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
vehicle = Vehicle.objects.get(vehicle_number=vehicle_number)
serializer = self.get_serializer(vehicle)
return Response(serializer.data)
except Vehicle.DoesNotExist:
return Response(
{'detail': 'Vehicle not found'},
status=status.HTTP_404_NOT_FOUND
)
class NomenclatureViewSet(viewsets.ModelViewSet):
"""
API endpoint for nomenclature management.
list: Get all nomenclatures with pagination
create: Create a new nomenclature with fields
retrieve: Get a specific nomenclature by ID
update: Full update of nomenclature and fields
partial_update: Partial update of nomenclature
destroy: Delete a nomenclature (cascades to fields)
by_code: Get nomenclature by code
by_applies_to: Filter nomenclatures by applies_to type
"""
queryset = Nomenclature.objects.prefetch_related('nomenclaturefield_set').all()
serializer_class = NomenclatureSerializer
permission_classes = [IsAuthenticated]
filterset_fields = ['applies_to', 'code']
search_fields = ['code', 'name']
ordering = ['code']
@action(detail=False, methods=['get'], url_path='by-code')
def by_code(self, request):
"""Get nomenclature by code query parameter"""
code = request.query_params.get('code', None)
if not code:
return Response(
{'detail': 'code parameter is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
nomenclature = Nomenclature.objects.get(code=code)
serializer = self.get_serializer(nomenclature)
return Response(serializer.data)
except Nomenclature.DoesNotExist:
return Response(
{'detail': 'Nomenclature not found'},
status=status.HTTP_404_NOT_FOUND
)
@action(detail=False, methods=['get'], url_path='by-applies-to')
def by_applies_to(self, request):
"""Get nomenclatures filtered by applies_to query parameter"""
applies_to = request.query_params.get('applies_to', None)
if not applies_to:
return Response(
{'detail': 'applies_to parameter is required'},
status=status.HTTP_400_BAD_REQUEST
)
if applies_to not in ['vehicle', 'container']:
return Response(
{'detail': 'applies_to must be either vehicle or container'},
status=status.HTTP_400_BAD_REQUEST
)
nomenclatures = Nomenclature.objects.filter(applies_to=applies_to)
page = self.paginate_queryset(nomenclatures)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(nomenclatures, many=True)
return Response(serializer.data)