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
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)
|