CR fixes
This commit is contained in:
@@ -6,7 +6,7 @@ Israeli real estate MCP system. Models provide validation, serialization,
|
|||||||
and type safety throughout the codebase.
|
and type safety throughout the codebase.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import date, datetime
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
from pydantic import BaseModel, Field, field_validator, computed_field, ConfigDict
|
from pydantic import BaseModel, Field, field_validator, computed_field, ConfigDict
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ class Deal(BaseModel):
|
|||||||
# Required fields
|
# Required fields
|
||||||
objectid: int = Field(..., description="Unique deal identifier")
|
objectid: int = Field(..., description="Unique deal identifier")
|
||||||
deal_amount: float = Field(..., alias="dealAmount", description="Transaction amount in NIS")
|
deal_amount: float = Field(..., alias="dealAmount", description="Transaction amount in NIS")
|
||||||
deal_date: str = Field(..., alias="dealDate", description="Transaction date (ISO format)")
|
deal_date: date = Field(..., alias="dealDate", description="Transaction date")
|
||||||
|
|
||||||
# Common optional fields
|
# Common optional fields
|
||||||
asset_area: Optional[float] = Field(None, alias="assetArea", description="Property area in sqm")
|
asset_area: Optional[float] = Field(None, alias="assetArea", description="Property area in sqm")
|
||||||
@@ -150,13 +150,21 @@ class Deal(BaseModel):
|
|||||||
|
|
||||||
@field_validator('deal_date', mode='before')
|
@field_validator('deal_date', mode='before')
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse_deal_date(cls, v: Any) -> str:
|
def parse_deal_date(cls, v: Any) -> date:
|
||||||
"""Parse deal date to ISO format string."""
|
"""Parse deal date string into a date object."""
|
||||||
if isinstance(v, str):
|
if isinstance(v, date):
|
||||||
return v
|
return v
|
||||||
if isinstance(v, datetime):
|
if isinstance(v, datetime):
|
||||||
return v.isoformat()
|
return v.date()
|
||||||
return str(v)
|
if isinstance(v, str):
|
||||||
|
# Handle ISO format with optional time and timezone
|
||||||
|
if 'T' in v:
|
||||||
|
v = v.split('T')[0]
|
||||||
|
try:
|
||||||
|
return date.fromisoformat(v)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(f"Invalid date format: {v}")
|
||||||
|
raise TypeError(f"Unsupported type for date parsing: {type(v)}")
|
||||||
|
|
||||||
|
|
||||||
class DealStatistics(BaseModel):
|
class DealStatistics(BaseModel):
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ This module provides pure mathematical functions for analyzing real estate deal
|
|||||||
|
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from typing import List
|
from typing import List
|
||||||
from datetime import datetime
|
import logging
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
from .models import Deal, DealStatistics
|
from .models import Deal, DealStatistics
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def calculate_deal_statistics(deals: List[Deal]) -> DealStatistics:
|
def calculate_deal_statistics(deals: List[Deal]) -> DealStatistics:
|
||||||
"""
|
"""
|
||||||
@@ -127,6 +130,7 @@ def calculate_deal_statistics(deals: List[Deal]) -> DealStatistics:
|
|||||||
date_str = date_str.split('T')[0]
|
date_str = date_str.split('T')[0]
|
||||||
parsed_dates.append(date_str)
|
parsed_dates.append(date_str)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
|
logger.warning(f"Invalid date format: {date_str}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if parsed_dates:
|
if parsed_dates:
|
||||||
@@ -135,7 +139,8 @@ def calculate_deal_statistics(deals: List[Deal]) -> DealStatistics:
|
|||||||
"earliest": sorted_dates[0],
|
"earliest": sorted_dates[0],
|
||||||
"latest": sorted_dates[-1],
|
"latest": sorted_dates[-1],
|
||||||
}
|
}
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
|
logger.warning("Invalid date format: {date_range_dict}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return DealStatistics(
|
return DealStatistics(
|
||||||
|
|||||||
+22
-10
@@ -427,21 +427,33 @@ class TestModelIntegration:
|
|||||||
|
|
||||||
def test_deal_to_statistics_workflow(self):
|
def test_deal_to_statistics_workflow(self):
|
||||||
"""Test creating deals and calculating statistics."""
|
"""Test creating deals and calculating statistics."""
|
||||||
|
# Import the function to test integration
|
||||||
|
from nadlan_mcp.govmap.statistics import calculate_deal_statistics
|
||||||
|
|
||||||
deals = [
|
deals = [
|
||||||
Deal(
|
Deal(
|
||||||
objectid=i,
|
objectid=1,
|
||||||
deal_amount=1500000.0 + (i * 10000),
|
deal_amount=1000000.0,
|
||||||
deal_date=f"2024-01-{i+1:02d}",
|
deal_date="2024-01-01",
|
||||||
asset_area=80.0 + i,
|
asset_area=100.0,
|
||||||
property_type_description="דירה"
|
property_type_description="דירה"
|
||||||
)
|
),
|
||||||
for i in range(10)
|
Deal(
|
||||||
|
objectid=2,
|
||||||
|
deal_amount=2000000.0,
|
||||||
|
deal_date="2024-01-02",
|
||||||
|
asset_area=100.0,
|
||||||
|
property_type_description="דירה"
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Verify all deals have price_per_sqm
|
stats = calculate_deal_statistics(deals)
|
||||||
for deal in deals:
|
|
||||||
assert deal.price_per_sqm is not None
|
assert isinstance(stats, DealStatistics)
|
||||||
assert deal.price_per_sqm > 0
|
assert stats.total_deals == 2
|
||||||
|
assert stats.price_statistics["mean"] == 1500000.0
|
||||||
|
assert stats.price_per_sqm_statistics["mean"] == 15000.0
|
||||||
|
assert stats.property_type_distribution["דירה"] == 2
|
||||||
|
|
||||||
def test_autocomplete_to_deals_workflow(self):
|
def test_autocomplete_to_deals_workflow(self):
|
||||||
"""Test autocomplete response leading to deal search."""
|
"""Test autocomplete response leading to deal search."""
|
||||||
|
|||||||
Reference in New Issue
Block a user