| import uuid
|
| import builtins
|
| from django.db import models
|
| from pgvector.django import VectorField
|
| from django.conf import settings
|
|
|
|
|
| class GlobalQA(models.Model):
|
| """Global Q&A for greetings - works across ALL properties and agencies"""
|
| id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
| question = models.TextField(unique=True)
|
| answer = models.TextField()
|
| question_embedding = VectorField(dimensions=384)
|
| language = models.CharField(max_length=20, choices=[
|
| ('en', 'English'),
|
| ('ur', 'Roman Urdu'),
|
| ('both', 'Both')
|
| ], default='both')
|
| is_active = models.BooleanField(default=True)
|
| priority = models.IntegerField(default=0)
|
| created_at = models.DateTimeField(auto_now_add=True)
|
|
|
| class Meta:
|
| ordering = ['-priority', 'question']
|
| indexes = [
|
| models.Index(fields=['is_active', '-priority']),
|
| models.Index(fields=['language', 'is_active']),
|
| ]
|
|
|
| def __str__(self):
|
| return f"{self.question[:50]}"
|
|
|
|
|
| class MasterQuestion(models.Model):
|
| """Pre-defined questions created by admin that agencies must answer"""
|
| id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
| question = models.TextField(unique=True)
|
| is_active = models.BooleanField(default=True)
|
| order = models.IntegerField(default=0)
|
| created_at = models.DateTimeField(auto_now_add=True)
|
|
|
| class Meta:
|
| ordering = ['order']
|
| indexes = [
|
| models.Index(fields=['is_active', 'order']),
|
| ]
|
|
|
| def __str__(self):
|
| return self.question[:100]
|
|
|
|
|
| class PropertyQA(models.Model):
|
| """Agency's answers to master questions for a specific property"""
|
| id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
|
| agency = models.ForeignKey(
|
| settings.AUTH_USER_MODEL,
|
| on_delete=models.CASCADE,
|
| related_name='property_qa_answers'
|
| )
|
| property = models.ForeignKey(
|
| 'Property.Property',
|
| on_delete=models.CASCADE,
|
| related_name='ai_qa'
|
| )
|
| master_question = models.ForeignKey(
|
| MasterQuestion,
|
| on_delete=models.CASCADE,
|
| null=True,
|
| blank=True
|
| )
|
| answer = models.TextField()
|
|
|
|
|
| question_embedding = VectorField(dimensions=384)
|
|
|
| is_active = models.BooleanField(default=True)
|
| created_at = models.DateTimeField(auto_now_add=True)
|
| updated_at = models.DateTimeField(auto_now=True)
|
|
|
| class Meta:
|
| unique_together = ['property', 'master_question']
|
|
|
| indexes = [
|
| models.Index(fields=['property', 'is_active']),
|
| models.Index(fields=['agency', 'is_active']),
|
| models.Index(fields=['master_question', 'is_active']),
|
|
|
| ]
|
|
|
| @builtins.property
|
| def question_text(self):
|
| return self.master_question.question if self.master_question else None
|
|
|
| def __str__(self):
|
| return f"{self.property.title}: {self.question_text[:50] if self.question_text else 'No question'}"
|
|
|
|
|
| class AgencyAutoChatSetting(models.Model):
|
| """Auto-chat settings per agency"""
|
| agency = models.OneToOneField(
|
| settings.AUTH_USER_MODEL,
|
| on_delete=models.CASCADE,
|
| related_name='auto_chat_setting'
|
| )
|
| is_enabled = models.BooleanField(default=False)
|
| delay_seconds = models.IntegerField(default=30, help_text="Seconds to wait before AI replies")
|
| confidence_threshold = models.FloatField(default=0.6, help_text="Minimum similarity score (0-1)")
|
| created_at = models.DateTimeField(auto_now_add=True)
|
| updated_at = models.DateTimeField(auto_now=True)
|
|
|
| class Meta:
|
| indexes = [
|
| models.Index(fields=['is_enabled']),
|
| ]
|
|
|
| def __str__(self):
|
| return f"{self.agency.email}: {'ON' if self.is_enabled else 'OFF'}"
|
|
|
|
|
| class PropertyAutoChatState(models.Model):
|
| """Track auto-chat state per property"""
|
| property = models.OneToOneField(
|
| 'Property.Property',
|
| on_delete=models.CASCADE,
|
| related_name='auto_chat_state'
|
| )
|
| is_auto_chat_enabled = models.BooleanField(default=False)
|
| last_auto_reply_at = models.DateTimeField(null=True, blank=True)
|
| total_auto_replies = models.IntegerField(default=0)
|
| last_agency_reply_at = models.DateTimeField(null=True, blank=True)
|
|
|
| class Meta:
|
| indexes = [
|
| models.Index(fields=['is_auto_chat_enabled']),
|
| models.Index(fields=['property', 'is_auto_chat_enabled']),
|
| ]
|
|
|
| def __str__(self):
|
| return f"{self.property.title}: Auto Chat {'ON' if self.is_auto_chat_enabled else 'OFF'}"
|
|
|
|
|
| class PropertyCustomQA(models.Model):
|
| """
|
| Custom Q&A for a specific property (max 5 per property)
|
| Agency writes these when enabling auto-chat
|
| """
|
| id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
|
|
|
| agency = models.ForeignKey(
|
| settings.AUTH_USER_MODEL,
|
| on_delete=models.CASCADE,
|
| related_name='property_custom_qa'
|
| )
|
| property = models.ForeignKey(
|
| 'Property.Property',
|
| on_delete=models.CASCADE,
|
| related_name='custom_qa'
|
| )
|
|
|
|
|
| question = models.TextField()
|
| answer = models.TextField()
|
|
|
|
|
| question_embedding = VectorField(dimensions=384)
|
|
|
|
|
| order = models.IntegerField(default=0)
|
| is_active = models.BooleanField(default=True)
|
| created_at = models.DateTimeField(auto_now_add=True)
|
| updated_at = models.DateTimeField(auto_now=True)
|
|
|
| class Meta:
|
| ordering = ['order']
|
|
|
| constraints = [
|
| models.UniqueConstraint(
|
| fields=['property', 'order'],
|
| name='unique_order_per_property'
|
| )
|
| ]
|
| indexes = [
|
| models.Index(fields=['property', 'is_active']),
|
| models.Index(fields=['agency', 'is_active']),
|
| ]
|
|
|
| def __str__(self):
|
| return f"{self.property.title}: Q{self.order} - {self.question[:50]}" |