* Vietnamese translation incomplete

Classified ads Models

Classified ads Models (English fallback)

Aug. 16, 2025

Posted by admin

Nhom

Notes

 

from django.db import models

from django.contrib.auth.models import User

from django.urls import reverse

from django.utils import timezone

from django.utils.translation import gettext_lazy as _

from ckeditor_uploader.fields import RichTextUploadingField

 

 

 

 

 

class AdStatus(models.TextChoices):

    DRAFT = 'draft', 'Draft'

    ACTIVE = 'active', 'Active'

    SOLD = 'sold', 'Sold'

    EXPIRED = 'expired', 'Expired'

    SUSPENDED = 'suspended', 'Suspended'

 

class AdType(models.TextChoices):

    SELL = 'sell', 'For Sale'

    BUY = 'buy', 'Looking to Buy'

    RENT = 'rent', 'For Rent'

    SERVICE = 'service', 'Service'

    JOB = 'job', 'Job'

 

 

 

class Location(models.Model):

    """Predefined locations for classified ads"""

    name = models.CharField(max_length=100)

    slug = models.SlugField(unique=True)

    state_province = models.CharField(max_length=50, blank=True)

    country = models.CharField(max_length=50, default='Vietnam')

    is_popular = models.BooleanField(default=False, help_text="Show in popular locations")

    is_active = models.BooleanField(default=True)

    order = models.PositiveIntegerField(default=0)

    created_at = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        verbose_name = _("Ad Location")

        verbose_name_plural = _("Ad Locations")

        ordering = ['order', 'name']

        verbose_name_plural = "Locations"

   

    def __str__(self):

        if self.state_province:

            return f"{self.name}, {self.state_province}"

        return self.name

   

    def get_ads_count(self):

        """Get count of active ads in this location"""

        return ClassifiedAd.objects.filter(

            status=AdStatus.ACTIVE,

            location__icontains=self.name

        ).count()

 

 

Ads

class ClassifiedAd(models.Model):

    """Main classified ad model"""

    TRANSLATION_STATUS_CHOICES = [

        ('pending', 'Pending Translation'),

        ('translated', 'Translated'),

        ('reviewed', 'Reviewed'),

    ]

   

    title = models.CharField(max_length=200)

    title_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Title")

    description = RichTextUploadingField()

    description_vi = RichTextUploadingField(blank=True, verbose_name="Vietnamese Description")

    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='ads')

    ad_type = models.CharField(max_length=20, choices=AdType.choices, default=AdType.SELL)

   

    # User information

    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='classified_ads')

    contact_name = models.CharField(max_length=100, blank=True)

    contact_email = models.EmailField(blank=True)

    contact_phone = models.CharField(max_length=20, blank=True)

   

    # Location

    location = models.CharField(max_length=200, blank=True)

   

    # Pricing

    price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)

    price_negotiable = models.BooleanField(default=False)

   

    # Status and timing

    status = models.CharField(max_length=20, choices=AdStatus.choices, default=AdStatus.DRAFT)

    featured = models.BooleanField(default=False)

    expires_at = models.DateTimeField(null=True, blank=True)

   

    # Timestamps

    created_at = models.DateTimeField(auto_now_add=True)

    updated_at = models.DateTimeField(auto_now=True)

    published_at = models.DateTimeField(null=True, blank=True)

   

    # Vietnamese translation fields

    translation_status = models.CharField(

        max_length=20,

        choices=TRANSLATION_STATUS_CHOICES,

        default='pending',

        help_text="Status of Vietnamese translation"

    )

    translated_by = models.CharField(max_length=100, blank=True, help_text="Name of translator")

    translated_at = models.DateTimeField(null=True, blank=True, help_text="When translation was completed")

   

    # Counters

    view_count = models.PositiveIntegerField(default=0)

   

    class Meta:

        ordering = ['-created_at']

        indexes = [

            models.Index(fields=['status']),

            models.Index(fields=['category']),

            models.Index(fields=['created_at']),

            models.Index(fields=['featured']),

        ]

   

    def __str__(self):

        return self.title

   

    def get_display_title(self, language='en'):

        """Get ad title in specified language"""

        if language == 'vi' and self.title_vi:

            return self.title_vi

        return self.title

   

    def get_display_description(self, language='en'):

        """Get ad description in specified language"""

        if language == 'vi' and self.description_vi:

            return self.description_vi

        return self.description

   

    def is_translated(self):

        """Check if ad has Vietnamese translation"""

        return bool(self.title_vi and self.description_vi)

   

    def save(self, *args, **kwargs):

        if self.status == AdStatus.ACTIVE and not self.published_at:

            self.published_at = timezone.now()

        super().save(*args, **kwargs)

   

    def get_absolute_url(self):

        return reverse('classified_ads:detail', kwargs={'pk': self.pk})

   

    def is_expired(self):

        if self.expires_at and timezone.now() > self.expires_at:

            return True

        return False

   

    @property

    def days_since_published(self):

        if self.published_at:

            return (timezone.now() - self.published_at).days

        return 0

 

Ads Category

class Category(models.Model):

    """Categories for classified ads"""

    TRANSLATION_STATUS_CHOICES = [

        ('pending', 'Pending Translation'),

        ('translated', 'Translated'),

        ('reviewed', 'Reviewed'),

    ]

   

    name = models.CharField(max_length=100)

    name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name")

    description = models.TextField(blank=True)

    description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")

    parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')

    icon = models.CharField(max_length=50, blank=True, help_text="Font Awesome icon class (e.g., fas fa-car)")

    slug = models.SlugField(max_length=255, unique=True)

    is_active = models.BooleanField(default=True)

    created_at = models.DateTimeField(auto_now_add=True)

   

    # Vietnamese translation fields

    translation_status = models.CharField(

        max_length=20,

        choices=TRANSLATION_STATUS_CHOICES,

        default='pending',

        help_text="Status of Vietnamese translation"

    )

    translated_by = models.CharField(max_length=100, blank=True, help_text="Name of translator")

    translated_at = models.DateTimeField(null=True, blank=True, help_text="When translation was completed")

   

    class Meta:

        verbose_name = _("Ad Category")

        verbose_name_plural = _("Ad Categories")

        verbose_name_plural = "Categories"

        ordering = ['name']

   

    def __str__(self):

        return self.name

   

    def get_display_name(self, language='en'):

        """Get category name in specified language"""

        if language == 'vi' and self.name_vi:

            return self.name_vi

        return self.name

   

    def get_display_description(self, language='en'):

        """Get category description in specified language"""

        if language == 'vi' and self.description_vi:

            return self.description_vi

        return self.description

   

    def is_translated(self):

        """Check if category has Vietnamese translation"""

        return bool(self.name_vi and self.description_vi)

   

    def get_absolute_url(self):

        return reverse('classified_ads:category', kwargs={'slug': self.slug})

 

 

 

Ads favorite

class Favorite(models.Model):

    """User favorites"""

    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='favorites')

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='favorited_by')

    created_at = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        verbose_name = _("Favorite Ad")

        verbose_name_plural = _("Favorite Ads")

        unique_together = ['user', 'ad']

        ordering = ['-created_at']

   

    def __str__(self):

        return f"{self.user.username} - {self.ad.title}"

 

 

Ads Image

class AdImage(models.Model):

    """Images for classified ads"""

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='images')

    image = models.ImageField(upload_to='classified_ads/images/%Y/%m/')

    caption = models.CharField(max_length=200, blank=True)

    is_primary = models.BooleanField(default=False)

    order = models.PositiveIntegerField(default=0)

    uploaded_at = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        ordering = ['order', 'uploaded_at']

   

    def __str__(self):

        return f"Image for {self.ad.title}"

 

 

Ads inquiry

class AdInquiry(models.Model):

    """Inquiries about ads"""

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='inquiries')

    name = models.CharField(max_length=100)

    email = models.EmailField()

    phone = models.CharField(max_length=20, blank=True)

    message = models.TextField()

    created_at = models.DateTimeField(auto_now_add=True)

    is_read = models.BooleanField(default=False)

   

    class Meta:

        ordering = ['-created_at']

        verbose_name_plural = "Ad Inquiries"

   

    def __str__(self):

        return f"Inquiry for {self.ad.title} from {self.name}"

 

 

Ads Promotion

class AdPromotion(models.Model):

    """Paid promotions for ads"""

    PROMOTION_TYPES = (

        ('featured', 'Featured Ad - $5/week'),

        ('top_listing', 'Top Listing - $10/week'),

        ('homepage_banner', 'Homepage Banner - $25/week'),

        ('category_banner', 'Category Banner - $15/week'),

        ('urgent', 'Urgent Ad - $3/week'),

        ('highlight', 'Highlighted Ad - $2/week'),

    )

   

    PAYMENT_STATUS = (

        ('pending', 'Pending'),

        ('completed', 'Completed'),

        ('failed', 'Failed'),

        ('refunded', 'Refunded'),

    )

   

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='promotions')

    promotion_type = models.CharField(max_length=20, choices=PROMOTION_TYPES)

    amount = models.DecimalField(max_digits=10, decimal_places=2)

    duration_days = models.PositiveIntegerField(default=7)

   

    payment_status = models.CharField(max_length=20, choices=PAYMENT_STATUS, default='pending')

    payment_method = models.CharField(max_length=50, blank=True)

    payment_id = models.CharField(max_length=100, blank=True)

   

    start_date = models.DateTimeField()

    end_date = models.DateTimeField()

    is_active = models.BooleanField(default=True)

   

    created_at = models.DateTimeField(auto_now_add=True)

    updated_at = models.DateTimeField(auto_now=True)

   

    class Meta:

        ordering = ['-created_at']

   

    def __str__(self):

        return f"{self.ad.title} - {self.get_promotion_type_display()}"

   

    def save(self, *args, **kwargs):

        # Auto-calculate end_date

        if not self.end_date and self.start_date:

            from datetime import timedelta

            self.end_date = self.start_date + timedelta(days=self.duration_days)

        super().save(*args, **kwargs)

 

 

Ads Revenue

class PlatformRevenue(models.Model):

    """Track platform revenue streams"""

    REVENUE_TYPES = (

        ('subscription', 'User Subscription'),

        ('promotion', 'Ad Promotion'),

        ('commission', 'Transaction Commission'),

        ('featured_listing', 'Featured Listing Fee'),

        ('banner_ad', 'Banner Advertisement'),

        ('premium_placement', 'Premium Placement'),

    )

   

    revenue_type = models.CharField(max_length=20, choices=REVENUE_TYPES)

    amount = models.DecimalField(max_digits=10, decimal_places=2)

    currency = models.CharField(max_length=3, default='USD')

   

    # Related objects

    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.SET_NULL, null=True, blank=True)

    promotion = models.ForeignKey(AdPromotion, on_delete=models.SET_NULL, null=True, blank=True)

   

    # Payment details

    payment_method = models.CharField(max_length=50)

    payment_id = models.CharField(max_length=100, blank=True)

    transaction_fee = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

    net_amount = models.DecimalField(max_digits=10, decimal_places=2)

   

    description = models.TextField(blank=True)

    date_created = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        ordering = ['-date_created']

   

    def __str__(self):

        return f"{self.get_revenue_type_display()} - ${self.amount}"

   

    def save(self, *args, **kwargs):

        # Calculate net amount after transaction fees

        if not self.net_amount:

            self.net_amount = self.amount - self.transaction_fee

        super().save(*args, **kwargs)

 

 

Ads Statistic

class AdStatistics(models.Model):

    """Track ad performance for analytics"""

    ad = models.OneToOneField(ClassifiedAd, on_delete=models.CASCADE, related_name='statistics')

   

    # View statistics

    total_views = models.PositiveIntegerField(default=0)

    unique_views = models.PositiveIntegerField(default=0)

    views_today = models.PositiveIntegerField(default=0)

    views_this_week = models.PositiveIntegerField(default=0)

    views_this_month = models.PositiveIntegerField(default=0)

   

    # Contact statistics

    total_contacts = models.PositiveIntegerField(default=0)

    phone_clicks = models.PositiveIntegerField(default=0)

    email_clicks = models.PositiveIntegerField(default=0)

    inquiries_received = models.PositiveIntegerField(default=0)

   

    # Social sharing

    shares_count = models.PositiveIntegerField(default=0)

    favorites_count = models.PositiveIntegerField(default=0)

   

    # Revenue tracking

    revenue_generated = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

    promotion_spent = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

   

    last_updated = models.DateTimeField(auto_now=True)

   

    def __str__(self):

        return f"Stats for {self.ad.title}"

 

 

Ads View

class AdView(models.Model):

    """Track ad views"""

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='ad_views')

    ip_address = models.GenericIPAddressField()

    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

    viewed_at = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        unique_together = ['ad', 'ip_address']

        ordering = ['-viewed_at']

 

 

Vietnamese translation is not available for this article. Showing English content:

Nhom

Notes

 

from django.db import models

from django.contrib.auth.models import User

from django.urls import reverse

from django.utils import timezone

from django.utils.translation import gettext_lazy as _

from ckeditor_uploader.fields import RichTextUploadingField

 

 

 

 

 

class AdStatus(models.TextChoices):

    DRAFT = 'draft', 'Draft'

    ACTIVE = 'active', 'Active'

    SOLD = 'sold', 'Sold'

    EXPIRED = 'expired', 'Expired'

    SUSPENDED = 'suspended', 'Suspended'

 

class AdType(models.TextChoices):

    SELL = 'sell', 'For Sale'

    BUY = 'buy', 'Looking to Buy'

    RENT = 'rent', 'For Rent'

    SERVICE = 'service', 'Service'

    JOB = 'job', 'Job'

 

 

 

class Location(models.Model):

    """Predefined locations for classified ads"""

    name = models.CharField(max_length=100)

    slug = models.SlugField(unique=True)

    state_province = models.CharField(max_length=50, blank=True)

    country = models.CharField(max_length=50, default='Vietnam')

    is_popular = models.BooleanField(default=False, help_text="Show in popular locations")

    is_active = models.BooleanField(default=True)

    order = models.PositiveIntegerField(default=0)

    created_at = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        verbose_name = _("Ad Location")

        verbose_name_plural = _("Ad Locations")

        ordering = ['order', 'name']

        verbose_name_plural = "Locations"

   

    def __str__(self):

        if self.state_province:

            return f"{self.name}, {self.state_province}"

        return self.name

   

    def get_ads_count(self):

        """Get count of active ads in this location"""

        return ClassifiedAd.objects.filter(

            status=AdStatus.ACTIVE,

            location__icontains=self.name

        ).count()

 

 

Ads

class ClassifiedAd(models.Model):

    """Main classified ad model"""

    TRANSLATION_STATUS_CHOICES = [

        ('pending', 'Pending Translation'),

        ('translated', 'Translated'),

        ('reviewed', 'Reviewed'),

    ]

   

    title = models.CharField(max_length=200)

    title_vi = models.CharField(max_length=200, blank=True, verbose_name="Vietnamese Title")

    description = RichTextUploadingField()

    description_vi = RichTextUploadingField(blank=True, verbose_name="Vietnamese Description")

    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='ads')

    ad_type = models.CharField(max_length=20, choices=AdType.choices, default=AdType.SELL)

   

    # User information

    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='classified_ads')

    contact_name = models.CharField(max_length=100, blank=True)

    contact_email = models.EmailField(blank=True)

    contact_phone = models.CharField(max_length=20, blank=True)

   

    # Location

    location = models.CharField(max_length=200, blank=True)

   

    # Pricing

    price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)

    price_negotiable = models.BooleanField(default=False)

   

    # Status and timing

    status = models.CharField(max_length=20, choices=AdStatus.choices, default=AdStatus.DRAFT)

    featured = models.BooleanField(default=False)

    expires_at = models.DateTimeField(null=True, blank=True)

   

    # Timestamps

    created_at = models.DateTimeField(auto_now_add=True)

    updated_at = models.DateTimeField(auto_now=True)

    published_at = models.DateTimeField(null=True, blank=True)

   

    # Vietnamese translation fields

    translation_status = models.CharField(

        max_length=20,

        choices=TRANSLATION_STATUS_CHOICES,

        default='pending',

        help_text="Status of Vietnamese translation"

    )

    translated_by = models.CharField(max_length=100, blank=True, help_text="Name of translator")

    translated_at = models.DateTimeField(null=True, blank=True, help_text="When translation was completed")

   

    # Counters

    view_count = models.PositiveIntegerField(default=0)

   

    class Meta:

        ordering = ['-created_at']

        indexes = [

            models.Index(fields=['status']),

            models.Index(fields=['category']),

            models.Index(fields=['created_at']),

            models.Index(fields=['featured']),

        ]

   

    def __str__(self):

        return self.title

   

    def get_display_title(self, language='en'):

        """Get ad title in specified language"""

        if language == 'vi' and self.title_vi:

            return self.title_vi

        return self.title

   

    def get_display_description(self, language='en'):

        """Get ad description in specified language"""

        if language == 'vi' and self.description_vi:

            return self.description_vi

        return self.description

   

    def is_translated(self):

        """Check if ad has Vietnamese translation"""

        return bool(self.title_vi and self.description_vi)

   

    def save(self, *args, **kwargs):

        if self.status == AdStatus.ACTIVE and not self.published_at:

            self.published_at = timezone.now()

        super().save(*args, **kwargs)

   

    def get_absolute_url(self):

        return reverse('classified_ads:detail', kwargs={'pk': self.pk})

   

    def is_expired(self):

        if self.expires_at and timezone.now() > self.expires_at:

            return True

        return False

   

    @property

    def days_since_published(self):

        if self.published_at:

            return (timezone.now() - self.published_at).days

        return 0

 

Ads Category

class Category(models.Model):

    """Categories for classified ads"""

    TRANSLATION_STATUS_CHOICES = [

        ('pending', 'Pending Translation'),

        ('translated', 'Translated'),

        ('reviewed', 'Reviewed'),

    ]

   

    name = models.CharField(max_length=100)

    name_vi = models.CharField(max_length=100, blank=True, verbose_name="Vietnamese Name")

    description = models.TextField(blank=True)

    description_vi = models.TextField(blank=True, verbose_name="Vietnamese Description")

    parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')

    icon = models.CharField(max_length=50, blank=True, help_text="Font Awesome icon class (e.g., fas fa-car)")

    slug = models.SlugField(max_length=255, unique=True)

    is_active = models.BooleanField(default=True)

    created_at = models.DateTimeField(auto_now_add=True)

   

    # Vietnamese translation fields

    translation_status = models.CharField(

        max_length=20,

        choices=TRANSLATION_STATUS_CHOICES,

        default='pending',

        help_text="Status of Vietnamese translation"

    )

    translated_by = models.CharField(max_length=100, blank=True, help_text="Name of translator")

    translated_at = models.DateTimeField(null=True, blank=True, help_text="When translation was completed")

   

    class Meta:

        verbose_name = _("Ad Category")

        verbose_name_plural = _("Ad Categories")

        verbose_name_plural = "Categories"

        ordering = ['name']

   

    def __str__(self):

        return self.name

   

    def get_display_name(self, language='en'):

        """Get category name in specified language"""

        if language == 'vi' and self.name_vi:

            return self.name_vi

        return self.name

   

    def get_display_description(self, language='en'):

        """Get category description in specified language"""

        if language == 'vi' and self.description_vi:

            return self.description_vi

        return self.description

   

    def is_translated(self):

        """Check if category has Vietnamese translation"""

        return bool(self.name_vi and self.description_vi)

   

    def get_absolute_url(self):

        return reverse('classified_ads:category', kwargs={'slug': self.slug})

 

 

 

Ads favorite

class Favorite(models.Model):

    """User favorites"""

    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='favorites')

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='favorited_by')

    created_at = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        verbose_name = _("Favorite Ad")

        verbose_name_plural = _("Favorite Ads")

        unique_together = ['user', 'ad']

        ordering = ['-created_at']

   

    def __str__(self):

        return f"{self.user.username} - {self.ad.title}"

 

 

Ads Image

class AdImage(models.Model):

    """Images for classified ads"""

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='images')

    image = models.ImageField(upload_to='classified_ads/images/%Y/%m/')

    caption = models.CharField(max_length=200, blank=True)

    is_primary = models.BooleanField(default=False)

    order = models.PositiveIntegerField(default=0)

    uploaded_at = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        ordering = ['order', 'uploaded_at']

   

    def __str__(self):

        return f"Image for {self.ad.title}"

 

 

Ads inquiry

class AdInquiry(models.Model):

    """Inquiries about ads"""

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='inquiries')

    name = models.CharField(max_length=100)

    email = models.EmailField()

    phone = models.CharField(max_length=20, blank=True)

    message = models.TextField()

    created_at = models.DateTimeField(auto_now_add=True)

    is_read = models.BooleanField(default=False)

   

    class Meta:

        ordering = ['-created_at']

        verbose_name_plural = "Ad Inquiries"

   

    def __str__(self):

        return f"Inquiry for {self.ad.title} from {self.name}"

 

 

Ads Promotion

class AdPromotion(models.Model):

    """Paid promotions for ads"""

    PROMOTION_TYPES = (

        ('featured', 'Featured Ad - $5/week'),

        ('top_listing', 'Top Listing - $10/week'),

        ('homepage_banner', 'Homepage Banner - $25/week'),

        ('category_banner', 'Category Banner - $15/week'),

        ('urgent', 'Urgent Ad - $3/week'),

        ('highlight', 'Highlighted Ad - $2/week'),

    )

   

    PAYMENT_STATUS = (

        ('pending', 'Pending'),

        ('completed', 'Completed'),

        ('failed', 'Failed'),

        ('refunded', 'Refunded'),

    )

   

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='promotions')

    promotion_type = models.CharField(max_length=20, choices=PROMOTION_TYPES)

    amount = models.DecimalField(max_digits=10, decimal_places=2)

    duration_days = models.PositiveIntegerField(default=7)

   

    payment_status = models.CharField(max_length=20, choices=PAYMENT_STATUS, default='pending')

    payment_method = models.CharField(max_length=50, blank=True)

    payment_id = models.CharField(max_length=100, blank=True)

   

    start_date = models.DateTimeField()

    end_date = models.DateTimeField()

    is_active = models.BooleanField(default=True)

   

    created_at = models.DateTimeField(auto_now_add=True)

    updated_at = models.DateTimeField(auto_now=True)

   

    class Meta:

        ordering = ['-created_at']

   

    def __str__(self):

        return f"{self.ad.title} - {self.get_promotion_type_display()}"

   

    def save(self, *args, **kwargs):

        # Auto-calculate end_date

        if not self.end_date and self.start_date:

            from datetime import timedelta

            self.end_date = self.start_date + timedelta(days=self.duration_days)

        super().save(*args, **kwargs)

 

 

Ads Revenue

class PlatformRevenue(models.Model):

    """Track platform revenue streams"""

    REVENUE_TYPES = (

        ('subscription', 'User Subscription'),

        ('promotion', 'Ad Promotion'),

        ('commission', 'Transaction Commission'),

        ('featured_listing', 'Featured Listing Fee'),

        ('banner_ad', 'Banner Advertisement'),

        ('premium_placement', 'Premium Placement'),

    )

   

    revenue_type = models.CharField(max_length=20, choices=REVENUE_TYPES)

    amount = models.DecimalField(max_digits=10, decimal_places=2)

    currency = models.CharField(max_length=3, default='USD')

   

    # Related objects

    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.SET_NULL, null=True, blank=True)

    promotion = models.ForeignKey(AdPromotion, on_delete=models.SET_NULL, null=True, blank=True)

   

    # Payment details

    payment_method = models.CharField(max_length=50)

    payment_id = models.CharField(max_length=100, blank=True)

    transaction_fee = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

    net_amount = models.DecimalField(max_digits=10, decimal_places=2)

   

    description = models.TextField(blank=True)

    date_created = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        ordering = ['-date_created']

   

    def __str__(self):

        return f"{self.get_revenue_type_display()} - ${self.amount}"

   

    def save(self, *args, **kwargs):

        # Calculate net amount after transaction fees

        if not self.net_amount:

            self.net_amount = self.amount - self.transaction_fee

        super().save(*args, **kwargs)

 

 

Ads Statistic

class AdStatistics(models.Model):

    """Track ad performance for analytics"""

    ad = models.OneToOneField(ClassifiedAd, on_delete=models.CASCADE, related_name='statistics')

   

    # View statistics

    total_views = models.PositiveIntegerField(default=0)

    unique_views = models.PositiveIntegerField(default=0)

    views_today = models.PositiveIntegerField(default=0)

    views_this_week = models.PositiveIntegerField(default=0)

    views_this_month = models.PositiveIntegerField(default=0)

   

    # Contact statistics

    total_contacts = models.PositiveIntegerField(default=0)

    phone_clicks = models.PositiveIntegerField(default=0)

    email_clicks = models.PositiveIntegerField(default=0)

    inquiries_received = models.PositiveIntegerField(default=0)

   

    # Social sharing

    shares_count = models.PositiveIntegerField(default=0)

    favorites_count = models.PositiveIntegerField(default=0)

   

    # Revenue tracking

    revenue_generated = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

    promotion_spent = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

   

    last_updated = models.DateTimeField(auto_now=True)

   

    def __str__(self):

        return f"Stats for {self.ad.title}"

 

 

Ads View

class AdView(models.Model):

    """Track ad views"""

    ad = models.ForeignKey(ClassifiedAd, on_delete=models.CASCADE, related_name='ad_views')

    ip_address = models.GenericIPAddressField()

    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

    viewed_at = models.DateTimeField(auto_now_add=True)

   

    class Meta:

        unique_together = ['ad', 'ip_address']

        ordering = ['-viewed_at']

 

 

Attached Files

0 files found.

You are viewing this article in public mode. Some features may be limited.