CCCPaste

d464c2ac3f990

from django.contrib.auth.base_user import AbstractBaseUser
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from phonenumber_field.modelfields import PhoneNumberField
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import BaseUserManager

class UserManager(BaseUserManager):
    def create_user(self, phone, email=None, password=None, **extra_fields):
        if not phone:
            raise ValueError('The Phone field must be set')
        user = self.model(phone=phone, email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, phone, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self.create_user(phone, email, password, **extra_fields)

class User(AbstractBaseUser):
    name = models.CharField(max_length=255, blank=True, null=True)
    email = models.EmailField(unique=False, null=True, blank=True)
    phone = PhoneNumberField(unique=True)
    about_user = models.TextField(null=True, blank=True)
    work_experience = models.FloatField(null=True, blank=True)
    role = models.CharField(max_length=50, choices=[('client', 'Client'), ('executor', 'Executor')], default='client')
    password = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    region = models.CharField(max_length=100, null=True, blank=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    telegram_username = models.CharField(max_length=255, null=True, blank=True)
    gender = models.CharField(max_length=10, choices=[('male', 'Male'), ('female', 'Female')], null=True, blank=True)
    executor_rating = models.FloatField(default=0.0, validators=[MinValueValidator(0.0), MaxValueValidator(5.0)])
    client_rating = models.FloatField(default=0.0, validators=[MinValueValidator(0.0), MaxValueValidator(5.0)])
    avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
    birthday = models.DateField(null=True, blank=True)

    objects = UserManager()

    USERNAME_FIELD = 'phone'
    REQUIRED_FIELDS = []

    class Meta:
        db_table = 'users'

    def __str__(self):
        return str(self.phone)

    def has_perm(self, perm, obj=None):
        return self.is_superuser

    def has_module_perms(self, app_label):
        return self.is_superuser

    def update_ratings(self):
        # Calculate average executor rating
        executor_reviews = self.executor_reviews_as_executor.all()
        if executor_reviews.exists():
            self.executor_rating = executor_reviews.aggregate(avg_rating=models.Avg('rating'))['avg_rating'] or 0.0
        else:
            self.executor_rating = 0.0

        # Calculate average client rating
        client_reviews = self.client_reviews_as_client.all()
        if client_reviews.exists():
            self.client_rating = client_reviews.aggregate(avg_rating=models.Avg('rating'))['avg_rating'] or 0.0
        else:
            self.client_rating = 0.0

        self.save()

class OTP_table(models.Model):
    phone = PhoneNumberField()
    code = models.CharField(max_length=6)
    created_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()

    class Meta:
        db_table = 'otp_table'

    def __str__(self):
        return f"Code {self.code} for {self.phone}"

class Category(models.Model):
    name = models.CharField(max_length=255)
    display_ru = models.CharField(max_length=255, null=True, blank=True)
    display_uz = models.CharField(max_length=255, null=True, blank=True)
    service_count = models.IntegerField(default=0)

class SubCategory(models.Model):
    name = models.CharField(max_length=255)
    display_ru = models.CharField(max_length=255, blank=True, null=True)
    display_uz = models.CharField(max_length=255, blank=True, null=True)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='sub_categories')

    class Meta:
        db_table = 'sub_categories'

    def __str__(self):
        return self.name

class Service(models.Model):
    MODERATION_CHOICES = (
        ('Pending', 'Pending'),
        ('Approved', 'Approved'),
        ('Rejected', 'Rejected'),
    )
    name = models.CharField(max_length=255)
    description = models.TextField()
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='services')
    sub_categories = models.ManyToManyField(SubCategory, related_name='services')
    price = models.DecimalField(max_digits=10, decimal_places=2)
    executor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='services')
    moderation = models.CharField(max_length=20, choices=MODERATION_CHOICES, default='Pending', null=True, blank=True)

    class Meta:
        db_table = 'services'

    def __str__(self):
        return self.name

class ServiceImage(models.Model):
    service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='images')
    image = models.ImageField(upload_to='services/')
    uploaded_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'service_images'

    def __str__(self):
        return f"Image for {self.service.name} uploaded at {self.uploaded_at}"

class Vacancy(models.Model):
    name = models.CharField(max_length=255, null=False, blank=False)
    description = models.CharField(max_length=512, null=True, blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='category')
    sub_categories = models.ForeignKey(SubCategory, on_delete=models.CASCADE, related_name="sub_categories")
    client = models.ForeignKey(User, on_delete=models.CASCADE, related_name="client")
    images = models.ImageField(upload_to='vacancies/')

class VacancyImage(models.Model):
    vacancy = models.ForeignKey(
        Vacancy,
        on_delete=models.CASCADE,
        related_name="images"
    )
    image = models.ImageField(upload_to="vacancies/")
    uploaded_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Image for {self.vacancy.name}"

class ExecutorReview(models.Model):
    order = models.ForeignKey('Order', on_delete=models.CASCADE, related_name='executor_reviews')
    executor = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='executor_reviews_as_executor'
    )
    client = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='executor_reviews_as_client'
    )
    rating = models.FloatField(
        validators=[MinValueValidator(1.0), MaxValueValidator(5.0)],
        help_text="Rating between 1.0 and 5.0"
    )
    review = models.TextField(max_length=250, help_text="Review text up to 250 characters")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'executor_reviews'
        unique_together = ('order', 'client')  # Prevent multiple reviews by the same client for an order

    def __str__(self):
        return f"Review by {self.client} for executor {self.executor} on order {self.order.id}"


    vacancy = models.ForeignKey(Vacancy, on_delete=models.CASCADE, related_name='images')
    image = models.ImageField(upload_to='vacancies/')
    uploaded_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'vacancy_images'

    def __str__(self):
        return f"Image for {self.vacancy.name} uploaded at {self.uploaded_at}"

class ClientReview(models.Model):
    order = models.ForeignKey('Order', on_delete=models.CASCADE, related_name='client_reviews')
    executor = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='client_reviews_as_executor'
    )
    client = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='client_reviews_as_client'
    )
    rating = models.FloatField(
        validators=[MinValueValidator(1.0), MaxValueValidator(5.0)],
        help_text="Rating between 1.0 and 5.0"
    )
    review = models.TextField(max_length=250, help_text="Review text up to 250 characters")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'client_reviews'
        unique_together = ('order', 'executor')  # Prevent multiple reviews by the same executor for an order

    def __str__(self):
        return f"Review by {self.executor} for client {self.client} on order {self.order.id}"

class ChatRoom(models.Model):
    name = models.CharField(max_length=255, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)

    participants = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name="chat_rooms",
        blank=True
    )

    def __str__(self):
        return self.name or f"ChatRoom {self.id}"

class Chat_table(models.Model):
    user1 = models.ForeignKey(User, on_delete=models.CASCADE, related_name="chats_started")
    user2 = models.ForeignKey(User, on_delete=models.CASCADE, related_name="chats_received")
    created_at = models.DateTimeField(auto_now_add=True)
    last_message = models.TextField(blank=True, null=True)

    def __str__(self):
        return f"Chat {self.id}: {self.user1} - {self.user2}"

class Order(models.Model):
    service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='orders', null=True, blank=True)
    vacancy = models.ForeignKey(Vacancy, on_delete=models.CASCADE, related_name='orders', null=True, blank=True)
    client = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='client_orders')
    status = models.CharField(max_length=50, choices=[
        ('Awaiting', 'Awaiting'),
        ('Pending', 'Pending'),
        ('Completed', 'Completed'),
        ('Cancelled', 'Cancelled')
    ], default='Awaiting')

    class Meta:
        db_table = 'orders'

    def __str__(self):
        return f"Order {self.id} for {self.service.name if self.service else self.vacancy.name}"

    def clean(self):
        from django.core.exceptions import ValidationError
        if (self.service is None and self.vacancy is None) or (self.service is not None and self.vacancy is not None):
            raise ValidationError("An order must reference exactly one service or vacancy.")

    room = models.ForeignKey(ChatRoom, on_delete=models.CASCADE, related_name='messages')
    sender = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='sent_messages')
    content = models.TextField(null=True, blank=True)
    file = models.FileField(upload_to='chat_files/', null=True, blank=True)
    timestamp = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'messages'
        ordering = ['timestamp']

    def __str__(self):
        return f"Message in {self.room.name} by {self.sender.name}"

class Reklama(models.Model):
    image = models.ImageField(upload_to='reklamas/', verbose_name="Reklama Banner")
    start_date = models.DateTimeField(verbose_name="Start of Showing Reklama")
    end_date = models.DateTimeField(verbose_name="End of Showing Reklama")
    region = models.CharField(max_length=500, null=True, blank=True, verbose_name="Regions (comma-separated)", help_text="Enter regions separated by commas, e.g., 'Region1, Region2'")
    link = models.URLField(max_length=2000, null=True, blank=True, verbose_name="Click Link", help_text="URL where the banner should redirect when clicked")

    class Meta:
        db_table = 'reklamas'
        verbose_name = "Reklama"
        verbose_name_plural = "Reklamas"

    def __str__(self):
        return f"Reklama from {self.start_date} to {self.end_date}"

# New Boost-related models
class Boost(models.Model):
    name = models.CharField(max_length=100, unique=True)  # e.g., "Top 1 Day", "Turbo 1 Week"
    boost_type = models.CharField(max_length=20, choices=[('Top', 'Top'), ('Turbo', 'Turbo')])
    duration_days = models.PositiveIntegerField()  # Duration in days (e.g., 1, 7, 14, 30)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    discount = models.DecimalField(max_digits=5, decimal_places=2, default=0.00)  # Percentage discount (e.g., 10.00 for 10%)
    applies_to = models.CharField(max_length=20, choices=[('Service', 'Service'), ('Vacancy', 'Vacancy')])

    class Meta:
        db_table = 'boosts'

    def __str__(self):
        return f"{self.name} ({self.boost_type}) for {self.applies_to} - {self.duration_days} days"

class BoostPayment(models.Model):
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    service = models.ForeignKey('Service', on_delete=models.SET_NULL, null=True, blank=True)
    vacancy = models.ForeignKey('Vacancy', on_delete=models.SET_NULL, null=True, blank=True)
    boost = models.ForeignKey('Boost', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=20, default='pending')  # pending, completed, cancelled
    boost_payment_id = models.IntegerField(editable=False, unique=True, null=True, blank=True)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        if self.boost_payment_id != self.id:
            self.boost_payment_id = self.id
            super().save(update_fields=['boost_payment_id'])

    def __str__(self):
        return f"Order #{self.id}"

@receiver(post_save, sender=ExecutorReview)
@receiver(post_save, sender=ClientReview)
def update_user_ratings(sender, instance, created, **kwargs):
    if created:
        if sender == ExecutorReview:
            instance.executor.update_ratings()
        elif sender == ClientReview:
            instance.client.update_ratings()