Merge remote-tracking branch 'origin/main'

This commit is contained in:
marzban-dev
2024-12-14 18:22:17 +03:30
17 changed files with 273 additions and 5 deletions
@@ -0,0 +1,26 @@
# Generated by Django 5.1.2 on 2024-12-14 10:38
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0005_remove_user_groups_remove_user_user_permissions'),
]
operations = [
migrations.CreateModel(
name='UserAddressModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=30)),
('address', models.TextField()),
('postal_code', models.CharField(max_length=10)),
('phone', models.CharField(max_length=11)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
+9
View File
@@ -98,3 +98,12 @@ class User(AbstractBaseUser, PermissionsMixin):
return self.email return self.email
class UserAddressModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
address = models.TextField()
postal_code = models.CharField(max_length=10)
phone = models.CharField(max_length=11)
def __str__(self):
return f"{self.user.phone}, {self.name}"
+12
View File
@@ -9,3 +9,15 @@ class ProfileSerializer(serializers.ModelSerializer):
model = User model = User
fields = ['first_name', 'last_name', 'email', 'profile_photo', 'phone'] fields = ['first_name', 'last_name', 'email', 'profile_photo', 'phone']
read_only_fields = ("phone",) read_only_fields = ("phone",)
class UserAddressSerializer(serializers.ModelSerializer):
class Meta:
model = UserAddressModel
fields = ['id', 'name', 'address', 'postal_code', 'phone']
def validate(self, data):
user = self.context['request'].user
if not user.is_authenticated:
raise serializers.ValidationError("You must be logged in to perform this action.")
return data
+6
View File
@@ -3,4 +3,10 @@ from . import views
urlpatterns = [ urlpatterns = [
path('profile', views.ProfileView.as_view()), path('profile', views.ProfileView.as_view()),
path('address/create', views.CreateAddressView.as_view(), name='create-address'),
path('address/edit/<int:pk>', views.EditAddressView.as_view(), name='edit-address'),
path('address/delete/<int:pk>', views.DeleteAddressView.as_view(), name='delete-address'),
path('address/list', views.GetUserAddressesView.as_view(), name='list-addresses'),
path('address/<int:pk>', views.GetIDUserAddressView.as_view(), name='get-ID-address'),
] ]
+43 -5
View File
@@ -1,13 +1,14 @@
from django.shortcuts import render from django.shortcuts import render
from rest_framework.views import APIView from rest_framework.views import APIView
from .serializers import ProfileSerializer from rest_framework import generics, permissions, status
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
from .serializers import ProfileSerializer, UserAddressSerializer
from .models import UserAddressModel
from rest_framework.permissions import IsAuthenticated
class ProfileView(APIView): class ProfileView(APIView):
serializer_class = ProfileSerializer serializer_class = ProfileSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def get(self, request): def get(self, request):
user_ser = self.serializer_class(instance=request.user) user_ser = self.serializer_class(instance=request.user)
return Response(user_ser.data, status=status.HTTP_200_OK) return Response(user_ser.data, status=status.HTTP_200_OK)
@@ -19,4 +20,41 @@ class ProfileView(APIView):
if user_ser.is_valid(): if user_ser.is_valid():
user_ser.save() user_ser.save()
return Response(user_ser.data) return Response(user_ser.data)
return Response(user_ser.errors, status=status.HTTP_400_BAD_REQUEST) return Response(user_ser.errors, status=status.HTTP_400_BAD_REQUEST)
class CreateAddressView(generics.CreateAPIView):
queryset = UserAddressModel.objects.all()
serializer_class = UserAddressSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class EditAddressView(generics.UpdateAPIView):
queryset = UserAddressModel.objects.all()
serializer_class = UserAddressSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return UserAddressModel.objects.filter(user=self.request.user)
class DeleteAddressView(generics.DestroyAPIView):
queryset = UserAddressModel.objects.all()
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return UserAddressModel.objects.filter(user=self.request.user)
class GetUserAddressesView(generics.ListAPIView):
serializer_class = UserAddressSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return UserAddressModel.objects.filter(user=self.request.user)
class GetIDUserAddressView(generics.RetrieveAPIView):
serializer_class = UserAddressSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return UserAddressModel.objects.filter(user=self.request.user)
+1
View File
@@ -97,6 +97,7 @@ INSTALLED_APPS = [
'product', 'product',
'account', 'account',
'entertainment', 'entertainment',
'ticket',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
+1
View File
@@ -20,6 +20,7 @@ urlpatterns = [
# path('comment/<int:pk>', views.CommentView.as_view(), name='comment-list'), # path('comment/<int:pk>', views.CommentView.as_view(), name='comment-list'),
path('products/', include('product.urls')), path('products/', include('product.urls')),
path('accounts/', include('account.urls')), path('accounts/', include('account.urls')),
path('tickets/', include('ticket.urls')),
path('', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), path('', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
] ]
View File
+3
View File
@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.
+6
View File
@@ -0,0 +1,6 @@
from django.apps import AppConfig
class TicketConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'ticket'
+39
View File
@@ -0,0 +1,39 @@
# Generated by Django 5.1.2 on 2024-12-14 10:51
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Ticket',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('subject', models.CharField(max_length=255)),
('status', models.CharField(choices=[('open', 'Open'), ('in_progress', 'In Progress'), ('resolved', 'Resolved'), ('closed', 'Closed')], default='open', max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('admin', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_tickets', to=settings.AUTH_USER_MODEL)),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tickets', 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()),
('created_at', models.DateTimeField(auto_now_add=True)),
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='ticket.ticket')),
],
),
]
+29
View File
@@ -0,0 +1,29 @@
from django.db import models
from account.models import User
class Ticket(models.Model):
STATUS_CHOICES = [
('open', 'Open'),
('in_progress', 'In Progress'),
('resolved', 'Resolved'),
('closed', 'Closed'),
]
subject = models.CharField(max_length=255)
customer = models.ForeignKey(User, on_delete=models.CASCADE, related_name="tickets")
admin = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name="assigned_tickets")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='open')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.subject
class Message(models.Model):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name="messages")
sender = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Message by {self.sender.username} on {self.ticket.subject}"
+14
View File
@@ -0,0 +1,14 @@
from rest_framework import serializers
from .models import Ticket, Message
class MessageSerializer(serializers.ModelSerializer):
class Meta:
model = Message
fields = '__all__'
class TicketSerializer(serializers.ModelSerializer):
messages = MessageSerializer(many=True, read_only=True)
class Meta:
model = Ticket
fields = '__all__'
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+16
View File
@@ -0,0 +1,16 @@
from django.urls import path
from .views import (
TicketCreateView,
TicketListView,
TicketDetailView,
MessageCreateView,
UpdateTicketStatusView
)
urlpatterns = [
path('create', TicketCreateView.as_view(), name='ticket-create'),
path('', TicketListView.as_view(), name='ticket-list'),
path('<int:pk>', TicketDetailView.as_view(), name='ticket-detail'),
path('<int:pk>/messages', MessageCreateView.as_view(), name='message-create'),
path('<int:pk>/update-status', UpdateTicketStatusView.as_view(), name='update-ticket-status'),
]
+65
View File
@@ -0,0 +1,65 @@
from rest_framework import generics, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import Ticket, Message
from .serializers import TicketSerializer, MessageSerializer
class TicketCreateView(generics.CreateAPIView):
queryset = Ticket.objects.all()
serializer_class = TicketSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer):
serializer.save(customer=self.request.user)
class TicketListView(generics.ListAPIView):
serializer_class = TicketSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
user = self.request.user
if user.is_staff:
return Ticket.objects.all()
return Ticket.objects.filter(customer=user)
class TicketDetailView(generics.RetrieveAPIView):
queryset = Ticket.objects.all()
serializer_class = TicketSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
user = self.request.user
if user.is_staff:
return Ticket.objects.all()
return Ticket.objects.filter(customer=user)
class MessageCreateView(generics.CreateAPIView):
queryset = Message.objects.all()
serializer_class = MessageSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer):
ticket = serializer.validated_data.get('ticket')
if self.request.user != ticket.customer and not self.request.user.is_staff:
raise permissions.PermissionDenied("You are not authorized to send a message to this ticket.")
serializer.save(sender=self.request.user)
class UpdateTicketStatusView(APIView):
permission_classes = [permissions.IsAdminUser]
def post(self, request, pk):
try:
ticket = Ticket.objects.get(pk=pk)
except Ticket.DoesNotExist:
return Response({"error": "Ticket not found"}, status=404)
new_status = request.data.get('status')
if new_status not in ['open', 'in_progress', 'resolved', 'closed']:
return Response({"error": "Invalid status"}, status=400)
ticket.status = new_status
ticket.save()
return Response({"message": "Ticket status updated successfully"})