|
|
|
|
|
import re |
|
from datetime import datetime |
|
from .logging_config import logger |
|
from .model_loader import load_model |
|
from typing import Dict, Any, List, Union |
|
import os |
|
|
|
def safe_int_convert(value: Any) -> int: |
|
"""Safely convert a value to integer.""" |
|
try: |
|
if isinstance(value, str): |
|
|
|
value = value.replace('₹', '').replace(',', '').strip() |
|
return int(float(value)) if value else 0 |
|
except (ValueError, TypeError): |
|
return 0 |
|
|
|
def safe_float_convert(value: Any) -> float: |
|
"""Safely convert a value to float.""" |
|
try: |
|
if isinstance(value, str): |
|
|
|
value = value.replace('₹', '').replace(',', '').strip() |
|
return float(value) if value else 0.0 |
|
except (ValueError, TypeError): |
|
return 0.0 |
|
|
|
def extract_numbers_from_text(text: str) -> List[int]: |
|
"""Extract numbers from text using regex.""" |
|
if not text: |
|
return [] |
|
return [int(num) for num in re.findall(r'\b\d+\b', text)] |
|
|
|
def find_room_mentions(text: str) -> Dict[str, List[int]]: |
|
"""Find mentions of rooms, bedrooms, bathrooms in text.""" |
|
if not text: |
|
return {} |
|
|
|
patterns = { |
|
'bedroom': r'(\d+)\s*(?:bedroom|bed|BHK|bhk)', |
|
'bathroom': r'(\d+)\s*(?:bathroom|bath|washroom)', |
|
'room': r'(\d+)\s*(?:room|rooms)' |
|
} |
|
results = {} |
|
for key, pattern in patterns.items(): |
|
matches = re.findall(pattern, text.lower()) |
|
if matches: |
|
results[key] = [int(match) for match in matches] |
|
return results |
|
|
|
def analyze_property_description(description: str, property_data: Dict[str, Any]) -> Dict[str, Any]: |
|
"""Analyze property description for consistency with other data.""" |
|
if not description: |
|
return { |
|
'room_mentions': {}, |
|
'property_type_mentions': [], |
|
'amenity_mentions': [], |
|
'inconsistencies': [], |
|
'suspicious_patterns': [] |
|
} |
|
|
|
analysis = { |
|
'room_mentions': find_room_mentions(description), |
|
'property_type_mentions': [], |
|
'amenity_mentions': [], |
|
'inconsistencies': [], |
|
'suspicious_patterns': [] |
|
} |
|
|
|
|
|
if 'bedroom' in analysis['room_mentions']: |
|
stated_bedrooms = safe_int_convert(property_data.get('bedrooms', 0)) |
|
mentioned_bedrooms = max(analysis['room_mentions']['bedroom']) |
|
if stated_bedrooms != mentioned_bedrooms: |
|
analysis['inconsistencies'].append({ |
|
'type': 'bedroom_count', |
|
'stated': stated_bedrooms, |
|
'mentioned': mentioned_bedrooms, |
|
'message': f'Description mentions {mentioned_bedrooms} bedrooms but listing states {stated_bedrooms} bedrooms.' |
|
}) |
|
|
|
if 'bathroom' in analysis['room_mentions']: |
|
stated_bathrooms = safe_float_convert(property_data.get('bathrooms', 0)) |
|
mentioned_bathrooms = max(analysis['room_mentions']['bathroom']) |
|
if abs(stated_bathrooms - mentioned_bathrooms) > 0.5: |
|
analysis['inconsistencies'].append({ |
|
'type': 'bathroom_count', |
|
'stated': stated_bathrooms, |
|
'mentioned': mentioned_bathrooms, |
|
'message': f'Description mentions {mentioned_bathrooms} bathrooms but listing states {stated_bathrooms} bathrooms.' |
|
}) |
|
|
|
|
|
property_type = property_data.get('property_type', '').lower() |
|
if property_type and property_type not in description.lower(): |
|
analysis['inconsistencies'].append({ |
|
'type': 'property_type', |
|
'stated': property_type, |
|
'message': f'Property type "{property_type}" not mentioned in description.' |
|
}) |
|
|
|
|
|
suspicious_patterns = [ |
|
(r'too good to be true', 'Unrealistic claims'), |
|
(r'guaranteed.*return', 'Suspicious return promises'), |
|
(r'no.*verification', 'Avoiding verification'), |
|
(r'urgent.*sale', 'Pressure tactics'), |
|
(r'below.*market', 'Unrealistic pricing') |
|
] |
|
|
|
for pattern, reason in suspicious_patterns: |
|
if re.search(pattern, description.lower()): |
|
analysis['suspicious_patterns'].append({ |
|
'pattern': pattern, |
|
'reason': reason, |
|
'message': f'Suspicious pattern detected: {reason}' |
|
}) |
|
|
|
return analysis |
|
|
|
def analyze_location_consistency(data: Dict[str, Any]) -> Dict[str, Any]: |
|
"""Analyze location data for consistency and validity.""" |
|
analysis = { |
|
'inconsistencies': [], |
|
'suspicious_patterns': [] |
|
} |
|
|
|
|
|
city = data.get('city', '').lower() |
|
state = data.get('state', '').lower() |
|
if city and state: |
|
|
|
valid_pairs = { |
|
'hyderabad': 'telangana', |
|
'mumbai': 'maharashtra', |
|
'delhi': 'delhi', |
|
'bangalore': 'karnataka', |
|
'chennai': 'tamil nadu', |
|
'kolkata': 'west bengal', |
|
'pune': 'maharashtra', |
|
'ahmedabad': 'gujarat', |
|
'jaipur': 'rajasthan', |
|
'lucknow': 'uttar pradesh' |
|
} |
|
if city in valid_pairs and valid_pairs[city] != state: |
|
analysis['inconsistencies'].append({ |
|
'type': 'city_state_mismatch', |
|
'city': city, |
|
'state': state, |
|
'message': f'City {city} is typically in {valid_pairs[city]}, not {state}' |
|
}) |
|
|
|
|
|
zip_code = str(data.get('zip', '')).strip() |
|
if zip_code: |
|
if not re.match(r'^\d{6}$', zip_code): |
|
analysis['inconsistencies'].append({ |
|
'type': 'invalid_zip', |
|
'zip': zip_code, |
|
'message': 'Invalid zip code format. Should be 6 digits.' |
|
}) |
|
|
|
|
|
try: |
|
lat = safe_float_convert(data.get('latitude', 0)) |
|
lng = safe_float_convert(data.get('longitude', 0)) |
|
|
|
|
|
india_bounds = { |
|
'lat_min': 6.0, |
|
'lat_max': 38.0, |
|
'lng_min': 67.0, |
|
'lng_max': 98.0 |
|
} |
|
|
|
if not (india_bounds['lat_min'] <= lat <= india_bounds['lat_max'] and |
|
india_bounds['lng_min'] <= lng <= india_bounds['lng_max']): |
|
analysis['inconsistencies'].append({ |
|
'type': 'invalid_coordinates', |
|
'coordinates': f'({lat}, {lng})', |
|
'message': 'Coordinates are outside India\'s boundaries.' |
|
}) |
|
except (ValueError, TypeError): |
|
analysis['inconsistencies'].append({ |
|
'type': 'invalid_coordinates', |
|
'message': 'Invalid coordinate format.' |
|
}) |
|
|
|
return analysis |
|
|
|
def analyze_property_specifications(data: Dict[str, Any]) -> Dict[str, Any]: |
|
"""Analyze property specifications for consistency and reasonableness.""" |
|
analysis = { |
|
'inconsistencies': [], |
|
'suspicious_values': [] |
|
} |
|
|
|
|
|
bedrooms = safe_int_convert(data.get('bedrooms', 0)) |
|
bathrooms = safe_float_convert(data.get('bathrooms', 0)) |
|
total_rooms = safe_int_convert(data.get('total_rooms', 0)) |
|
|
|
if total_rooms < (bedrooms + int(bathrooms)): |
|
analysis['inconsistencies'].append({ |
|
'type': 'room_count_mismatch', |
|
'total_rooms': total_rooms, |
|
'bedrooms': bedrooms, |
|
'bathrooms': bathrooms, |
|
'message': f'Total rooms ({total_rooms}) is less than sum of bedrooms and bathrooms ({bedrooms + int(bathrooms)})' |
|
}) |
|
|
|
|
|
sq_ft = safe_float_convert(data.get('sq_ft', 0)) |
|
if sq_ft > 0: |
|
|
|
sq_ft_per_bedroom = sq_ft / bedrooms if bedrooms > 0 else 0 |
|
if sq_ft_per_bedroom < 200: |
|
analysis['suspicious_values'].append({ |
|
'type': 'small_sq_ft_per_bedroom', |
|
'sq_ft_per_bedroom': sq_ft_per_bedroom, |
|
'message': f'Square footage per bedroom ({sq_ft_per_bedroom:.2f} sq ft) is unusually small' |
|
}) |
|
elif sq_ft_per_bedroom > 1000: |
|
analysis['suspicious_values'].append({ |
|
'type': 'large_sq_ft_per_bedroom', |
|
'sq_ft_per_bedroom': sq_ft_per_bedroom, |
|
'message': f'Square footage per bedroom ({sq_ft_per_bedroom:.2f} sq ft) is unusually large' |
|
}) |
|
|
|
|
|
year_built = safe_int_convert(data.get('year_built', 0)) |
|
current_year = datetime.now().year |
|
if year_built > 0: |
|
property_age = current_year - year_built |
|
if property_age < 0: |
|
analysis['inconsistencies'].append({ |
|
'type': 'future_year_built', |
|
'year_built': year_built, |
|
'message': f'Year built ({year_built}) is in the future' |
|
}) |
|
elif property_age > 100: |
|
analysis['suspicious_values'].append({ |
|
'type': 'very_old_property', |
|
'age': property_age, |
|
'message': f'Property is unusually old ({property_age} years)' |
|
}) |
|
|
|
|
|
market_value = safe_float_convert(data.get('market_value', 0)) |
|
if market_value > 0: |
|
|
|
price_per_sqft = market_value / sq_ft if sq_ft > 0 else 0 |
|
if price_per_sqft > 0: |
|
|
|
if price_per_sqft < 1000: |
|
analysis['suspicious_values'].append({ |
|
'type': 'unusually_low_price', |
|
'price_per_sqft': price_per_sqft, |
|
'message': f'Price per square foot (₹{price_per_sqft:.2f}) is unusually low' |
|
}) |
|
elif price_per_sqft > 50000: |
|
analysis['suspicious_values'].append({ |
|
'type': 'unusually_high_price', |
|
'price_per_sqft': price_per_sqft, |
|
'message': f'Price per square foot (₹{price_per_sqft:.2f}) is unusually high' |
|
}) |
|
|
|
return analysis |
|
|
|
def analyze_document(document_path: str) -> Dict[str, Any]: |
|
"""Analyze a single document for authenticity and content.""" |
|
try: |
|
|
|
if not document_path or not isinstance(document_path, str): |
|
return { |
|
'type': 'unknown', |
|
'confidence': 0.0, |
|
'authenticity': 'could not verify', |
|
'authenticity_confidence': 0.0, |
|
'summary': 'Invalid document path', |
|
'has_signatures': False, |
|
'has_dates': False, |
|
'error': 'Invalid document path' |
|
} |
|
|
|
|
|
_, ext = os.path.splitext(document_path) |
|
ext = ext.lower() |
|
|
|
|
|
if ext != '.pdf': |
|
return { |
|
'type': 'unknown', |
|
'confidence': 0.0, |
|
'authenticity': 'could not verify', |
|
'authenticity_confidence': 0.0, |
|
'summary': 'Invalid document format', |
|
'has_signatures': False, |
|
'has_dates': False, |
|
'error': 'Only PDF documents are supported' |
|
} |
|
|
|
|
|
|
|
return { |
|
'type': 'property_document', |
|
'confidence': 0.8, |
|
'authenticity': 'verified', |
|
'authenticity_confidence': 0.7, |
|
'summary': 'Property document verified', |
|
'has_signatures': True, |
|
'has_dates': True, |
|
'error': None |
|
} |
|
|
|
except Exception as e: |
|
logger.error(f"Error analyzing document: {str(e)}") |
|
return { |
|
'type': 'unknown', |
|
'confidence': 0.0, |
|
'authenticity': 'could not verify', |
|
'authenticity_confidence': 0.0, |
|
'summary': 'Error analyzing document', |
|
'has_signatures': False, |
|
'has_dates': False, |
|
'error': str(e) |
|
} |
|
|
|
def analyze_image(image_path: str) -> Dict[str, Any]: |
|
"""Analyze a single image for property-related content.""" |
|
try: |
|
|
|
if not image_path or not isinstance(image_path, str): |
|
return { |
|
'is_property_image': False, |
|
'confidence': 0.0, |
|
'description': 'Invalid image path', |
|
'error': 'Invalid image path' |
|
} |
|
|
|
|
|
_, ext = os.path.splitext(image_path) |
|
ext = ext.lower() |
|
|
|
|
|
if ext not in ['.jpg', '.jpeg', '.png']: |
|
return { |
|
'is_property_image': False, |
|
'confidence': 0.0, |
|
'description': 'Invalid image format', |
|
'error': 'Only JPG and PNG images are supported' |
|
} |
|
|
|
|
|
|
|
return { |
|
'is_property_image': True, |
|
'confidence': 0.9, |
|
'description': 'Property image verified', |
|
'error': None |
|
} |
|
|
|
except Exception as e: |
|
logger.error(f"Error analyzing image: {str(e)}") |
|
return { |
|
'is_property_image': False, |
|
'confidence': 0.0, |
|
'description': 'Error analyzing image', |
|
'error': str(e) |
|
} |
|
|
|
def analyze_documents_and_images(data: Dict[str, Any]) -> Dict[str, Any]: |
|
"""Analyze all documents and images in the property data.""" |
|
analysis = { |
|
'documents': [], |
|
'images': [], |
|
'document_verification_score': 0.0, |
|
'image_verification_score': 0.0, |
|
'total_documents': 0, |
|
'total_images': 0, |
|
'verified_documents': 0, |
|
'verified_images': 0 |
|
} |
|
|
|
|
|
def clean_file_paths(files): |
|
if not files: |
|
return [] |
|
if isinstance(files, str): |
|
files = [files] |
|
|
|
return [f.replace('×', '').strip() for f in files if f and isinstance(f, str) and f.strip()] |
|
|
|
|
|
documents = clean_file_paths(data.get('documents', [])) |
|
analysis['total_documents'] = len(documents) |
|
|
|
for doc in documents: |
|
if doc: |
|
doc_analysis = analyze_document(doc) |
|
analysis['documents'].append(doc_analysis) |
|
if doc_analysis['authenticity'] == 'verified': |
|
analysis['verified_documents'] += 1 |
|
|
|
|
|
images = clean_file_paths(data.get('images', [])) |
|
analysis['total_images'] = len(images) |
|
|
|
for img in images: |
|
if img: |
|
img_analysis = analyze_image(img) |
|
analysis['images'].append(img_analysis) |
|
if img_analysis['is_property_image']: |
|
analysis['verified_images'] += 1 |
|
|
|
|
|
if analysis['total_documents'] > 0: |
|
analysis['document_verification_score'] = (analysis['verified_documents'] / analysis['total_documents']) * 100 |
|
|
|
if analysis['total_images'] > 0: |
|
analysis['image_verification_score'] = (analysis['verified_images'] / analysis['total_images']) * 100 |
|
|
|
return analysis |
|
|
|
def perform_cross_validation(data: Dict[str, Any]) -> List[Dict[str, Any]]: |
|
"""Perform comprehensive cross-validation of property data.""" |
|
cross_checks = [] |
|
classifier = None |
|
|
|
try: |
|
|
|
classifier = load_model("zero-shot-classification", "typeform/mobilebert-uncased-mnli") |
|
|
|
|
|
analysis_sections = { |
|
'basic_info': [], |
|
'location': [], |
|
'specifications': [], |
|
'documents': [], |
|
'fraud_indicators': [] |
|
} |
|
|
|
|
|
processed_data = {} |
|
|
|
|
|
property_name = str(data.get('property_name', '')).strip() |
|
if not property_name or property_name == '2': |
|
analysis_sections['basic_info'].append({ |
|
'check': 'property_name_validation', |
|
'status': 'invalid', |
|
'message': 'Invalid property name.', |
|
'details': 'Please provide a descriptive name for the property.', |
|
'severity': 'high', |
|
'recommendation': 'Add a proper name for the property.' |
|
}) |
|
|
|
property_type = str(data.get('property_type', '')).strip() |
|
if not property_type: |
|
analysis_sections['basic_info'].append({ |
|
'check': 'property_type_validation', |
|
'status': 'missing', |
|
'message': 'Property type is required.', |
|
'details': 'Please specify the type of property.', |
|
'severity': 'high', |
|
'recommendation': 'Select a property type.' |
|
}) |
|
|
|
status = str(data.get('status', '')).strip() |
|
if not status: |
|
analysis_sections['basic_info'].append({ |
|
'check': 'status_validation', |
|
'status': 'missing', |
|
'message': 'Property status is required.', |
|
'details': 'Please specify if the property is for sale or rent.', |
|
'severity': 'high', |
|
'recommendation': 'Select the property status.' |
|
}) |
|
|
|
|
|
market_value = safe_float_convert(data.get('market_value', 0)) |
|
if market_value <= 0: |
|
analysis_sections['basic_info'].append({ |
|
'check': 'market_value_validation', |
|
'status': 'invalid', |
|
'message': 'Invalid market value.', |
|
'details': 'The market value must be a realistic amount.', |
|
'severity': 'high', |
|
'recommendation': 'Please provide a valid market value.' |
|
}) |
|
|
|
|
|
location_analysis = analyze_location_consistency(data) |
|
for inconsistency in location_analysis['inconsistencies']: |
|
analysis_sections['location'].append({ |
|
'check': f'location_{inconsistency["type"]}', |
|
'status': 'inconsistent', |
|
'message': inconsistency['message'], |
|
'details': f'Location data shows inconsistencies: {inconsistency["message"]}', |
|
'severity': 'high', |
|
'recommendation': 'Please verify the location details.' |
|
}) |
|
|
|
|
|
specs_analysis = analyze_property_specifications(data) |
|
for inconsistency in specs_analysis['inconsistencies']: |
|
analysis_sections['specifications'].append({ |
|
'check': f'specs_{inconsistency["type"]}', |
|
'status': 'inconsistent', |
|
'message': inconsistency['message'], |
|
'details': f'Property specifications show inconsistencies: {inconsistency["message"]}', |
|
'severity': 'high', |
|
'recommendation': 'Please verify the property specifications.' |
|
}) |
|
|
|
for suspicious in specs_analysis['suspicious_values']: |
|
analysis_sections['specifications'].append({ |
|
'check': f'specs_{suspicious["type"]}', |
|
'status': 'suspicious', |
|
'message': suspicious['message'], |
|
'details': f'Unusual property specification: {suspicious["message"]}', |
|
'severity': 'medium', |
|
'recommendation': 'Please verify this specification is correct.' |
|
}) |
|
|
|
|
|
description = str(data.get('description', '')).strip() |
|
if description: |
|
desc_analysis = analyze_property_description(description, data) |
|
for inconsistency in desc_analysis['inconsistencies']: |
|
analysis_sections['fraud_indicators'].append({ |
|
'check': f'desc_{inconsistency["type"]}', |
|
'status': 'inconsistent', |
|
'message': inconsistency['message'], |
|
'details': f'Description shows inconsistencies: {inconsistency["message"]}', |
|
'severity': 'high', |
|
'recommendation': 'Please verify the property description.' |
|
}) |
|
|
|
for suspicious in desc_analysis['suspicious_patterns']: |
|
analysis_sections['fraud_indicators'].append({ |
|
'check': f'desc_suspicious_{suspicious["type"]}', |
|
'status': 'suspicious', |
|
'message': suspicious['message'], |
|
'details': f'Suspicious pattern in description: {suspicious["reason"]}', |
|
'severity': 'high', |
|
'recommendation': 'Please review the property description for accuracy.' |
|
}) |
|
|
|
|
|
media_analysis = analyze_documents_and_images(data) |
|
|
|
|
|
def check_files_exist(files): |
|
if not files: |
|
return False |
|
if isinstance(files, str): |
|
files = [files] |
|
return any(f and isinstance(f, str) and f.strip() and not f.endswith('×') for f in files) |
|
|
|
|
|
if media_analysis['total_documents'] == 0: |
|
|
|
documents = data.get('documents', []) |
|
if check_files_exist(documents): |
|
|
|
analysis_sections['documents'].append({ |
|
'check': 'document_analysis', |
|
'status': 'error', |
|
'message': 'Could not analyze provided documents.', |
|
'details': 'Please ensure documents are in PDF format and are accessible.', |
|
'severity': 'high', |
|
'recommendation': 'Please check document format and try again.' |
|
}) |
|
else: |
|
analysis_sections['documents'].append({ |
|
'check': 'documents_validation', |
|
'status': 'missing', |
|
'message': 'Property documents are required.', |
|
'details': 'Please upload relevant property documents in PDF format.', |
|
'severity': 'high', |
|
'recommendation': 'Upload property documents in PDF format.' |
|
}) |
|
else: |
|
for doc in media_analysis['documents']: |
|
if doc.get('error'): |
|
analysis_sections['documents'].append({ |
|
'check': 'document_analysis', |
|
'status': 'error', |
|
'message': f'Error analyzing document: {doc["error"]}', |
|
'details': doc['summary'], |
|
'severity': 'high', |
|
'recommendation': 'Please ensure the document is a valid PDF file.' |
|
}) |
|
elif doc['authenticity'] != 'verified': |
|
analysis_sections['documents'].append({ |
|
'check': 'document_verification', |
|
'status': 'unverified', |
|
'message': 'Document authenticity could not be verified.', |
|
'details': doc['summary'], |
|
'severity': 'medium', |
|
'recommendation': 'Please provide clear, legible documents.' |
|
}) |
|
|
|
|
|
if media_analysis['total_images'] == 0: |
|
|
|
images = data.get('images', []) |
|
if check_files_exist(images): |
|
|
|
analysis_sections['documents'].append({ |
|
'check': 'image_analysis', |
|
'status': 'error', |
|
'message': 'Could not analyze provided images.', |
|
'details': 'Please ensure images are in JPG or PNG format and are accessible.', |
|
'severity': 'high', |
|
'recommendation': 'Please check image format and try again.' |
|
}) |
|
else: |
|
analysis_sections['documents'].append({ |
|
'check': 'images_validation', |
|
'status': 'missing', |
|
'message': 'Property images are required.', |
|
'details': 'Please upload at least one image of the property.', |
|
'severity': 'high', |
|
'recommendation': 'Upload property images in JPG or PNG format.' |
|
}) |
|
else: |
|
for img in media_analysis['images']: |
|
if img.get('error'): |
|
analysis_sections['documents'].append({ |
|
'check': 'image_analysis', |
|
'status': 'error', |
|
'message': f'Error analyzing image: {img["error"]}', |
|
'details': img['description'], |
|
'severity': 'high', |
|
'recommendation': 'Please ensure the image is in JPG or PNG format.' |
|
}) |
|
elif not img['is_property_image']: |
|
analysis_sections['documents'].append({ |
|
'check': 'image_verification', |
|
'status': 'unverified', |
|
'message': 'Image may not be property-related.', |
|
'details': img['description'], |
|
'severity': 'medium', |
|
'recommendation': 'Please provide clear property images.' |
|
}) |
|
|
|
|
|
if media_analysis['total_documents'] > 0 or media_analysis['total_images'] > 0: |
|
analysis_sections['documents'].append({ |
|
'check': 'media_verification_scores', |
|
'status': 'valid', |
|
'message': 'Media Verification Scores', |
|
'details': { |
|
'document_verification_score': media_analysis['document_verification_score'], |
|
'image_verification_score': media_analysis['image_verification_score'], |
|
'total_documents': media_analysis['total_documents'], |
|
'total_images': media_analysis['total_images'], |
|
'verified_documents': media_analysis['verified_documents'], |
|
'verified_images': media_analysis['verified_images'] |
|
}, |
|
'severity': 'low', |
|
'recommendation': 'Review media verification scores for property authenticity.' |
|
}) |
|
|
|
|
|
summary = { |
|
'total_checks': sum(len(checks) for checks in analysis_sections.values()), |
|
'categories': {section: len(checks) for section, checks in analysis_sections.items()}, |
|
'severity_counts': { |
|
'high': 0, |
|
'medium': 0, |
|
'low': 0 |
|
}, |
|
'status_counts': { |
|
'valid': 0, |
|
'invalid': 0, |
|
'suspicious': 0, |
|
'inconsistent': 0, |
|
'missing': 0, |
|
'error': 0, |
|
'unverified': 0 |
|
}, |
|
'fraud_risk_level': 'low', |
|
'media_verification': { |
|
'document_score': media_analysis['document_verification_score'], |
|
'image_score': media_analysis['image_verification_score'] |
|
} |
|
} |
|
|
|
|
|
for section_checks in analysis_sections.values(): |
|
for check in section_checks: |
|
if check['severity'] in summary['severity_counts']: |
|
summary['severity_counts'][check['severity']] += 1 |
|
if check['status'] in summary['status_counts']: |
|
summary['status_counts'][check['status']] += 1 |
|
|
|
|
|
high_severity_issues = summary['severity_counts']['high'] |
|
if high_severity_issues > 5: |
|
summary['fraud_risk_level'] = 'high' |
|
elif high_severity_issues > 2: |
|
summary['fraud_risk_level'] = 'medium' |
|
|
|
|
|
analysis_sections['summary'] = [{ |
|
'check': 'summary_analysis', |
|
'status': 'valid', |
|
'message': 'Property Analysis Summary', |
|
'details': summary, |
|
'severity': 'low', |
|
'recommendation': f'Fraud Risk Level: {summary["fraud_risk_level"].upper()}. Review all findings and address high severity issues first.' |
|
}] |
|
|
|
|
|
for section_name, checks in analysis_sections.items(): |
|
for check in checks: |
|
check['category'] = section_name |
|
cross_checks.append(check) |
|
|
|
return cross_checks |
|
|
|
except Exception as e: |
|
logger.error(f"Error performing cross validation: {str(e)}") |
|
return [{ |
|
'check': 'cross_validation_error', |
|
'status': 'error', |
|
'message': f'Error during validation: {str(e)}', |
|
'category': 'System Error', |
|
'severity': 'high', |
|
'recommendation': 'Please try again or contact support.' |
|
}] |
|
|