barebone app with django and react, sse, jwt token, comport reader, test comport writer, requires com0com, users with groups, sample table vehicles, tokens for access and refresh
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user