diff --git a/server/.gitignore b/server/.gitignore index db3bdc54..f4828ac4 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1,2 +1,3 @@ # Ignore the workspace folder -workspace/ \ No newline at end of file +workspace/ +__pycache__ \ No newline at end of file diff --git a/server/farmapp/__pycache__/__init__.cpython-312.pyc b/server/farmapp/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index bd222ab8..00000000 Binary files a/server/farmapp/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/__init__.cpython-313.pyc b/server/farmapp/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index eeb642ba..00000000 Binary files a/server/farmapp/__pycache__/__init__.cpython-313.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/admin.cpython-312.pyc b/server/farmapp/__pycache__/admin.cpython-312.pyc deleted file mode 100644 index b06631a1..00000000 Binary files a/server/farmapp/__pycache__/admin.cpython-312.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/admin.cpython-313.pyc b/server/farmapp/__pycache__/admin.cpython-313.pyc deleted file mode 100644 index 5291f27c..00000000 Binary files a/server/farmapp/__pycache__/admin.cpython-313.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/apps.cpython-312.pyc b/server/farmapp/__pycache__/apps.cpython-312.pyc deleted file mode 100644 index 79937188..00000000 Binary files a/server/farmapp/__pycache__/apps.cpython-312.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/apps.cpython-313.pyc b/server/farmapp/__pycache__/apps.cpython-313.pyc deleted file mode 100644 index 900ab51b..00000000 Binary files a/server/farmapp/__pycache__/apps.cpython-313.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/models.cpython-312.pyc b/server/farmapp/__pycache__/models.cpython-312.pyc deleted file mode 100644 index 8b5a14cf..00000000 Binary files a/server/farmapp/__pycache__/models.cpython-312.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/models.cpython-313.pyc b/server/farmapp/__pycache__/models.cpython-313.pyc deleted file mode 100644 index 9db25d3e..00000000 Binary files a/server/farmapp/__pycache__/models.cpython-313.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/serializers.cpython-312.pyc b/server/farmapp/__pycache__/serializers.cpython-312.pyc deleted file mode 100644 index 4a4238f9..00000000 Binary files a/server/farmapp/__pycache__/serializers.cpython-312.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/serializers.cpython-313.pyc b/server/farmapp/__pycache__/serializers.cpython-313.pyc deleted file mode 100644 index 619e0b75..00000000 Binary files a/server/farmapp/__pycache__/serializers.cpython-313.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/urls.cpython-312.pyc b/server/farmapp/__pycache__/urls.cpython-312.pyc deleted file mode 100644 index 98d1bc51..00000000 Binary files a/server/farmapp/__pycache__/urls.cpython-312.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/urls.cpython-313.pyc b/server/farmapp/__pycache__/urls.cpython-313.pyc deleted file mode 100644 index 6599386c..00000000 Binary files a/server/farmapp/__pycache__/urls.cpython-313.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/views.cpython-312.pyc b/server/farmapp/__pycache__/views.cpython-312.pyc deleted file mode 100644 index 6e1df9a1..00000000 Binary files a/server/farmapp/__pycache__/views.cpython-312.pyc and /dev/null differ diff --git a/server/farmapp/__pycache__/views.cpython-313.pyc b/server/farmapp/__pycache__/views.cpython-313.pyc deleted file mode 100644 index cf5ddcaf..00000000 Binary files a/server/farmapp/__pycache__/views.cpython-313.pyc and /dev/null differ diff --git a/server/farmapp/models.py b/server/farmapp/models.py index bbb681c1..ebf372f9 100644 --- a/server/farmapp/models.py +++ b/server/farmapp/models.py @@ -136,15 +136,15 @@ class Meta: class Review(models.Model): reviewId = models.AutoField(primary_key=True) product = models.ForeignKey(Product, on_delete=models.CASCADE) - customer = models.ForeignKey(User, on_delete=models.CASCADE, limit_choices_to={'role': 'customer'}) + customerId = models.ForeignKey(User, on_delete=models.CASCADE, limit_choices_to={'role': 'customer'}) rating = models.IntegerField() comment = models.TextField(null=True, blank=True) createdAt = models.DateTimeField(auto_now_add=True) updatedAt = models.DateTimeField(auto_now=True) def __str__(self): - return self.reviewId - + return f"Review {self.reviewId}" + class Meta: db_table = 'Review' managed = False @@ -163,3 +163,4 @@ def __str__(self): class Meta: db_table = 'AdminActivityLog' managed = False + \ No newline at end of file diff --git a/server/farmapp/serializers.py b/server/farmapp/serializers.py index e8711c2a..f49dc180 100644 --- a/server/farmapp/serializers.py +++ b/server/farmapp/serializers.py @@ -1,5 +1,46 @@ from rest_framework import serializers from .models import Order, Product +from .models import Product, Farmer, Review, User + +class ProductSerializer(serializers.ModelSerializer): + productId = serializers.IntegerField() + productName = serializers.CharField() + quantity = serializers.IntegerField(source='stockQuantity') + price = serializers.DecimalField(source='unitPrice', max_digits=10, decimal_places=2) + description = serializers.CharField() + imageUrl = serializers.ImageField(source='productImage', required=False) + status = serializers.CharField() + dateAdded = serializers.DateTimeField(source='createdAt') + + class Meta: + model = Product + fields = ['productId', 'productName', 'quantity', 'price', 'description', 'imageUrl', 'status', 'dateAdded'] + + +class ProductCreateUpdateSerializer(serializers.ModelSerializer): + productName = serializers.CharField() + quantity = serializers.IntegerField(source='stockQuantity') + price = serializers.DecimalField(source='unitPrice', max_digits=10, decimal_places=2) + description = serializers.CharField() + imageUrl = serializers.ImageField(source='productImage', required=False) + status = serializers.CharField() + + class Meta: + model = Product + fields = ['productName', 'quantity', 'price', 'description', 'imageUrl', 'status'] + + +class ReviewSerializer(serializers.ModelSerializer): + class Meta: + model = Review + fields = ['reviewId', 'product', 'customerId', 'rating', 'comment', 'createdAt', 'updatedAt'] + read_only_fields = ['reviewId', 'createdAt', 'updatedAt'] + + def validate_rating(self, value): + if not (1 <= value <= 5): + raise serializers.ValidationError("Rating must be between 1 and 5.") + return value + class OrderDetailSerializer(serializers.ModelSerializer): """ @@ -80,4 +121,4 @@ def validate_status(self, value): class Meta: model = Order - fields = ['status'] \ No newline at end of file + fields = ['status'] diff --git a/server/farmapp/urls.py b/server/farmapp/urls.py index eae7572a..996aee1a 100644 --- a/server/farmapp/urls.py +++ b/server/farmapp/urls.py @@ -1,8 +1,20 @@ from django.urls import path -from .views import DeleteReviewView, OrderDetailView, UpdateOrderStatusView, DeleteOrderView +from . import views urlpatterns = [ - # URL for retrieving order details + # For listing products for a specific farmer + path('api/farmer//products/', views.FarmerProductsListView.as_view(), name='farmer-products-list'), + + # For creating a product for a specific farmer + path('api/farmer//products', views.ProductCreateView.as_view(), name='product-create'), + + # For updating a product for a specific farmer + path('api/farmer//products/', views.ProductUpdateView.as_view(), name='product-update'), + + #For making a product review + path('api/product//reviews', views.ProductReviewCreateView.as_view(), name='add-review'), + + # URL for retrieving order details path("api/farmer//orders/", OrderDetailView.as_view(), name="order-detail"), # URL for updating order status @@ -13,4 +25,7 @@ # URL for deleting a review path("api/product//reviews/", DeleteReviewView.as_view(), name="delete-review"), + ] + + diff --git a/server/farmapp/views.py b/server/farmapp/views.py index a7581b0a..a74ddafd 100644 --- a/server/farmapp/views.py +++ b/server/farmapp/views.py @@ -1,10 +1,87 @@ -from django.db import DatabaseError -from django.shortcuts import get_object_or_404 +from rest_framework import status from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework.exceptions import ValidationError +from rest_framework.generics import UpdateAPIView +from django.shortcuts import get_object_or_404 +from rest_framework.permissions import IsAuthenticated +from .serializers import ReviewSerializer, ProductSerializer, ProductCreateUpdateSerializer +from django.db import DatabaseError from rest_framework import status, views from .serializers import OrderDetailSerializer, OrderStatusUpdateSerializer -from .models import Farmer, Order, Product, Review, User -from rest_framework.views import APIView +from .models import Farmer, Order, Product, Review, User,Farmer + + +class ProductReviewCreateView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request, productId): + # Ensure the product exists + try: + product = Product.objects.get(productId=productId) + except Product.DoesNotExist: + return Response({"error": "Product not found"}, status=status.HTTP_404_NOT_FOUND) + + # Prepare review data + review_data = request.data + review_data['product'] = productId # Link the review to the product + review_data['customerId'] = request.user.userId # Set the customer to the current user + + # Validate and save the review + serializer = ReviewSerializer(data=review_data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +class FarmerProductsListView(APIView): + def get(self, request, farmerId): + # Fetch the Farmer instance by farmerId + farmer = get_object_or_404(Farmer, pk=farmerId) + # Retrieve all products for this farmer + products = Product.objects.filter(farmer=farmer) + serializer = ProductSerializer(products, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + + +class ProductCreateView(APIView): + def post(self, request, farmerId): + # Fetch the Farmer instance + farmer = get_object_or_404(Farmer, pk=farmerId) + # Add farmer to the incoming data + data = request.data + data['farmer'] = farmer.farmerId # Associate the product with the farmer + + # Serialize and validate the data + serializer = ProductCreateUpdateSerializer(data=data) + if serializer.is_valid(): + serializer.save(farmer=farmer) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class ProductUpdateView(UpdateAPIView): + serializer_class = ProductCreateUpdateSerializer + + # Override lookup_field to use 'productId' instead of 'pk' + lookup_field = 'productId' + + def get_queryset(self): + # Get the queryset by filtering products by farmerId and productId + farmerId = self.kwargs['farmerId'] + productId = self.kwargs['productId'] # Now we can use 'productId' instead of 'pk' + return Product.objects.filter(farmer_id=farmerId, productId=productId) + + def update(self, request, *args, **kwargs): + # Override update method to perform validation and save updated product + instance = self.get_object() + serializer = self.get_serializer(instance, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + class OrderDetailView(APIView): @@ -179,4 +256,4 @@ def delete(self, request, productId, reviewId): except DatabaseError: # Step 4: Handle potential database errors during deletion return Response({"error": "An error occurred while attempting to delete the review."}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR) \ No newline at end of file + status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/server/farmsales/__pycache__/__init__.cpython-312.pyc b/server/farmsales/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 7ea242ec..00000000 Binary files a/server/farmsales/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/server/farmsales/__pycache__/__init__.cpython-313.pyc b/server/farmsales/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 06b87e66..00000000 Binary files a/server/farmsales/__pycache__/__init__.cpython-313.pyc and /dev/null differ diff --git a/server/farmsales/__pycache__/settings.cpython-312.pyc b/server/farmsales/__pycache__/settings.cpython-312.pyc deleted file mode 100644 index c2594b22..00000000 Binary files a/server/farmsales/__pycache__/settings.cpython-312.pyc and /dev/null differ diff --git a/server/farmsales/__pycache__/settings.cpython-313.pyc b/server/farmsales/__pycache__/settings.cpython-313.pyc deleted file mode 100644 index 24184cd6..00000000 Binary files a/server/farmsales/__pycache__/settings.cpython-313.pyc and /dev/null differ diff --git a/server/farmsales/__pycache__/urls.cpython-312.pyc b/server/farmsales/__pycache__/urls.cpython-312.pyc deleted file mode 100644 index 3c7cb3c8..00000000 Binary files a/server/farmsales/__pycache__/urls.cpython-312.pyc and /dev/null differ diff --git a/server/farmsales/__pycache__/urls.cpython-313.pyc b/server/farmsales/__pycache__/urls.cpython-313.pyc deleted file mode 100644 index d228daec..00000000 Binary files a/server/farmsales/__pycache__/urls.cpython-313.pyc and /dev/null differ diff --git a/server/farmsales/__pycache__/wsgi.cpython-312.pyc b/server/farmsales/__pycache__/wsgi.cpython-312.pyc deleted file mode 100644 index 318642ba..00000000 Binary files a/server/farmsales/__pycache__/wsgi.cpython-312.pyc and /dev/null differ diff --git a/server/farmsales/__pycache__/wsgi.cpython-313.pyc b/server/farmsales/__pycache__/wsgi.cpython-313.pyc deleted file mode 100644 index a27b8b88..00000000 Binary files a/server/farmsales/__pycache__/wsgi.cpython-313.pyc and /dev/null differ diff --git a/server/farmsales/settings.py b/server/farmsales/settings.py index e8f98fbb..f66b995d 100644 --- a/server/farmsales/settings.py +++ b/server/farmsales/settings.py @@ -14,6 +14,7 @@ # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent +AUTH_USER_MODEL = 'farmapp.User' # Replace 'yourapp' with the actual name of your app # Quick-start development settings - unsuitable for production @@ -79,10 +80,10 @@ 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'CropCircle', - 'USER': 'backend_team', - 'PASSWORD': 'Akogo660221.', + # 'USER': 'root', + # 'PASSWORD': '2005', 'HOST': 'localhost', - 'PORT': '3306', + # 'PORT': '3308', } } diff --git a/server/farmsales/urls.py b/server/farmsales/urls.py index 0ce13d95..23ded9a7 100644 --- a/server/farmsales/urls.py +++ b/server/farmsales/urls.py @@ -20,4 +20,4 @@ urlpatterns = [ path('admin/', admin.site.urls), path('farmapp/', include('farmapp.urls')) -] +] \ No newline at end of file diff --git a/server/product_images/spinach.jpeg b/server/product_images/spinach.jpeg new file mode 100644 index 00000000..097ab4ad Binary files /dev/null and b/server/product_images/spinach.jpeg differ diff --git a/server/product_images/spinach_MVlPCwO.jpeg b/server/product_images/spinach_MVlPCwO.jpeg new file mode 100644 index 00000000..097ab4ad Binary files /dev/null and b/server/product_images/spinach_MVlPCwO.jpeg differ diff --git a/server/product_images/spinach_lmtbPMJ.jpeg b/server/product_images/spinach_lmtbPMJ.jpeg new file mode 100644 index 00000000..097ab4ad Binary files /dev/null and b/server/product_images/spinach_lmtbPMJ.jpeg differ