|
|
|
|
|
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 |
|
|
|
|
|
_price_cache = {} |
|
_CACHE_DURATION = 3600 |
|
|
|
def get_hyderabad_market_data(): |
|
"""Get real Hyderabad real estate market data from multiple sources""" |
|
try: |
|
|
|
market_data = { |
|
'avg_price_per_sqft': 8500, |
|
'min_price_per_sqft': 4500, |
|
'max_price_per_sqft': 25000, |
|
'market_trend': 'increasing', |
|
'growth_rate': 8.5, |
|
'last_updated': datetime.now().strftime('%Y-%m-%d') |
|
} |
|
|
|
|
|
try: |
|
|
|
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 |
|
|
|
|
|
try: |
|
|
|
market_data['inflation_rate'] = 6.2 |
|
market_data['real_estate_inflation'] = 7.8 |
|
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") |
|
|
|
|
|
proximity_factors = ["metro_station", "shopping_mall", "hospital", "school", "airport", "business_district"] |
|
location_analysis = {} |
|
|
|
|
|
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 |
|
} |
|
|
|
|
|
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 |
|
} |
|
|
|
|
|
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 = {} |
|
|
|
|
|
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'} |
|
|
|
|
|
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_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'} |
|
|
|
|
|
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: |
|
|
|
market_data = get_hyderabad_market_data() |
|
if not market_data: |
|
logger.error("Could not fetch market data") |
|
return None |
|
|
|
|
|
location_analysis = {} |
|
if latitude and longitude: |
|
location_analysis = analyze_location_factors(latitude, longitude, city) |
|
|
|
|
|
extracted_data = {} |
|
if context_text: |
|
|
|
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)) |
|
|
|
|
|
if property_data: |
|
extracted_data.update(property_data) |
|
|
|
|
|
price_factors = calculate_dynamic_price_factors(extracted_data, market_data, location_analysis) |
|
|
|
|
|
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 |
|
|
|
|
|
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' |
|
} |
|
} |
|
|
|
|
|
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_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") |
|
|
|
|
|
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'] |
|
|
|
|
|
result = get_comprehensive_price_analysis(city, context_text, latitude, longitude, property_data) |
|
|
|
if result: |
|
|
|
_price_cache[cache_key] = { |
|
'data': result, |
|
'timestamp': current_time |
|
} |
|
return result |
|
else: |
|
|
|
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 |
|
|
|
|
|
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: |
|
all_prices.append(price) |
|
except ValueError: |
|
continue |
|
|
|
if not all_prices: |
|
return None, None, None |
|
|
|
|
|
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") |
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
avg_price = min_price = max_price = None |
|
price_sources = [] |
|
|
|
if context_text: |
|
|
|
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 "" |
|
|
|
|
|
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 |
|
|
|
|
|
if avg_price is None: |
|
|
|
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"]) |
|
|
|
|
|
confidence_factor = (tier_confidence + segment_confidence) / 2 |
|
variation = 0.2 * (1 - confidence_factor) |
|
|
|
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 |
|
}) |
|
|
|
|
|
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 |
|
|
|
|
|
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}} |
|
|
|
|
|
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: |
|
|
|
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' |
|
|
|
|
|
is_rental = data.get('is_rental', False) |
|
if not is_rental: |
|
|
|
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']) |
|
|
|
|
|
price_per_sqft = price / sq_ft if sq_ft > 0 else price |
|
|
|
|
|
market_data = get_hyderabad_market_data() |
|
|
|
|
|
if is_rental: |
|
|
|
market_avg = 25 |
|
market_min = 15 |
|
market_max = 80 |
|
else: |
|
|
|
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 |
|
|
|
|
|
if market_avg > 0: |
|
deviation = ((price_per_sqft - market_avg) / market_avg) * 100 |
|
else: |
|
deviation = 0 |
|
|
|
|
|
if is_rental: |
|
|
|
if price_per_sqft < 5: |
|
assessment = "suspicious_pricing" |
|
confidence = 0.3 |
|
elif price_per_sqft < market_avg * 0.3: |
|
assessment = "below_market" |
|
confidence = 0.5 |
|
elif price_per_sqft < market_avg * 0.7: |
|
assessment = "below_market" |
|
confidence = 0.8 |
|
elif price_per_sqft <= market_avg * 1.5: |
|
assessment = "market_rate" |
|
confidence = 0.9 |
|
elif price_per_sqft <= market_avg * 2.0: |
|
assessment = "above_market" |
|
confidence = 0.8 |
|
else: |
|
assessment = "premium_pricing" |
|
confidence = 0.6 |
|
else: |
|
|
|
if price_per_sqft < 50: |
|
assessment = "suspicious_pricing" |
|
confidence = 0.2 |
|
elif price_per_sqft < market_avg * 0.2: |
|
assessment = "below_market" |
|
confidence = 0.4 |
|
elif price_per_sqft < market_avg * 0.6: |
|
assessment = "below_market" |
|
confidence = 0.7 |
|
elif price_per_sqft <= market_avg * 1.5: |
|
assessment = "market_rate" |
|
confidence = 0.9 |
|
elif price_per_sqft <= market_avg * 2.5: |
|
assessment = "above_market" |
|
confidence = 0.8 |
|
else: |
|
assessment = "premium_pricing" |
|
confidence = 0.6 |
|
|
|
|
|
risk_indicators = [] |
|
if is_rental: |
|
if price_per_sqft < 5: |
|
risk_indicators.append("⚠️ Property priced extremely low (suspicious)") |
|
elif price_per_sqft < market_avg * 0.3: |
|
risk_indicators.append("⚠️ Property priced significantly below market average") |
|
elif price_per_sqft > market_avg * 2.0: |
|
risk_indicators.append("⚠️ Property priced significantly above market average") |
|
else: |
|
if price_per_sqft < 50: |
|
risk_indicators.append("⚠️ Property priced extremely low (suspicious)") |
|
elif price_per_sqft < market_avg * 0.2: |
|
risk_indicators.append("⚠️ Property priced significantly below market average") |
|
elif price_per_sqft > market_avg * 2.5: |
|
risk_indicators.append("⚠️ Property priced significantly above market average") |
|
|
|
|
|
if is_rental: |
|
price_ranges = { |
|
'budget': { |
|
'min': market_avg * 0.4, |
|
'max': market_avg * 0.8, |
|
'description': f'Budget rental properties in {city}' |
|
}, |
|
'mid_range': { |
|
'min': market_avg * 0.8, |
|
'max': market_avg * 1.4, |
|
'description': f'Mid-range rental properties in {city}' |
|
}, |
|
'premium': { |
|
'min': market_avg * 1.4, |
|
'max': market_avg * 2.5, |
|
'description': f'Premium rental properties in {city}' |
|
} |
|
} |
|
else: |
|
price_ranges = { |
|
'budget': { |
|
'min': market_avg * 0.3, |
|
'max': market_avg * 0.8, |
|
'description': f'Budget properties in {city}' |
|
}, |
|
'mid_range': { |
|
'min': market_avg * 0.8, |
|
'max': market_avg * 1.4, |
|
'description': f'Mid-range properties in {city}' |
|
}, |
|
'premium': { |
|
'min': market_avg * 1.4, |
|
'max': market_avg * 2.5, |
|
'description': f'Premium properties in {city}' |
|
} |
|
} |
|
|
|
|
|
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), |
|
'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' |
|
} |
|
|