From b6741cab5586338340e03c4cf211b311c0f2260a Mon Sep 17 00:00:00 2001 From: DevStrikerTech Date: Sun, 14 Apr 2024 11:51:28 +0100 Subject: [PATCH 1/6] serializing api models --- backend/api/serializer.py | 404 +++++++++++++++++++++++++++++++++++++- 1 file changed, 402 insertions(+), 2 deletions(-) diff --git a/backend/api/serializer.py b/backend/api/serializer.py index c6698ab..52f198f 100644 --- a/backend/api/serializer.py +++ b/backend/api/serializer.py @@ -1,8 +1,8 @@ -from django.contrib.auth.password_validation import validate_password - from rest_framework import serializers +from django.contrib.auth.password_validation import validate_password from rest_framework_simplejwt.serializers import TokenObtainPairSerializer +from api import models as api_models from userauths.models import Profile, User @@ -97,3 +97,403 @@ class ProfileSerializer(serializers.ModelSerializer): class Meta: model = Profile fields = "__all__" + + +class CategorySerializer(serializers.ModelSerializer): + """ + Serializes the Category model. + + Args: + serializers (type): The serializer class for the Category model. + """ + + class Meta: + fields = ["id", "title", "image", "slug", "course_count"] + model = api_models.Category + + +class TeacherSerializer(serializers.ModelSerializer): + """ + Serializes the Teacher model. + + Args: + serializers (type): The serializer class for the Teacher model. + """ + + class Meta: + fields = [ + "user", + "image", + "full_name", + "bio", + "about", + "country", + "youtube", + "github", + "twitter", + "linkedin", + "courses", + "review", + "students", + ] + model = api_models.Teacher + + +class VariantItemSerializer(serializers.ModelSerializer): + """ + Serializes the VariantItem model. + + Args: + serializers (type): The serializer class for the VariantItem model. + """ + + class Meta: + fields = "__all__" + model = api_models.VariantItem + + def __init__(self, *args, **kwargs): + super(VariantItemSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class VariantSerializer(serializers.ModelSerializer): + """ + Serializes the Variant model. + + Args: + serializers (type): The serializer class for the Variant model. + """ + + variant_items = VariantItemSerializer(many=True) + items = VariantItemSerializer(many=True) + + class Meta: + fields = "__all__" + model = api_models.Variant + + def __init__(self, *args, **kwargs): + super(VariantSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class QuestionAnswerMessageSerializer(serializers.ModelSerializer): + """ + Serializes the QuestionAnswerMessage model. + + Args: + serializers (type): The serializer class for the QuestionAnswerMessage model. + """ + + profile = ProfileSerializer(many=False) + + class Meta: + fields = "__all__" + model = api_models.QuestionAnswerMessage + + +class QuestionAnswerSerializer(serializers.ModelSerializer): + """ + Serializes the QuestionAnswer model. + + Args: + serializers (type): The serializer class for the QuestionAnswer model. + """ + + messages = QuestionAnswerMessageSerializer(many=True) + profile = ProfileSerializer(many=False) + + class Meta: + fields = "__all__" + model = api_models.QuestionAnswer + + +class CartSerializer(serializers.ModelSerializer): + """ + Serializes the Cart model. + + Args: + serializers (type): The serializer class for the Cart model. + """ + + class Meta: + fields = "__all__" + model = api_models.Cart + + def __init__(self, *args, **kwargs): + super(CartSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class CartOrderItemSerializer(serializers.ModelSerializer): + """ + Serializes the CartOrderItem model. + + Args: + serializers (type): The serializer class for the CartOrderItem model. + """ + + class Meta: + fields = "__all__" + model = api_models.CartOrderItem + + def __init__(self, *args, **kwargs): + super(CartOrderItemSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class CartOrderSerializer(serializers.ModelSerializer): + """ + Serializes the CartOrder model. + + Args: + serializers (type): The serializer class for the CartOrder model. + """ + + order_items = CartOrderItemSerializer(many=True) + + class Meta: + fields = "__all__" + model = api_models.CartOrder + + def __init__(self, *args, **kwargs): + super(CartOrderSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class CertificateSerializer(serializers.ModelSerializer): + """ + Serializes the Certificate model. + + Args: + serializers (type): The serializer class for the Certificate model. + """ + + class Meta: + fields = "__all__" + model = api_models.Certificate + + +class CompletedLessonSerializer(serializers.ModelSerializer): + """ + Serializes the CompletedLesson model. + + Args: + serializers (type): The serializer class for the CompletedLesson model. + """ + + class Meta: + fields = "__all__" + model = api_models.CompletedLesson + + def __init__(self, *args, **kwargs): + super(CompletedLessonSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class NoteSerializer(serializers.ModelSerializer): + """ + Serializes the Note model. + + Args: + serializers (type): The serializer class for the Note model. + """ + + class Meta: + fields = "__all__" + model = api_models.Note + + +class ReviewSerializer(serializers.ModelSerializer): + """ + Serializes the Review model. + + Args: + serializers (type): The serializer class for the Review model. + """ + + profile = ProfileSerializer(many=False) + + class Meta: + fields = "__all__" + model = api_models.Review + + def __init__(self, *args, **kwargs): + super(ReviewSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class NotificationSerializer(serializers.ModelSerializer): + """ + Serializes the Notification model. + + Args: + serializers (type): The serializer class for the Notification model. + """ + + class Meta: + fields = "__all__" + model = api_models.Notification + + +class CouponSerializer(serializers.ModelSerializer): + """ + Serializes the Coupon model. + + Args: + serializers (type): The serializer class for the Coupon model. + """ + + class Meta: + fields = "__all__" + model = api_models.Coupon + + +class WishlistSerializer(serializers.ModelSerializer): + """ + Serializes the Wishlist model. + + Args: + serializers (type): The serializer class for the Wishlist model. + """ + + class Meta: + fields = "__all__" + model = api_models.Wishlist + + def __init__(self, *args, **kwargs): + super(WishlistSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class CountrySerializer(serializers.ModelSerializer): + """ + Serializes the Country model. + + Args: + serializers (type): The serializer class for the Country model. + """ + + class Meta: + fields = "__all__" + model = api_models.Country + + +class EnrolledCourseSerializer(serializers.ModelSerializer): + """ + Serializes the EnrolledCourse model. + + Args: + serializers (type): The serializer class for the EnrolledCourse model. + """ + + lectures = VariantItemSerializer(many=True, read_only=True) + completed_lesson = CompletedLessonSerializer(many=True, read_only=True) + curriculum = VariantSerializer(many=True, read_only=True) + note = NoteSerializer(many=True, read_only=True) + question_answer = QuestionAnswerSerializer(many=True, read_only=True) + review = ReviewSerializer(many=False, read_only=True) + + class Meta: + fields = "__all__" + model = api_models.EnrolledCourse + + def __init__(self, *args, **kwargs): + super(EnrolledCourseSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 + + +class CourseSerializer(serializers.ModelSerializer): + """ + Serializes the Course model. + + Args: + serializers (type): The serializer class for the Course model. + """ + + students = EnrolledCourseSerializer( + many=True, + required=False, + read_only=True, + ) + curriculum = VariantSerializer( + many=True, + required=False, + read_only=True, + ) + lectures = VariantItemSerializer( + many=True, + required=False, + read_only=True, + ) + reviews = ReviewSerializer(many=True, read_only=True, required=False) + + class Meta: + fields = [ + "id", + "category", + "teacher", + "file", + "image", + "title", + "description", + "price", + "language", + "level", + "platform_status", + "teacher_course_status", + "featured", + "course_id", + "slug", + "date", + "students", + "curriculum", + "lectures", + "average_rating", + "rating_count", + "reviews", + ] + model = api_models.Course + + def __init__(self, *args, **kwargs): + super(CourseSerializer, self).__init__(*args, **kwargs) + request = self.context.get("request") + if request and request.method == "POST": + self.Meta.depth = 0 + else: + self.Meta.depth = 3 From a06d2ddf2fa4a089eb14147239ade1e25372dcbd Mon Sep 17 00:00:00 2001 From: clicmefast <80172192+clicmefast@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:31:30 +0500 Subject: [PATCH 2/6] Update CONTRIBUTING.md Saima Nutech --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f894a5a..3ac5b22 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,6 @@ The goal of this file is to help the larger community settle on some best practi - When making a pull request, reference the issue number it addresses. - Use clear and succinct commit messages. - Keep your pull request focused on one issue. -- Describe the goal of the changes. +- Describe the goal of the changes in the project. - Mention specific developers for review. - Make code readable and include code comments. From 610ded2a8ae099174836e98e3759305f00091336 Mon Sep 17 00:00:00 2001 From: Emah Khujaemah <58174184+emahkhujaemah@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:59:08 +0700 Subject: [PATCH 3/6] update add icon in technologies used --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 4db9136..1e96bb5 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,15 @@ The Learning Management System (LMS) is a web application that facilitates onlin ## Technologies Used +

+ django + react + redux + sass + postgresql +

+ + - **Django**: A high-level Python web framework for backend development. - **React.js**: A JavaScript library for building interactive user interfaces. - **React Router**: For client-side routing. From b36499c9037ec7fcdd45e1cacaca62bbd218f7d1 Mon Sep 17 00:00:00 2001 From: Emah Khujaemah <58174184+emahkhujaemah@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:00:31 +0700 Subject: [PATCH 4/6] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1e96bb5..fc02fcf 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ The Learning Management System (LMS) is a web application that facilitates onlin ## Technologies Used

- django - react - redux - sass - postgresql + django + react + redux + sass + postgresql

From e0da79cb4fd427dc5b797f65b8d39e49a4e84bc1 Mon Sep 17 00:00:00 2001 From: DevStrikerTech Date: Wed, 17 Apr 2024 20:08:16 +0100 Subject: [PATCH 5/6] added course list and detail api view --- backend/api/urls.py | 4 +++ backend/api/views.py | 62 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/backend/api/urls.py b/backend/api/urls.py index 872c994..10840c3 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -14,4 +14,8 @@ api_views.PasswordResetEmailVerifyAPIView.as_view(), ), path("user/password-change/", api_views.PasswordChangeAPIView.as_view()), + # Code Endpoints + path("course/category/", api_views.CategoryListAPIView.as_view()), + path("course/course-list/", api_views.CourseListAPIView.as_view()), + path("course/course-detail//", api_views.CourseDetailAPIView.as_view()), ] diff --git a/backend/api/views.py b/backend/api/views.py index 8a9463e..008c0e6 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -1,10 +1,7 @@ +from django.conf import settings from django.shortcuts import render from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string -from django.conf import settings - -from api import serializer as api_serializer -from userauths.models import User, Profile from rest_framework import generics, status from rest_framework.response import Response @@ -14,6 +11,10 @@ from random import randint +from api import models as api_models +from userauths.models import User, Profile +from api import serializer as api_serializer + class MyTokenObtainPairView(TokenObtainPairView): """ @@ -131,3 +132,56 @@ def create(self, request, *args, **kwargs): return Response( {"message": "User Does Not Exists"}, status=status.HTTP_404_NOT_FOUND ) + + +class CategoryListAPIView(generics.ListAPIView): + """ + API view for listing categories. + + Args: + generics (type): The base class for generic views. + """ + + queryset = api_models.Category.objects.filter(active=True) + serializer_class = api_serializer.CategorySerializer + permission_classes = [AllowAny] + + +class CourseListAPIView(generics.ListAPIView): + """ + API view for listing published courses. + + Args: + generics (type): The base class for generic views. + """ + + queryset = api_models.Course.objects.filter( + platform_status="Published", teacher_course_status="Published" + ) + serializer_class = api_serializer.CourseSerializer + permission_classes = [AllowAny] + + +class CourseDetailAPIView(generics.RetrieveAPIView): + """ + API view for retrieving details of a published course. + + Args: + generics (type): The base class for generic views. + + Returns: + type: The serialized course details. + """ + + serializer_class = api_serializer.CourseSerializer + permission_classes = [AllowAny] + queryset = api_models.Course.objects.filter( + platform_status="Published", teacher_course_status="Published" + ) + + def get_object(self): + slug = self.kwargs["slug"] + course = api_models.Course.objects.get( + slug=slug, platform_status="Published", teacher_course_status="Published" + ) + return course From 30ebf410eb9335fad3955e433a4cad237f1fe2fd Mon Sep 17 00:00:00 2001 From: DevStrikerTech Date: Thu, 18 Apr 2024 13:58:26 +0100 Subject: [PATCH 6/6] added all cart api views --- backend/api/urls.py | 7 ++ backend/api/views.py | 128 +++++++++++++++++++++ frontend/index.html | 2 + frontend/src/index.css | 4 + frontend/src/main.jsx | 1 + frontend/src/views/partials/BaseFooter.jsx | 6 +- frontend/src/views/partials/BaseHeader.jsx | 4 +- 7 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 frontend/src/index.css diff --git a/backend/api/urls.py b/backend/api/urls.py index 10840c3..bb81723 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -18,4 +18,11 @@ path("course/category/", api_views.CategoryListAPIView.as_view()), path("course/course-list/", api_views.CourseListAPIView.as_view()), path("course/course-detail//", api_views.CourseDetailAPIView.as_view()), + path("course/cart/", api_views.CartAPIView.as_view()), + path("course/cart-list//", api_views.CartListAPIView.as_view()), + path( + "course/cart-item-delete///", + api_views.CartItemDeleteAPIView.as_view(), + ), + path("cart/stats//", api_views.CartStatsAPIView.as_view()), ] diff --git a/backend/api/views.py b/backend/api/views.py index 008c0e6..9dfb5cc 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -10,6 +10,7 @@ from rest_framework_simplejwt.views import TokenObtainPairView from random import randint +from decimal import Decimal from api import models as api_models from userauths.models import User, Profile @@ -185,3 +186,130 @@ def get_object(self): slug=slug, platform_status="Published", teacher_course_status="Published" ) return course + + +class CartAPIView(generics.CreateAPIView): + """ + API view for managing shopping carts. + + Args: + generics (Type[generics.CreateAPIView]): The base class for creating a new cart. + + Returns: + Response: A response indicating the success or failure of cart creation/update. + """ + + queryset = api_models.Cart.objects.all() + serializer_class = api_serializer.CartSerializer + permission_classes = [AllowAny] + + def create(self, request, *args, **kwargs): + course_id = request.data["course_id"] + user_id = request.data["user_id"] + price = request.data["price"] + country_name = request.data["country_name"] + cart_id = request.data["cart_id"] + + course = api_models.Course.objects.filter(id=course_id).first() + user = User.objects.get(id=user_id) if user_id != "undefined" else None + + try: + country_object = api_models.Country.objects.get(name=country_name) + country = country_object.name + tax_rate = country_object.tax_rate / 100 + except api_models.Country.DoesNotExist: + country = "United Kingdom" + tax_rate = 0 + + cart = api_models.Cart.objects.filter(cart_id=cart_id, course=course).first() + + if cart: + cart.course = course + cart.user = user + cart.price = price + cart.tax_fee = Decimal(price) * Decimal(tax_rate) + cart.country = country + cart.cart_id = cart_id + cart.total = Decimal(cart.price) + Decimal(cart.tax_fee) + cart.save() + + return Response( + {"message": "Cart Updated Successfully"}, status=status.HTTP_200_OK + ) + + else: + cart = api_models.Cart() + + cart.course = course + cart.user = user + cart.price = price + cart.tax_fee = Decimal(price) * Decimal(tax_rate) + cart.country = country + cart.cart_id = cart_id + cart.total = Decimal(cart.price) + Decimal(cart.tax_fee) + cart.save() + + return Response( + {"message": "Cart Created Successfully"}, status=status.HTTP_201_CREATED + ) + + +class CartListAPIView(generics.ListAPIView): + serializer_class = api_serializer.CartSerializer + permission_classes = [AllowAny] + + def get_queryset(self): + cart_id = self.kwargs["cart_id"] + queryset = api_models.Cart.objects.filter(cart_id=cart_id) + + return queryset + + +class CartItemDeleteAPIView(generics.DestroyAPIView): + serializer_class = api_serializer.CartSerializer + permission_classes = [AllowAny] + + def get_object(self): + cart_id = self.kwargs["cart_id"] + item_id = self.kwargs["item_id"] + + return api_models.Cart.objects.filter(cart_id=cart_id, id=item_id).first() + + +class CartStatsAPIView(generics.RetrieveAPIView): + serializer_class = api_serializer.CartSerializer + permission_classes = [AllowAny] + lookup_field = "cart_id" + + def get_queryset(self): + cart_id = self.kwargs["cart_id"] + return api_models.Cart.objects.filter(cart_id=cart_id) + + def retrieve_cart_statistics(self, request, *args, **kwargs): + queryset = self.get_queryset() + + total_price = 0.00 + total_tax = 0.00 + total_total = 0.00 + + for cart_item in queryset: + total_price += float(self.calculate_price(cart_item)) + total_tax += float(self.calculate_tax(cart_item)) + total_total += round(float(self.calculate_total(cart_item)), 2) + + data = { + "price": total_price, + "tax": total_tax, + "total": total_total, + } + + return Response(data) + + def calculate_price(self, cart_item: api_models.Cart) -> float: + return cart_item.price + + def calculate_tax(self, cart_item: api_models.Cart) -> float: + return cart_item.tax_fee + + def calculate_total(self, cart_item: api_models.Cart) -> float: + return cart_item.total diff --git a/frontend/index.html b/frontend/index.html index 7aea37e..e30edcf 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -20,6 +20,8 @@ rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" /> + + LMS diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..432b87d --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,4 @@ +:root, +.navbar { + background-color: #dc3545 !important; +} diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index f77517b..dcab280 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -3,6 +3,7 @@ import ReactDOM from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import App from "./App.jsx"; +import "./index.css"; /** * Entry point for the React application. diff --git a/frontend/src/views/partials/BaseFooter.jsx b/frontend/src/views/partials/BaseFooter.jsx index 8d4e448..16d43b2 100644 --- a/frontend/src/views/partials/BaseFooter.jsx +++ b/frontend/src/views/partials/BaseFooter.jsx @@ -2,7 +2,7 @@ import React from "react"; function BaseFooter() { return ( -