Skip to content

Commit

Permalink
Hook up vehicle filters to the backend (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
ltan02 authored Apr 11, 2024
1 parent 613e0d7 commit 0694cec
Show file tree
Hide file tree
Showing 23 changed files with 736 additions and 439 deletions.
4 changes: 2 additions & 2 deletions backend/auction/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from .views import (
AuctionDayApiView,
AuctionDetailApiView,
AuctionItemsApiView,
AuctionListApiView,
AuctionVehiclesApiView,
BidderVerificationApiView,
CurrentAuctionApiView,
GetSavedUnitApiView,
Expand All @@ -29,7 +29,7 @@
),
path(
"/<uuid:auction_id>/days/<uuid:auction_day_id>/vehicles",
AuctionVehiclesApiView.as_view(),
AuctionItemsApiView.as_view(),
name="auction_vehicles",
),
path(
Expand Down
99 changes: 65 additions & 34 deletions backend/auction/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from datetime import timedelta

from django.contrib.contenttypes.models import ContentType
from django.core.paginator import Paginator
from django.db.models import Prefetch, Q
from django.utils import timezone
from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.pagination import PageNumberPagination
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
Expand Down Expand Up @@ -245,8 +247,6 @@ def post(self, request, **kwargs):
vehicle_id = kwargs.get("vehicle_id")
auction_id = kwargs.get("auction_id")

# Replace this later to fetch authentication details
# from headers instead of body
vehicle = get_object_or_404(Vehicle, id=vehicle_id)
auction_for_vehicle = Auction.objects.get(id=auction_id)
bidder = self.cognitoService.get_user_details(bidder_id)
Expand Down Expand Up @@ -316,48 +316,79 @@ def get(self, request, **kwargs):
return Response({"vehicles": vehicle_data}, status=status.HTTP_200_OK)


class AuctionVehiclesApiView(APIView):
"""
An endpoint to retrieve an auction's associated vehicles
"""
class AuctionItemsApiView(APIView):
permission_classes = [IsAuthenticated]

cognitoService = AWSCognitoService()
def get_filtered_queryset(self, model, auction_day_id, filters):
"""
Filters the queryset of a given model based on the provided filters
and the relation to AuctionItem through ContentType.
"""
content_type = ContentType.objects.get_for_model(model)
related_item_ids = AuctionItem.objects.filter(
auction_day_id=auction_day_id, content_type=content_type
).values_list("object_id", flat=True)

def get_permissions(self):
if self.request.method == "GET":
self.permission_classes = [IsAuthenticated]
else:
self.permission_classes = [IsAdminUser]
return super().get_permissions()
return model.objects.filter(id__in=related_item_ids, **filters)

def get(self, request, auction_id, auction_day_id):
auction_day = get_object_or_404(AuctionDay, id=auction_day_id)

auction_items = AuctionItem.objects.filter(auction_day=auction_day)
item_type = request.query_params.get("item_type", None)
type = request.query_params.get("type", None)
brand_id = request.query_params.get("brand", None)
min_price = request.query_params.get("min_price", None)
max_price = request.query_params.get("max_price", None)
search_term = request.query_params.get("search", None)

filters = {}
if type:
filters["type"] = type
if brand_id:
filters["brand"] = brand_id
if min_price:
filters["current_price__gte"] = min_price
if max_price:
filters["current_price__lte"] = max_price
if search_term:
filters["description__icontains"] = search_term

# Apply filters to each model's queryset
if item_type == "trucks":
combined_qs = self.get_filtered_queryset(Vehicle, auction_day_id, filters)
elif item_type == "equipment":
combined_qs = self.get_filtered_queryset(Equipment, auction_day_id, filters)
elif item_type == "trailers":
combined_qs = self.get_filtered_queryset(Trailer, auction_day_id, filters)
else:
vehicle_qs = self.get_filtered_queryset(Vehicle, auction_day_id, filters)
equipment_qs = self.get_filtered_queryset(
Equipment, auction_day_id, filters
)
trailer_qs = self.get_filtered_queryset(Trailer, auction_day_id, filters)

vehicle_list = []
equipment_list = []
trailer_list = []
combined_qs = list(vehicle_qs) + list(equipment_qs) + list(trailer_qs)

for auction_item in auction_items:
if isinstance(auction_item.content_object, Vehicle):
vehicle_list.append(auction_item.content_object)
elif isinstance(auction_item.content_object, Equipment):
equipment_list.append(auction_item.content_object)
elif isinstance(auction_item.content_object, Trailer):
trailer_list.append(auction_item.content_object)
# Implement custom pagination
page_number = request.query_params.get("page", 1)
paginator = Paginator(combined_qs, 5)
page_obj = paginator.get_page(page_number)

vehicle_data = [{"id": vehicle.id} for vehicle in vehicle_list]
equipment_data = [{"id": equipment.id} for equipment in equipment_list]
trailer_data = [{"id": trailer.id} for trailer in trailer_list]
serialized_data = []
for obj in page_obj:
if isinstance(obj, Vehicle):
serializer = VehicleSerializer(obj)
elif isinstance(obj, Equipment):
serializer = EquipmentSerializer(obj)
elif isinstance(obj, Trailer):
serializer = TrailerSerializer(obj)
serialized_data.append(serializer.data)

return Response(
{
"vehicles": vehicle_data,
"equipment": equipment_data,
"trailers": trailer_data,
},
status=status.HTTP_200_OK,
"count": paginator.count,
"next": page_obj.has_next(),
"previous": page_obj.has_previous(),
"results": serialized_data,
}
)

def post(self, request, auction_id, auction_day_id):
Expand Down
5 changes: 3 additions & 2 deletions backend/bid/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ class BidAdmin(admin.ModelAdmin):
"amount",
"bidder",
"auction",
"auction_day",
"content_type",
"object_id",
"created_at",
)
search_fields = ("bidder__username", "auction__name", "amount")
list_filter = ("auction", "bidder", "content_type")
search_fields = ("bidder", "amount")
list_filter = ("auction", "auction_day", "content_type")

def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "content_type":
Expand Down
24 changes: 24 additions & 0 deletions backend/bid/migrations/0003_bid_auction_day.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.0.4 on 2024-04-10 19:30

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("auction", "0003_alter_auctionverifieduser_cognito_user_id"),
("bid", "0002_alter_bid_bidder"),
]

operations = [
migrations.AddField(
model_name="bid",
name="auction_day",
field=models.ForeignKey(
default=0,
on_delete=django.db.models.deletion.PROTECT,
to="auction.auctionday",
),
preserve_default=False,
),
]
3 changes: 2 additions & 1 deletion backend/bid/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.contrib.contenttypes.models import ContentType
from django.db import models

from auction.models import Auction
from auction.models import Auction, AuctionDay
from core.models import MainModel


Expand All @@ -14,6 +14,7 @@ class Bid(MainModel):
amount = models.IntegerField(null=False)
bidder = models.CharField(null=False, max_length=500)
auction = models.ForeignKey(Auction, on_delete=models.PROTECT)
auction_day = models.ForeignKey(AuctionDay, on_delete=models.PROTECT)
content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT)
object_id = models.UUIDField()
content_object = GenericForeignKey("content_type", "object_id")
Expand Down
55 changes: 36 additions & 19 deletions backend/bid/signals.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# signals.py
import json

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver

Expand All @@ -13,24 +12,42 @@
channel_layer = get_channel_layer()


def update_current_price(bid_instance):
content_type = bid_instance.content_type
object_id = bid_instance.object_id
bid_amount = bid_instance.amount

model_class = content_type.model_class()

try:
if hasattr(model_class, "current_price"):
item = model_class.objects.get(pk=object_id)
item.current_price = bid_amount
item.save()
except ObjectDoesNotExist:
print(f"Item of type {content_type.model} with ID {object_id} not found.")


@receiver(post_save, sender=Bid)
def bid_updated(sender, instance, **kwargs):
current_date = instance.auction.start_date.date()
auction_day = AuctionDay.objects.filter(
auction=instance.auction, date=current_date
).first()

bid_data = {
"id": str(instance.id),
"amount": instance.amount,
"auction_id": str(instance.auction.id),
"auction_day_id": str(auction_day.id) if auction_day else None,
"vehicle_id": str(instance.object_id),
"bidder": str(instance.bidder),
}
async_to_sync(channel_layer.group_send)(
"bid_updates", {"type": "bid.update", "bid_data": bid_data}
)
def bid_updated(sender, instance, created, **kwargs):
if created:
bid_data = {
"id": str(instance.id),
"amount": instance.amount,
"auction_id": str(instance.auction.id),
"auction_day_id": str(instance.auction_day.id)
if instance.auction_day
else None,
"object_id": str(instance.object_id),
"bidder_id": str(instance.bidder),
}

async_to_sync(channel_layer.group_send)(
"auction_{}".format(instance.auction_id),
{"type": "bid.update", "bid_data": bid_data},
)

update_current_price(instance)


@receiver(post_delete, sender=Bid)
Expand Down
Loading

0 comments on commit 0694cec

Please sign in to comment.