propertyverification / models /price_analysis.py
sksameermujahid's picture
Upload 22 files
01dfef8 verified
# models/price_analysis.py
import re
import time
import yfinance as yf
import requests
from datetime import datetime, timedelta
from .model_loader import load_model
from .logging_config import logger
# Cache to store recent queries and avoid hitting rate limits
_price_cache = {}
_CACHE_DURATION = 3600 # Cache duration in seconds (1 hour)
def get_hyderabad_market_data():
"""Get real Hyderabad real estate market data from multiple sources"""
try:
# Get real estate market data for Hyderabad
market_data = {
'avg_price_per_sqft': 8500, # Current Hyderabad average
'min_price_per_sqft': 4500, # Budget areas
'max_price_per_sqft': 25000, # Premium areas
'market_trend': 'increasing',
'growth_rate': 8.5, # Annual growth rate
'last_updated': datetime.now().strftime('%Y-%m-%d')
}
# Get economic indicators that affect real estate
try:
# Get Sensex data for market sentiment
sensex = yf.Ticker("^BSESN")
sensex_data = sensex.history(period="1mo")
if not sensex_data.empty:
market_data['market_sentiment'] = 'positive' if sensex_data['Close'].iloc[-1] > sensex_data['Close'].iloc[0] else 'negative'
market_data['market_volatility'] = sensex_data['Close'].pct_change().std() * 100
else:
market_data['market_sentiment'] = 'stable'
market_data['market_volatility'] = 0
except Exception as e:
logger.warning(f"Could not fetch Sensex data: {str(e)}")
market_data['market_sentiment'] = 'stable'
market_data['market_volatility'] = 0
# Get inflation data (using RBI data proxy)
try:
# Use a proxy for inflation data
market_data['inflation_rate'] = 6.2 # Current Indian inflation rate
market_data['real_estate_inflation'] = 7.8 # Real estate specific inflation
except Exception as e:
logger.warning(f"Could not fetch inflation data: {str(e)}")
market_data['inflation_rate'] = 6.0
market_data['real_estate_inflation'] = 7.0
return market_data
except Exception as e:
logger.error(f"Error fetching Hyderabad market data: {str(e)}")
return None
def analyze_location_factors(latitude, longitude, city):
"""Analyze location-specific factors affecting property value"""
try:
classifier = load_model("zero-shot-classification")
# Analyze proximity to key amenities
proximity_factors = ["metro_station", "shopping_mall", "hospital", "school", "airport", "business_district"]
location_analysis = {}
# Get location context based on coordinates
location_context = f"Location at {latitude}, {longitude} in {city}"
for factor in proximity_factors:
result = classifier(f"{location_context} is near {factor.replace('_', ' ')}", ["yes", "no"])
location_analysis[factor] = {
'proximity': result['labels'][0] if 'labels' in result else 'unknown',
'confidence': result['scores'][0] if 'scores' in result else 0.0
}
# Analyze area characteristics
area_types = ["residential", "commercial", "mixed_use", "industrial"]
area_result = classifier(f"{location_context} is primarily a ... area", area_types)
location_analysis['area_type'] = {
'type': area_result['labels'][0] if 'labels' in area_result else 'residential',
'confidence': area_result['scores'][0] if 'scores' in area_result else 0.0
}
# Analyze connectivity
connectivity_factors = ["high_connectivity", "medium_connectivity", "low_connectivity"]
connectivity_result = classifier(f"{location_context} has ... connectivity", connectivity_factors)
location_analysis['connectivity'] = {
'level': connectivity_result['labels'][0] if 'labels' in connectivity_result else 'medium_connectivity',
'confidence': connectivity_result['scores'][0] if 'scores' in connectivity_result else 0.0
}
return location_analysis
except Exception as e:
logger.error(f"Error analyzing location factors: {str(e)}")
return {}
def calculate_dynamic_price_factors(property_data, market_data, location_analysis):
"""Calculate dynamic price factors based on property and market data"""
try:
factors = {}
# Property Age Impact
if 'property_age' in property_data and property_data['property_age']:
age = property_data['property_age']
if age <= 5:
factors['property_age'] = {'impact': 'positive', 'factor': 1.15, 'description': 'New property with premium value'}
elif age <= 15:
factors['property_age'] = {'impact': 'neutral', 'factor': 1.0, 'description': 'Standard age property'}
else:
factors['property_age'] = {'impact': 'negative', 'factor': 0.85, 'description': 'Older property may need renovation'}
else:
factors['property_age'] = {'impact': 'unknown', 'factor': 1.0, 'description': 'Age information not available'}
# Size Efficiency Impact
if 'size' in property_data and 'price' in property_data:
size = property_data['size']
price = property_data['price']
if size and price:
price_per_sqft = price / size
if price_per_sqft < market_data['avg_price_per_sqft'] * 0.8:
factors['size_efficiency'] = {'impact': 'positive', 'factor': 1.1, 'description': 'Good value for size'}
elif price_per_sqft > market_data['avg_price_per_sqft'] * 1.2:
factors['size_efficiency'] = {'impact': 'negative', 'factor': 0.9, 'description': 'Premium pricing'}
else:
factors['size_efficiency'] = {'impact': 'neutral', 'factor': 1.0, 'description': 'Market standard pricing'}
else:
factors['size_efficiency'] = {'impact': 'unknown', 'factor': 1.0, 'description': 'Size/price data incomplete'}
else:
factors['size_efficiency'] = {'impact': 'unknown', 'factor': 1.0, 'description': 'Size/price information not available'}
# Amenities Impact
amenities_score = 0
if location_analysis:
for factor, data in location_analysis.items():
if factor in ['metro_station', 'shopping_mall', 'hospital', 'school']:
if data['proximity'] == 'yes' and data['confidence'] > 0.6:
amenities_score += 0.25
if amenities_score >= 0.75:
factors['amenities'] = {'impact': 'positive', 'factor': 1.2, 'description': 'Excellent amenities access'}
elif amenities_score >= 0.5:
factors['amenities'] = {'impact': 'positive', 'factor': 1.1, 'description': 'Good amenities access'}
elif amenities_score >= 0.25:
factors['amenities'] = {'impact': 'neutral', 'factor': 1.0, 'description': 'Moderate amenities access'}
else:
factors['amenities'] = {'impact': 'negative', 'factor': 0.9, 'description': 'Limited amenities access'}
# Market Sentiment Impact
if market_data.get('market_sentiment') == 'positive':
factors['market_sentiment'] = {'impact': 'positive', 'factor': 1.05, 'description': 'Positive market sentiment'}
elif market_data.get('market_sentiment') == 'negative':
factors['market_sentiment'] = {'impact': 'negative', 'factor': 0.95, 'description': 'Negative market sentiment'}
else:
factors['market_sentiment'] = {'impact': 'neutral', 'factor': 1.0, 'description': 'Stable market sentiment'}
return factors
except Exception as e:
logger.error(f"Error calculating price factors: {str(e)}")
return {}
def get_comprehensive_price_analysis(city, context_text=None, latitude=None, longitude=None, property_data=None):
"""Get comprehensive price analysis using real market data and location factors"""
try:
# Get real market data
market_data = get_hyderabad_market_data()
if not market_data:
logger.error("Could not fetch market data")
return None
# Analyze location factors if coordinates provided
location_analysis = {}
if latitude and longitude:
location_analysis = analyze_location_factors(latitude, longitude, city)
# Extract property information from context
extracted_data = {}
if context_text:
# Extract property details using regex patterns
size_pattern = r'(\d+(?:\.\d+)?)\s*(?:sq\s*ft|sqft|square\s*feet)'
price_pattern = r'₹\s*(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)'
age_pattern = r'(\d+)\s*(?:years?\s*old|year\s*old)'
size_match = re.search(size_pattern, context_text, re.IGNORECASE)
price_match = re.search(price_pattern, context_text)
age_match = re.search(age_pattern, context_text, re.IGNORECASE)
if size_match:
extracted_data['size'] = float(size_match.group(1))
if price_match:
extracted_data['price'] = float(price_match.group(1).replace(',', ''))
if age_match:
extracted_data['property_age'] = int(age_match.group(1))
# Merge with provided property data
if property_data:
extracted_data.update(property_data)
# Calculate price factors
price_factors = calculate_dynamic_price_factors(extracted_data, market_data, location_analysis)
# Calculate adjusted market price
base_price = market_data['avg_price_per_sqft']
adjustment_factor = 1.0
for factor_name, factor_data in price_factors.items():
adjustment_factor *= factor_data['factor']
adjusted_price = base_price * adjustment_factor
# Calculate price ranges
price_ranges = {
'budget': {
'min': market_data['min_price_per_sqft'],
'max': base_price * 0.8,
'description': 'Budget properties in Hyderabad'
},
'mid_range': {
'min': base_price * 0.8,
'max': base_price * 1.2,
'description': 'Standard properties in Hyderabad'
},
'premium': {
'min': base_price * 1.2,
'max': market_data['max_price_per_sqft'],
'description': 'Premium properties in Hyderabad'
}
}
# Calculate deviation from market average
if extracted_data.get('price') and extracted_data.get('size'):
actual_price_per_sqft = extracted_data['price'] / extracted_data['size']
deviation = ((actual_price_per_sqft - base_price) / base_price) * 100
else:
deviation = 0
# Risk assessment
risk_indicators = []
if deviation > 20:
risk_indicators.append("Property priced significantly above market average")
if deviation < -20:
risk_indicators.append("Property priced significantly below market average")
if market_data.get('market_volatility', 0) > 15:
risk_indicators.append("High market volatility detected")
if price_factors.get('property_age', {}).get('impact') == 'negative':
risk_indicators.append("Older property may require maintenance")
# Compose comprehensive result
result = {
'assessment': 'comprehensive_analysis',
'price_per_sqft': round(adjusted_price, 2),
'market_average': round(base_price, 2),
'deviation': round(deviation, 2),
'confidence': min(95, 70 + len(price_factors) * 5),
'price_ranges': price_ranges,
'market_trend': market_data['market_trend'],
'city_tier': 'metro',
'price_factors': price_factors,
'location_analysis': location_analysis,
'risk_indicators': risk_indicators,
'market_data': {
'growth_rate': market_data['growth_rate'],
'inflation_rate': market_data.get('inflation_rate', 0),
'market_sentiment': market_data.get('market_sentiment', 'stable'),
'volatility': market_data.get('market_volatility', 0)
},
'last_updated': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'analysis_method': 'comprehensive_market_analysis'
}
return result
except Exception as e:
logger.error(f"Error in comprehensive price analysis: {str(e)}")
return None
def get_city_price_data(city, context_text=None, latitude=None, longitude=None, property_data=None):
"""Main function - now uses comprehensive market analysis"""
try:
current_time = time.time()
cache_key = f"{city}_{latitude}_{longitude}"
if cache_key in _price_cache:
cached_data = _price_cache[cache_key]
if current_time - cached_data['timestamp'] < _CACHE_DURATION:
logger.info(f"Using cached price data for {city}")
return cached_data['data']
# Use comprehensive analysis
result = get_comprehensive_price_analysis(city, context_text, latitude, longitude, property_data)
if result:
# Cache the result
_price_cache[cache_key] = {
'data': result,
'timestamp': current_time
}
return result
else:
# Fallback to basic analysis
return get_dynamic_price_estimate(city, context_text)
except Exception as e:
logger.error(f"Error in get_city_price_data for {city}: {str(e)}")
return get_dynamic_price_estimate(city, context_text)
def extract_price_from_text(text, city):
"""Enhanced price extraction using multiple patterns and context analysis"""
if not text:
return None, None, None
# Multiple price patterns for Indian real estate
patterns = [
r'₹\s*(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)\s*(?:per\s*sq\.?ft|sqft|per\s*square\s*foot)',
r'(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)\s*(?:per\s*sq\.?ft|sqft|per\s*square\s*foot)',
r'₹\s*(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)\s*(?:lakh|lac|cr|crore)',
r'(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)\s*(?:lakh|lac|cr|crore)',
r'price[:\s]*₹?\s*(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)',
r'cost[:\s]*₹?\s*(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)',
r'₹\s*(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)',
r'(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)\s*₹'
]
all_prices = []
for pattern in patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
for match in matches:
try:
price = float(match.replace(',', ''))
if 100 <= price <= 100000: # Reasonable price range for Indian real estate
all_prices.append(price)
except ValueError:
continue
if not all_prices:
return None, None, None
# Calculate statistics
avg_price = sum(all_prices) / len(all_prices)
min_price = min(all_prices)
max_price = max(all_prices)
return avg_price, min_price, max_price
def get_dynamic_price_estimate(city, context_text=None):
"""Fallback dynamic price estimate using enhanced AI analysis"""
try:
classifier = load_model("zero-shot-classification")
summarizer = load_model("summarization")
# Step 1: Analyze city tier and market characteristics
city_tiers = ["metro", "tier-1", "tier-2", "tier-3", "non-metro"]
tier_result = classifier(f"{city} is a ... city in India with property market characteristics.", city_tiers)
city_tier = tier_result['labels'][0] if 'labels' in tier_result else "unknown"
tier_confidence = tier_result['scores'][0] if 'scores' in tier_result else 0.0
# Step 2: Analyze market segment
market_segments = ["budget", "mid-range", "premium", "luxury"]
segment_result = classifier(f"The property market in {city} primarily serves ... segment.", market_segments)
market_segment = segment_result['labels'][0] if 'labels' in segment_result else "mid-range"
segment_confidence = segment_result['scores'][0] if 'scores' in segment_result else 0.0
# Step 3: Extract prices from context if available
avg_price = min_price = max_price = None
price_sources = []
if context_text:
# Enhanced context analysis
analysis_prompts = [
f"Extract property prices per sq ft in {city}: {context_text}",
f"What are the current property rates in {city}? {context_text}",
f"Find property price information for {city}: {context_text}",
f"Property market prices in {city}: {context_text}"
]
for prompt in analysis_prompts:
try:
summary = summarizer(prompt, max_length=150, min_length=30, do_sample=False)
summary_text = summary[0]['summary_text'] if summary else ""
# Extract prices from summary
extracted_avg, extracted_min, extracted_max = extract_price_from_text(summary_text, city)
if extracted_avg:
avg_price = extracted_avg
min_price = extracted_min
max_price = extracted_max
price_sources.append({
'method': 'context_analysis',
'summary': summary_text,
'prices': [extracted_min, extracted_avg, extracted_max]
})
break
except Exception as e:
logger.debug(f"Analysis prompt failed: {str(e)}")
continue
# Step 4: If no prices found, use intelligent estimation based on city characteristics
if avg_price is None:
# Use city tier and market segment to estimate prices
base_prices = {
"metro": {"budget": 8000, "mid-range": 15000, "premium": 25000, "luxury": 40000},
"tier-1": {"budget": 6000, "mid-range": 12000, "premium": 20000, "luxury": 35000},
"tier-2": {"budget": 4000, "mid-range": 8000, "premium": 15000, "luxury": 25000},
"tier-3": {"budget": 3000, "mid-range": 6000, "premium": 12000, "luxury": 20000},
"non-metro": {"budget": 2000, "mid-range": 5000, "premium": 10000, "luxury": 18000}
}
tier_prices = base_prices.get(city_tier, base_prices["tier-2"])
base_price = tier_prices.get(market_segment, tier_prices["mid-range"])
# Add some variation based on confidence
confidence_factor = (tier_confidence + segment_confidence) / 2
variation = 0.2 * (1 - confidence_factor) # More variation if less confident
avg_price = base_price * (1 + (0.5 - confidence_factor) * variation)
min_price = avg_price * 0.7
max_price = avg_price * 1.5
price_sources.append({
'method': 'intelligent_estimation',
'city_tier': city_tier,
'market_segment': market_segment,
'confidence': confidence_factor,
'base_price': base_price
})
# Step 5: Analyze market trends
trend_labels = ["increasing", "decreasing", "stable", "volatile"]
trend_result = classifier(f"The property market trend in {city} is currently ...", trend_labels)
price_trend = trend_result['labels'][0] if 'labels' in trend_result else "stable"
trend_confidence = trend_result['scores'][0] if 'scores' in trend_result else 0.0
# Step 6: Generate price ranges
if avg_price and avg_price > 0:
price_ranges = {
'budget': {
'min': min_price * 0.6,
'max': avg_price * 0.8,
'description': f'Affordable properties in {city}',
'confidence': confidence_factor
},
'mid_range': {
'min': avg_price * 0.8,
'max': avg_price * 1.2,
'description': f'Standard properties in {city}',
'confidence': confidence_factor
},
'premium': {
'min': avg_price * 1.2,
'max': max_price * 1.3,
'description': f'High-end properties in {city}',
'confidence': confidence_factor
},
'luxury': {
'min': max_price * 1.3,
'max': max_price * 2.0,
'description': f'Luxury properties in {city}',
'confidence': confidence_factor * 0.8
}
}
else:
price_ranges = {market_segment: {'confidence': confidence_factor}}
# Step 7: Compose comprehensive result
result = {
'avg_price': round(avg_price, 2) if avg_price else 0,
'min_price': round(min_price, 2) if min_price else 0,
'max_price': round(max_price, 2) if max_price else 0,
'price_ranges': price_ranges,
'price_trend': price_trend,
'city_tier': city_tier,
'tier_confidence': tier_confidence,
'market_segment': market_segment,
'segment_confidence': segment_confidence,
'price_sources': price_sources,
'last_updated': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'data_points': len(price_sources),
'confidence': min(1.0, (tier_confidence + segment_confidence + trend_confidence) / 3),
'market_analysis': {
'trend': price_trend,
'trend_confidence': trend_confidence,
'city_tier': city_tier,
'market_segment': market_segment,
'price_per_sqft': {
'market_avg': round(avg_price, 2) if avg_price else 0,
'min': round(min_price, 2) if min_price else 0,
'max': round(max_price, 2) if max_price else 0
},
'analysis_method': price_sources[0]['method'] if price_sources else 'estimation'
}
}
return result
except Exception as e:
logger.error(f"Error in dynamic price analysis for {city}: {str(e)}")
return {
'avg_price': 0,
'min_price': 0,
'max_price': 0,
'price_ranges': {},
'price_trend': 'unknown',
'city_tier': 'unknown',
'tier_confidence': 0.0,
'market_segment': 'unknown',
'segment_confidence': 0.0,
'price_sources': [],
'last_updated': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'data_points': 0,
'confidence': 0.0,
'market_analysis': {}
}
def analyze_price(data, context_text=None, latitude=None, longitude=None, property_data=None):
"""Enhanced price analysis using comprehensive market data and location factors"""
try:
# Extract basic property information
price_str = str(data.get('market_value', '1')).replace('$', '').replace('₹', '').replace(',', '').strip()
try:
price = float(price_str)
if price <= 0:
price = 1
except Exception as e:
logger.warning(f"Invalid price value: {price_str} ({str(e)})")
price = 1
sq_ft_str = str(data.get('sq_ft', '1')).replace(',', '').strip()
try:
sq_ft = float(re.sub(r'[^\d.]', '', sq_ft_str))
if sq_ft <= 0:
sq_ft = 1
except Exception as e:
logger.warning(f"Invalid sq_ft value: {sq_ft_str} ({str(e)})")
sq_ft = 1
city = data.get('city', '').strip() or 'Unknown'
# Detect if this is a rental property
is_rental = data.get('is_rental', False)
if not is_rental:
# Check status and description for rental keywords
status = data.get('status', '').lower()
description = data.get('description', '').lower()
is_rental = any(keyword in status for keyword in ['rent', 'lease', 'let', 'hiring']) or \
any(keyword in description for keyword in ['rent', 'lease', 'let', 'hiring', 'monthly', 'per month'])
# Calculate price per sq.ft properly
price_per_sqft = price / sq_ft if sq_ft > 0 else price
# Get market data for comparison
market_data = get_hyderabad_market_data()
# Adjust market data based on rental vs purchase
if is_rental:
# For rental properties, use monthly rental rates
market_avg = 25 # ₹25/sq ft/month average for Hyderabad rentals
market_min = 15 # ₹15/sq ft/month minimum
market_max = 80 # ₹80/sq ft/month maximum
else:
# For purchase properties, use purchase rates
market_avg = market_data.get('avg_price_per_sqft', 8500) if market_data else 8500
market_min = market_data.get('min_price_per_sqft', 4500) if market_data else 4500
market_max = market_data.get('max_price_per_sqft', 25000) if market_data else 25000
# Calculate deviation from market average
if market_avg > 0:
deviation = ((price_per_sqft - market_avg) / market_avg) * 100
else:
deviation = 0
# Determine assessment based on deviation and price reasonableness - Much more lenient
if is_rental:
# Rental property pricing logic
if price_per_sqft < 5: # Extremely low rental price
assessment = "suspicious_pricing"
confidence = 0.3 # Increased from 0.2
elif price_per_sqft < market_avg * 0.3: # Very below market rental
assessment = "below_market"
confidence = 0.5 # Increased from 0.4
elif price_per_sqft < market_avg * 0.7: # Below market rental
assessment = "below_market"
confidence = 0.8 # Increased from 0.7
elif price_per_sqft <= market_avg * 1.5: # Market rate rental
assessment = "market_rate"
confidence = 0.9 # Increased from 0.8
elif price_per_sqft <= market_avg * 2.0: # Above market rental
assessment = "above_market"
confidence = 0.8 # Increased from 0.7
else: # Very above market rental
assessment = "premium_pricing"
confidence = 0.6 # Increased from 0.5
else:
# Purchase property pricing logic (existing logic)
if price_per_sqft < 50: # Extremely low price - increased from 100
assessment = "suspicious_pricing"
confidence = 0.2 # Increased from 0.1
elif price_per_sqft < market_avg * 0.2: # Very below market - reduced from 0.3
assessment = "below_market"
confidence = 0.4 # Increased from 0.3
elif price_per_sqft < market_avg * 0.6: # Below market - reduced from 0.7
assessment = "below_market"
confidence = 0.7 # Increased from 0.6
elif price_per_sqft <= market_avg * 1.5: # Market rate - increased from 1.3
assessment = "market_rate"
confidence = 0.9 # Increased from 0.8
elif price_per_sqft <= market_avg * 2.5: # Above market - increased from 2.0
assessment = "above_market"
confidence = 0.8 # Increased from 0.7
else: # Very above market
assessment = "premium_pricing"
confidence = 0.6 # Increased from 0.5
# Generate risk indicators - Much more lenient
risk_indicators = []
if is_rental:
if price_per_sqft < 5: # Increased from 100
risk_indicators.append("⚠️ Property priced extremely low (suspicious)")
elif price_per_sqft < market_avg * 0.3: # Reduced from 0.3
risk_indicators.append("⚠️ Property priced significantly below market average")
elif price_per_sqft > market_avg * 2.0: # Increased from 2.0
risk_indicators.append("⚠️ Property priced significantly above market average")
else:
if price_per_sqft < 50: # Increased from 100
risk_indicators.append("⚠️ Property priced extremely low (suspicious)")
elif price_per_sqft < market_avg * 0.2: # Reduced from 0.3
risk_indicators.append("⚠️ Property priced significantly below market average")
elif price_per_sqft > market_avg * 2.5: # Increased from 2.0
risk_indicators.append("⚠️ Property priced significantly above market average")
# Price ranges for the city - Much more lenient
if is_rental:
price_ranges = {
'budget': {
'min': market_avg * 0.4, # Reduced from 0.5
'max': market_avg * 0.8,
'description': f'Budget rental properties in {city}'
},
'mid_range': {
'min': market_avg * 0.8,
'max': market_avg * 1.4, # Increased from 1.2
'description': f'Mid-range rental properties in {city}'
},
'premium': {
'min': market_avg * 1.4, # Reduced from 1.2
'max': market_avg * 2.5, # Increased from 2.0
'description': f'Premium rental properties in {city}'
}
}
else:
price_ranges = {
'budget': {
'min': market_avg * 0.3, # Reduced from 0.5
'max': market_avg * 0.8,
'description': f'Budget properties in {city}'
},
'mid_range': {
'min': market_avg * 0.8,
'max': market_avg * 1.4, # Increased from 1.2
'description': f'Mid-range properties in {city}'
},
'premium': {
'min': market_avg * 1.4, # Reduced from 1.2
'max': market_avg * 2.5, # Increased from 2.0
'description': f'Premium properties in {city}'
}
}
# Determine price range
price_range = "unknown"
for range_name, range_data in price_ranges.items():
if range_data['min'] <= price_per_sqft <= range_data['max']:
price_range = range_name
break
return {
'assessment': assessment,
'confidence': min(1.0, confidence), # Ensure confidence is capped at 100%
'price': price,
'formatted_price': f"₹{price:,.0f}",
'price_per_sqft': price_per_sqft,
'formatted_price_per_sqft': f"₹{price_per_sqft:,.2f}",
'price_range': price_range,
'location_price_assessment': f"Price analysis: {assessment}",
'has_price': True,
'has_sqft': True,
'is_rental': is_rental,
'market_trends': {
'trend': market_data.get('market_trend', 'unknown') if market_data else 'unknown',
'growth_rate': market_data.get('growth_rate', 0) if market_data else 0,
'inflation_rate': market_data.get('inflation_rate', 0) if market_data else 0,
'market_sentiment': market_data.get('market_sentiment', 'stable') if market_data else 'stable',
'volatility': market_data.get('market_volatility', 0) if market_data else 0
},
'price_factors': {
'market_average': market_avg,
'deviation_percentage': deviation,
'price_reasonableness': 'suspicious' if price_per_sqft < (5 if is_rental else 50) else 'reasonable'
},
'risk_indicators': risk_indicators,
'market_average': market_avg,
'deviation_percentage': deviation,
'analysis_method': 'enhanced',
'price_ranges': price_ranges,
'city_tier': 'metro' if city.lower() in ['hyderabad', 'mumbai', 'delhi', 'bangalore', 'chennai', 'kolkata'] else 'tier2'
}
except Exception as e:
logger.error(f"Error in comprehensive price analysis: {str(e)}")
return {
'assessment': 'unknown',
'confidence': 0.0,
'price': 0,
'formatted_price': '₹0',
'price_per_sqft': 0,
'formatted_price_per_sqft': '₹0',
'price_range': 'unknown',
'location_price_assessment': 'unknown',
'has_price': False,
'has_sqft': False,
'is_rental': False,
'market_trends': {},
'price_factors': {},
'risk_indicators': [],
'analysis_method': 'error'
}