Browse Source

feat(ads): implement serializers, views, and routing for Ads and Evaluations

master
PouyaKhajavi 2 days ago
parent
commit
12c6cefb22
  1. 74
      backend/ads/migrations/0001_initial.py
  2. 51
      backend/ads/serializers.py
  3. 11
      backend/ads/urls.py
  4. 27
      backend/ads/views.py

74
backend/ads/migrations/0001_initial.py

@ -0,0 +1,74 @@
# Generated by Django 6.0.8 on 2026-08-08 07:53
import django.db.models.deletion
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('crawler', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Ad',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('divar_token', models.CharField(max_length=50, unique=True)),
('title', models.CharField(max_length=255)),
('description', models.TextField()),
('price', models.CharField(blank=True, max_length=100, null=True)),
('category', models.CharField(blank=True, max_length=100, null=True)),
('images', models.JSONField(default=list)),
('url', models.URLField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('published_at', models.DateTimeField(blank=True, null=True)),
],
options={
'indexes': [models.Index(fields=['created_at'], name='ads_ad_created_9f5b83_idx')],
},
),
migrations.CreateModel(
name='AdEvaluation',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('is_flagged', models.BooleanField(default=False)),
('reason', models.TextField(blank=True, null=True)),
('confidence', models.FloatField(blank=True, null=True)),
('extracted_fields', models.JSONField(default=dict)),
('evaluated_at', models.DateTimeField(auto_now_add=True)),
('ad', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='evaluations', to='ads.ad')),
('crawl_task', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='evaluations', to='crawler.crawltask')),
],
),
migrations.CreateModel(
name='NotificationLog',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('channel_id', models.CharField(max_length=100)),
('sent_at', models.DateTimeField(auto_now_add=True)),
('status', models.CharField(choices=[('SENT', 'Sent'), ('FAILED', 'Failed')], max_length=20)),
('error_message', models.TextField(blank=True, null=True)),
('evaluation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to='ads.adevaluation')),
],
options={
'abstract': False,
},
),
migrations.AddIndex(
model_name='adevaluation',
index=models.Index(fields=['is_flagged'], name='ads_adevalu_is_flag_1e9b55_idx'),
),
migrations.AddIndex(
model_name='adevaluation',
index=models.Index(fields=['evaluated_at'], name='ads_adevalu_evaluat_9e55c2_idx'),
),
migrations.AddConstraint(
model_name='adevaluation',
constraint=models.UniqueConstraint(fields=('crawl_task', 'ad'), name='unique_crawl_task_ad'),
),
]

51
backend/ads/serializers.py

@ -0,0 +1,51 @@
from rest_framework import serializers
from .models import Ad, AdEvaluation, NotificationLog
class AdSerializer(serializers.ModelSerializer):
class Meta:
model = Ad
fields = [
'id',
'divar_token',
'title',
'description',
'price',
'category',
'images',
'url',
'created_at',
'published_at',
]
read_only_fields = ['id', 'created_at']
class AdEvaluationSerializer(serializers.ModelSerializer):
ad = AdSerializer(read_only=True)
class Meta:
model = AdEvaluation
fields = [
'id',
'crawl_task',
'ad',
'is_flagged',
'reason',
'confidence',
'extracted_fields',
'evaluated_at',
]
read_only_fields = ['id', 'evaluated_at']
class NotificationLogSerializer(serializers.ModelSerializer):
class Meta:
model = NotificationLog
fields = [
'id',
'evaluation',
'channel_id',
'sent_at',
'status',
'error_message',
]
read_only_fields = ['id', 'sent_at']

11
backend/ads/urls.py

@ -1,6 +1,11 @@
from django.urls import path
from .views import AdsView
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import AdEvaluationViewSet
router = DefaultRouter()
router.register(r'', AdEvaluationViewSet, basename='ads')
urlpatterns = [
path('', AdsView.as_view()),
path('', include(router.urls)),
]

27
backend/ads/views.py

@ -1,10 +1,23 @@
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import viewsets
from .models import AdEvaluation
from .serializers import AdEvaluationSerializer
# Create your views here.
class AdEvaluationViewSet(viewsets.ReadOnlyModelViewSet):
"""
ViewSet for listing and viewing Ad Evaluations.
Supports filtering by crawl_task (UUID) and is_flagged (boolean).
"""
serializer_class = AdEvaluationSerializer
class AdsView(APIView):
def get(self, request):
return Response({'message': 'Hello from Ads'})
def get_queryset(self):
queryset = AdEvaluation.objects.all().select_related('ad', 'crawl_task').order_by('-evaluated_at')
crawl_task_id = self.request.query_params.get('crawl_task')
is_flagged = self.request.query_params.get('is_flagged')
if crawl_task_id:
queryset = queryset.filter(crawl_task_id=crawl_task_id)
if is_flagged is not None:
is_flagged_bool = is_flagged.lower() in ('true', '1', 't')
queryset = queryset.filter(is_flagged=is_flagged_bool)
return queryset
Loading…
Cancel
Save