Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added projektBazy/api/__pycache__/__init__.cpython-312.pyc
Binary file not shown.
Binary file not shown.
Binary file added projektBazy/api/__pycache__/urls.cpython-312.pyc
Binary file not shown.
Binary file added projektBazy/api/__pycache__/views.cpython-312.pyc
Binary file not shown.
42 changes: 41 additions & 1 deletion projektBazy/api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from rest_framework import serializers
from shop.models import Category, Listing, Address, Transaction
from shop.models import Category, Listing, Address, Transaction, Message, Chat, Review
from django.contrib.auth.models import User
class CategorySerializer(serializers.ModelSerializer):
class Meta:
Expand Down Expand Up @@ -54,3 +54,43 @@ def create(self, validated_data):
)
return transaction

class ChatSerializer(serializers.ModelSerializer):
class Meta:
model = Chat
fields = ['buyer', 'seller', 'listing']
read_only_fields = ['buyer', 'seller', 'listing']

def create(self, validated_data):
buyer = self.context['request'].user
seller = validated_data['seller']
listing = validated_data['listing']

chat = Chat.objects.create(
buyer=buyer,
seller=seller,
listing=listing
)
return chat


class MessageSerializer(serializers.ModelSerializer):
class Meta:
model = Message
fields = ['chat', 'sender', 'content', 'status', 'created_at', 'viewed_at']
read_only_fields = ['chat', 'sender', 'status', 'created_at', 'viewed_at']

def create(self, validated_data):
chat = validated_data['chat']
content = validated_data['content']
sender = self.context['request'].user
message = Message.objects.create(
chat=chat,
content=content,
sender=sender
)
return message

class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ['id', 'listing', 'reviewer', 'reviewee', 'rating', 'comment', 'created_at']
15 changes: 13 additions & 2 deletions projektBazy/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.urls import path
from .views import ListingsView, UserAddressesView
from .views import ListingsView, UserAddressesView, ReviewViewSet
from . import views
urlpatterns = [
path('',views.getData),
Expand All @@ -18,11 +18,22 @@
path('listings/user/<int:user_id>/', ListingsView.as_view(), name='user-listings'),
#/listings/category/2/
path('listings/category/<int:category_id>/', ListingsView.as_view(), name='category-listings'),
path('listings/<int:listing_id>/', views.get_listing, name='listing'),
#/user/1/addresses
path('user/<int:user_id>/addresses/', UserAddressesView.as_view(), name='user-addresses'),
path('transactions/', views.create_transaction, name='create-transaction'),
path('transactions/<int:transaction_id>/update/', views.update_transaction_status, name='update-transaction-status'),

#/addmessage/1/1
path('addmessage/<int:listing_id>/<int:chat_id>', views.add_chat_message, name='add-chat-message'),
path('addmessage/<int:listing_id>', views.add_chat_message, name='add-chat-message'),
path('chats/<int:listing_id>', views.get_chats, name='listing-chats'),
path('messages/<int:chat_id>', views.get_messages, name='chat-messages'),
path('viewedmessage/<int:message_id>', views.set_message_viewed, name='message-viewed'),
#nwm
path('reviews/', ReviewViewSet.as_view({'get': 'list', 'post': 'create'}), name='review-list-create'),
path('reviews/<int:pk>/', ReviewViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}), name='review-detail'),
path('reviews/<int:pk>/user-reviews/', ReviewViewSet.as_view({'get': 'user_reviews'}), name='user-reviews'),
path('reviews/<int:pk>/average-rating/', ReviewViewSet.as_view({'get': 'average_rating'}), name='average-rating'),
]
#Do something like this to test it
#http://127.0.0.1:8000/postAddress/
Expand Down
140 changes: 137 additions & 3 deletions projektBazy/api/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from rest_framework.response import Response
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from shop.models import Category, Listing, Address, Transaction
from shop.models import Category, Listing, Address, Transaction, Message, Chat, Review
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from .serializers import CategorySerializer, ListingSerializer, UserSerializer, AdressSerializer, TransactionSerializer
from .serializers import CategorySerializer, ListingSerializer, UserSerializer, AdressSerializer, TransactionSerializer, MessageSerializer, ChatSerializer, ReviewSerializer
from rest_framework import status, generics
from rest_framework.authtoken.models import Token
from rest_framework.exceptions import NotFound
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404

from rest_framework import viewsets
from rest_framework.decorators import action
from django.db.models import Avg
from datetime import datetime
#unnessesary
@api_view(['GET'])
def getCategory(request):
Expand Down Expand Up @@ -208,7 +211,138 @@ def update_transaction_status(request, transaction_id):
return Response(TransactionSerializer(transaction).data, status=status.HTTP_200_OK)


@api_view(['POST'])
@authentication_classes([SessionAuthentication, TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_chat_message(request, chat_id, listing_id):
if request.method == 'POST':
try:
listing = Listing.objects.get(id=listing_id)
except Listing.DoesNotExist:
return Response({'detail': 'Listing not found'}, status=status.HTTP_404_NOT_FOUND)

try:
chat = Chat.objects.get(id=chat_id)
if chat.listing_id != listing.id:
return Response({'detail': 'Invalid chat for the listing'}, status=status.HTTP_400_BAD_REQUEST)

except Chat.DoesNotExist:
chat = None

if chat is None:

if request.user.id == listing.seller.id:
return Response({'detail': 'Seller can not start the chat'}, status=status.HTTP_400_BAD_REQUEST)

try:
newChat = {
'buyer': request.user,
'seller': listing.seller,
'listing': listing
}
chat_serializer = ChatSerializer(data=newChat, context={'request': request})
if chat_serializer.is_valid():
chat = chat_serializer.save(seller=listing.seller, buyer=request.user, listing=listing)

except Exception as e:
return Response({'detail': 'Unable to create chat'}, status=status.HTTP_409_CONFLICT)

new_message = {
'content' : request.data.get('message')
}

message_serializer = MessageSerializer(data=new_message, context={'request': request})

if message_serializer.is_valid():
message_serializer.save(chat=chat)
return Response(message_serializer.data, status=status.HTTP_201_CREATED)

return Response(message_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET'])
def get_chats(request, listing_id):
try:
listing = Listing.objects.get(id=listing_id)
except Listing.DoesNotExist:
return Response({'detail': 'Listing not found'}, status=status.HTTP_404_NOT_FOUND)

chats = Chat.objects.filter(listing=listing_id)
serializer = ChatSerializer(chats, many=True)
return Response(serializer.data)

@api_view(['GET'])
def get_messages(request, chat_id):
try:
chat = Chat.objects.get(id=chat_id)
except Chat.DoesNotExist:
return Response({'detail': 'Chat not found'}, status=status.HTTP_404_NOT_FOUND)

messages = Message.objects.filter(chat=chat)
serializer = MessageSerializer(messages, many=True)
return Response(serializer.data)

@api_view(['PATCH'])
def set_message_viewed(request, message_id):
try:
message = Message.objects.get(id=message_id)
message.status = Message.Viewed
message.viewed_at = datetime.now()
message.save()
except Message.DoesNotExist:
return Response({'detail': 'Message not found'}, status=status.HTTP_404_NOT_FOUND)
except Exception as e:
return Response({'detail': 'Unable to update'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

return Response(MessageSerializer(message).data)

class ReviewViewSet(viewsets.ModelViewSet):
queryset = Review.objects.all()
serializer_class = ReviewSerializer

def create(self, request, *args, **kwargs):
reviewer = request.user
listing_id = request.data.get('listing')
reviewee_id = request.data.get('reviewee')

try:
listing = Listing.objects.get(id=listing_id)
if listing.seller == reviewer:
return Response({"error": "You can only review users for listings you purchased."}, status=status.HTTP_403_FORBIDDEN)
if listing.seller.id != int(reviewee_id):
return Response({"error": "Reviewee must be the seller of the purchased listing."}, status=status.HTTP_400_BAD_REQUEST)
except Listing.DoesNotExist:
return Response({"error": "Listing does not exist."}, status=status.HTTP_404_NOT_FOUND)

return super().create(request, *args, **kwargs)

def update(self, request, *args, **kwargs):
instance = self.get_object()
# if instance.reviewer != request.user:
# return Response({"error": "You can only update your own reviews."}, status=status.HTTP_403_FORBIDDEN)
return super().update(request, *args, **kwargs)

@action(detail=True, methods=['get'], url_path='user-reviews')
def user_reviews(self, request, pk=None):
user = User.objects.get(pk=pk)
reviews = Review.objects.filter(reviewee=user)
serializer = self.get_serializer(reviews, many=True)
return Response(serializer.data)

@action(detail=True, methods=['get'], url_path='average-rating')
def average_rating(self, request, pk=None):
user = User.objects.get(pk=pk)
average_rating = Review.objects.filter(reviewee=user).aggregate(Avg('rating'))['rating__avg']
return Response({"average_rating": average_rating})




@api_view(['GET'])
def get_listing(request, listing_id):
try:
listing = Listing.objects.get(id=listing_id)
serializer = ListingSerializer(listing)
return Response(serializer.data)
except Listing.DoesNotExist:
return Response({'detail': 'Listing not found'}, status=status.HTTP_404_NOT_FOUND)

Binary file added projektBazy/db.sqlite3
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
10 changes: 2 additions & 8 deletions projektBazy/projektBazy/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,10 @@

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Używamy MySQL jako backend (MariaDB jest kompatybilne)
'NAME': 'shopDB', # Nazwa Twojej bazy danych
'USER': '272561', # Użytkownik bazy danych
'PASSWORD': 'osboxes.org', # Hasło do bazy danych
'HOST': 'localhost', # Adres serwera bazy danych
'PORT': '3306', # Domyślny port dla MariaDB/MySQL
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}


# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators

Expand Down
Binary file not shown.
Binary file added projektBazy/shop/__pycache__/admin.cpython-312.pyc
Binary file not shown.
Binary file added projektBazy/shop/__pycache__/apps.cpython-312.pyc
Binary file not shown.
Binary file not shown.
6 changes: 4 additions & 2 deletions projektBazy/shop/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from django.contrib import admin
from .models import User, Listing, Address, Category, Transaction
from .models import User, Listing, Address, Category, Transaction, Chat, Message

# Register your models here.
admin.site.register(Listing)
admin.site.register(Address)
admin.site.register(Category)
admin.site.register(Transaction)
admin.site.register(Transaction)
admin.site.register(Chat)
admin.site.register(Message)
52 changes: 52 additions & 0 deletions projektBazy/shop/migrations/0009_chat_review_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Generated by Django 4.2.17 on 2024-12-30 21:26

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


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('shop', '0008_alter_transaction_status'),
]

operations = [
migrations.CreateModel(
name='Chat',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('buyer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chats_as_buyer', to=settings.AUTH_USER_MODEL)),
('listing', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chats', to='shop.listing')),
('seller', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chats_as_seller', to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('buyer', 'seller', 'listing')},
},
),
migrations.CreateModel(
name='Review',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('rating', models.IntegerField()),
('comment', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('listing', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.listing')),
('reviewee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews_received', to=settings.AUTH_USER_MODEL)),
('reviewer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews_given', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Message',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('content', models.TextField()),
('status', models.CharField(choices=[('sent', 'Sent'), ('Viewed', 'Viewed')], default='sent', max_length=6)),
('created_at', models.DateTimeField(auto_now_add=True)),
('viewed_at', models.DateTimeField(blank=True, default=None, null=True)),
('chat', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='shop.chat')),
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chats', to=settings.AUTH_USER_MODEL)),
],
),
]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
33 changes: 32 additions & 1 deletion projektBazy/shop/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,35 @@ def save(self, *args, **kwargs):
def __str__(self):
return f"Transaction between {self.seller.username} and {self.buyer.username} for {self.listing.title} curently {self.status}"


class Chat(models.Model):
buyer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='chats_as_buyer', to_field='id')
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='chats_as_seller', to_field='id')
listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name='chats', to_field='id')

class Meta:
unique_together=("buyer", "seller", "listing")


class Message(models.Model):
Sent = 'sent'
Viewed = 'Viewed'

STATUS_CHOICES = [
(Sent, 'Sent'),
(Viewed, 'Viewed'),
]

chat = models.ForeignKey(Chat, on_delete=models.CASCADE, related_name='messages', to_field='id')
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='chats', to_field='id')
content = models.TextField()
status = models.CharField(max_length=6, choices=STATUS_CHOICES, default=Sent)
created_at = models.DateTimeField(auto_now_add=True)
viewed_at = models.DateTimeField(default=None, blank=True, null=True)

class Review(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
reviewer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reviews_given')
reviewee = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reviews_received')
rating = models.IntegerField()
comment = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)