""" Analytics models for Dubai Land Department data. """ from django.db import models from django.contrib.postgres.indexes import BTreeIndex # JSONField is now in django.db.models in Django 3.1+ from apps.core.models import TimeStampedModel import uuid class Broker(TimeStampedModel): """Real estate brokers data.""" broker_number = models.CharField(max_length=20, unique=True, db_index=True) broker_name_en = models.CharField(max_length=255) gender = models.CharField(max_length=10, choices=[ ('male', 'Male'), ('female', 'Female'), ('أنثى', 'Female (Arabic)'), ]) license_start_date = models.DateTimeField() license_end_date = models.DateTimeField() webpage = models.URLField(blank=True, null=True) phone = models.CharField(max_length=50, blank=True) fax = models.CharField(max_length=50, blank=True) real_estate_number = models.CharField(max_length=20, blank=True) real_estate_name_en = models.CharField(max_length=255, blank=True) class Meta: db_table = 'brokers' indexes = [ BTreeIndex(fields=['broker_number']), BTreeIndex(fields=['real_estate_name_en']), BTreeIndex(fields=['license_end_date']), ] def __str__(self): return f"{self.broker_name_en} ({self.broker_number})" class Developer(TimeStampedModel): """Real estate developers data.""" developer_number = models.CharField(max_length=20, unique=True, db_index=True) developer_name_en = models.CharField(max_length=255) class Meta: db_table = 'developers' def __str__(self): return f"{self.developer_name_en} ({self.developer_number})" class Project(TimeStampedModel): """Real estate projects data.""" project_number = models.CharField(max_length=20, unique=True, db_index=True) project_name_en = models.CharField(max_length=255) developer = models.ForeignKey(Developer, on_delete=models.CASCADE, related_name='projects') start_date = models.DateTimeField() end_date = models.DateTimeField(null=True, blank=True) adoption_date = models.DateTimeField(null=True, blank=True) project_type = models.CharField(max_length=50, choices=[ ('Normal', 'Normal'), ('Escrow', 'Escrow'), ]) project_value = models.DecimalField(max_digits=15, decimal_places=2, null=True, blank=True) escrow_account_number = models.CharField(max_length=50, blank=True) project_status = models.CharField(max_length=20, choices=[ ('ACTIVE', 'Active'), ('PENDING', 'Pending'), ('CANCELLED', 'Cancelled'), ('COMPLETED', 'Completed'), ]) percent_completed = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) inspection_date = models.DateTimeField(null=True, blank=True) completion_date = models.DateTimeField(null=True, blank=True) description_en = models.TextField(blank=True) area_en = models.CharField(max_length=100) zone_en = models.CharField(max_length=100) count_land = models.IntegerField(default=0) count_building = models.IntegerField(default=0) count_villa = models.IntegerField(default=0) count_unit = models.IntegerField(default=0) master_project_en = models.CharField(max_length=255, blank=True) class Meta: db_table = 'projects' indexes = [ BTreeIndex(fields=['project_number']), BTreeIndex(fields=['project_status']), BTreeIndex(fields=['area_en']), BTreeIndex(fields=['zone_en']), BTreeIndex(fields=['developer']), ] def __str__(self): return f"{self.project_name_en} ({self.project_number})" class Land(TimeStampedModel): """Land data.""" land_type = models.CharField(max_length=50, choices=[ ('Agricultural', 'Agricultural'), ('Commercial', 'Commercial'), ('Residential', 'Residential'), ('Industrial', 'Industrial'), ]) property_sub_type = models.CharField(max_length=50) actual_area = models.DecimalField(max_digits=10, decimal_places=2) is_offplan = models.CharField(max_length=10, choices=[ ('Ready', 'Ready'), ('Off-Plan', 'Off-Plan'), ]) pre_registration_number = models.CharField(max_length=50, blank=True) is_freehold = models.CharField(max_length=20, choices=[ ('Free Hold', 'Free Hold'), ('Non Free Hold', 'Non Free Hold'), ]) dm_zip_code = models.CharField(max_length=10, blank=True) master_project = models.CharField(max_length=255, blank=True) project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True, related_name='lands') area_en = models.CharField(max_length=100) zone_en = models.CharField(max_length=100) class Meta: db_table = 'lands' indexes = [ BTreeIndex(fields=['land_type']), BTreeIndex(fields=['area_en']), BTreeIndex(fields=['zone_en']), BTreeIndex(fields=['is_freehold']), BTreeIndex(fields=['actual_area']), ] def __str__(self): return f"{self.area_en} - {self.land_type} ({self.actual_area} sqft)" class Transaction(TimeStampedModel): """Real estate transactions data.""" transaction_number = models.CharField(max_length=50, unique=True, db_index=True) instance_date = models.DateTimeField() group = models.CharField(max_length=50, choices=[ ('Mortgage', 'Mortgage'), ('Sale', 'Sale'), ('Rent', 'Rent'), ('Other', 'Other'), ]) procedure = models.CharField(max_length=100) is_offplan = models.CharField(max_length=10, choices=[ ('Off-Plan', 'Off-Plan'), ('Ready', 'Ready'), ]) is_freehold = models.CharField(max_length=20, choices=[ ('Free Hold', 'Free Hold'), ('Non Free Hold', 'Non Free Hold'), ]) usage = models.CharField(max_length=50, choices=[ ('Residential', 'Residential'), ('Commercial', 'Commercial'), ('Industrial', 'Industrial'), ('Mixed', 'Mixed'), ]) area_en = models.CharField(max_length=100) property_type = models.CharField(max_length=50, choices=[ ('Unit', 'Unit'), ('Villa', 'Villa'), ('Land', 'Land'), ('Building', 'Building'), ]) property_sub_type = models.CharField(max_length=50) transaction_value = models.DecimalField(max_digits=15, decimal_places=2) procedure_area = models.DecimalField(max_digits=10, decimal_places=2) actual_area = models.DecimalField(max_digits=10, decimal_places=2) rooms = models.CharField(max_length=20, blank=True) parking = models.CharField(max_length=20, blank=True) nearest_metro = models.CharField(max_length=100, blank=True) nearest_mall = models.CharField(max_length=100, blank=True) nearest_landmark = models.CharField(max_length=100, blank=True) total_buyer = models.IntegerField(default=0) total_seller = models.IntegerField(default=0) master_project = models.CharField(max_length=255, blank=True) project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True, related_name='transactions') class Meta: db_table = 'transactions' indexes = [ BTreeIndex(fields=['transaction_number']), BTreeIndex(fields=['instance_date']), BTreeIndex(fields=['area_en']), BTreeIndex(fields=['property_type']), BTreeIndex(fields=['transaction_value']), BTreeIndex(fields=['group']), BTreeIndex(fields=['usage']), ] def __str__(self): return f"{self.transaction_number} - {self.area_en} - {self.transaction_value}" @property def price_per_sqft(self): """Calculate price per square foot.""" if self.actual_area and self.actual_area > 0: return self.transaction_value / self.actual_area return None class Valuation(TimeStampedModel): """Property valuations data.""" property_total_value = models.DecimalField(max_digits=15, decimal_places=2) area_en = models.CharField(max_length=100) actual_area = models.DecimalField(max_digits=10, decimal_places=2) procedure_year = models.IntegerField() procedure_number = models.CharField(max_length=20) instance_date = models.DateTimeField() actual_worth = models.DecimalField(max_digits=15, decimal_places=2) procedure_area = models.DecimalField(max_digits=10, decimal_places=2) property_type = models.CharField(max_length=50, choices=[ ('Unit', 'Unit'), ('Villa', 'Villa'), ('Land', 'Land'), ('Building', 'Building'), ]) property_sub_type = models.CharField(max_length=50) class Meta: db_table = 'valuations' indexes = [ BTreeIndex(fields=['area_en']), BTreeIndex(fields=['property_type']), BTreeIndex(fields=['procedure_year']), BTreeIndex(fields=['instance_date']), BTreeIndex(fields=['property_total_value']), ] def __str__(self): return f"{self.area_en} - {self.property_type} - {self.property_total_value}" class Rent(TimeStampedModel): """Rental data.""" registration_date = models.DateTimeField() start_date = models.DateTimeField() end_date = models.DateTimeField() version = models.CharField(max_length=20, choices=[ ('New', 'New'), ('Renewed', 'Renewed'), ]) area_en = models.CharField(max_length=100) contract_amount = models.DecimalField(max_digits=15, decimal_places=2) annual_amount = models.DecimalField(max_digits=15, decimal_places=2) is_freehold = models.CharField(max_length=20, choices=[ ('Free Hold', 'Free Hold'), ('Non Free Hold', 'Non Free Hold'), ]) actual_area = models.DecimalField(max_digits=10, decimal_places=2) property_type = models.CharField(max_length=50, choices=[ ('Unit', 'Unit'), ('Villa', 'Villa'), ('Land', 'Land'), ('Building', 'Building'), ]) property_sub_type = models.CharField(max_length=50) rooms = models.CharField(max_length=20, blank=True) usage = models.CharField(max_length=50, choices=[ ('Residential', 'Residential'), ('Commercial', 'Commercial'), ('Industrial', 'Industrial'), ('Mixed', 'Mixed'), ]) nearest_metro = models.CharField(max_length=100, blank=True) nearest_mall = models.CharField(max_length=100, blank=True) nearest_landmark = models.CharField(max_length=100, blank=True) parking = models.CharField(max_length=20, blank=True) total_properties = models.IntegerField(default=1) master_project = models.CharField(max_length=255, blank=True) project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True, related_name='rents') class Meta: db_table = 'rents' indexes = [ BTreeIndex(fields=['area_en']), BTreeIndex(fields=['property_type']), BTreeIndex(fields=['registration_date']), BTreeIndex(fields=['start_date']), BTreeIndex(fields=['end_date']), BTreeIndex(fields=['contract_amount']), BTreeIndex(fields=['annual_amount']), ] def __str__(self): return f"{self.area_en} - {self.property_type} - {self.annual_amount}" @property def rent_per_sqft(self): """Calculate rent per square foot.""" if self.actual_area and self.actual_area > 0: return self.annual_amount / self.actual_area return None class Forecast(TimeStampedModel): """Property price forecasts.""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) area_en = models.CharField(max_length=100) property_type = models.CharField(max_length=50) property_sub_type = models.CharField(max_length=50, blank=True) forecast_date = models.DateTimeField() predicted_price = models.DecimalField(max_digits=15, decimal_places=2) confidence_interval_lower = models.DecimalField(max_digits=15, decimal_places=2) confidence_interval_upper = models.DecimalField(max_digits=15, decimal_places=2) model_version = models.CharField(max_length=20, default='1.0') accuracy_score = models.DecimalField(max_digits=5, decimal_places=4, null=True, blank=True) metadata = models.JSONField(default=dict) class Meta: db_table = 'forecasts' indexes = [ BTreeIndex(fields=['area_en']), BTreeIndex(fields=['property_type']), BTreeIndex(fields=['forecast_date']), BTreeIndex(fields=['predicted_price']), ] def __str__(self): return f"{self.area_en} - {self.property_type} - {self.forecast_date}" class MarketTrend(TimeStampedModel): """Market trend analysis data.""" area_en = models.CharField(max_length=100) property_type = models.CharField(max_length=50) period_start = models.DateTimeField() period_end = models.DateTimeField() average_price = models.DecimalField(max_digits=15, decimal_places=2) median_price = models.DecimalField(max_digits=15, decimal_places=2) price_change_percent = models.DecimalField(max_digits=5, decimal_places=2) transaction_count = models.IntegerField() volume = models.DecimalField(max_digits=15, decimal_places=2) price_per_sqft = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) class Meta: db_table = 'market_trends' indexes = [ BTreeIndex(fields=['area_en']), BTreeIndex(fields=['property_type']), BTreeIndex(fields=['period_start']), BTreeIndex(fields=['period_end']), ] def __str__(self): return f"{self.area_en} - {self.property_type} - {self.period_start} to {self.period_end}"