""" Data Manager Module Handles in-memory storage, persistence, and data operations. """ import json import logging from typing import List, Dict, Any, Set from datetime import datetime from pathlib import Path logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) DATA_DIR = Path("data") LISTINGS_FILE = DATA_DIR / "listings.json" FAVORITES_FILE = DATA_DIR / "favorites.json" class DataManager: """Manages in-memory and persistent storage of listings.""" def __init__(self): self.listings: Dict[str, Dict[str, Any]] = {} self.favorites: Set[str] = set() self.seen_ids: Set[str] = set() self._ensure_data_dir() self._load_data() def _ensure_data_dir(self): """Ensure data directory exists.""" DATA_DIR.mkdir(exist_ok=True) def _load_data(self): """Load persisted data from files.""" try: if LISTINGS_FILE.exists(): with open(LISTINGS_FILE, "r", encoding="utf-8") as f: data = json.load(f) self.listings = data.get("listings", {}) self.seen_ids = set(self.listings.keys()) logger.info(f"Loaded {len(self.listings)} listings from disk") except Exception as e: logger.error(f"Error loading listings: {e}") try: if FAVORITES_FILE.exists(): with open(FAVORITES_FILE, "r", encoding="utf-8") as f: data = json.load(f) self.favorites = set(data.get("favorites", [])) logger.info(f"Loaded {len(self.favorites)} favorites from disk") except Exception as e: logger.error(f"Error loading favorites: {e}") def _save_listings(self): """Persist listings to disk.""" try: with open(LISTINGS_FILE, "w", encoding="utf-8") as f: json.dump({"listings": self.listings}, f, ensure_ascii=False, indent=2) except Exception as e: logger.error(f"Error saving listings: {e}") def _save_favorites(self): """Persist favorites to disk.""" try: with open(FAVORITES_FILE, "w", encoding="utf-8") as f: json.dump({"favorites": list(self.favorites)}, f, ensure_ascii=False, indent=2) except Exception as e: logger.error(f"Error saving favorites: {e}") def add_listings(self, listings: List[Dict[str, Any]]) -> int: """ Add new listings to storage. Args: listings: List of listing dictionaries Returns: Number of new listings added """ new_count = 0 for listing in listings: divar_id = listing.get("divar_id") if divar_id and divar_id not in self.seen_ids: self.listings[divar_id] = listing self.seen_ids.add(divar_id) new_count += 1 if new_count > 0: self._save_listings() logger.info(f"Added {new_count} new listings") return new_count def get_all_listings(self) -> List[Dict[str, Any]]: """Get all listings.""" return list(self.listings.values()) def get_listings_by_neighborhood(self, neighborhood: str) -> List[Dict[str, Any]]: """Get listings for a specific neighborhood.""" return [l for l in self.listings.values() if l.get("neighborhood") == neighborhood] def get_budget_fit_listings(self) -> List[Dict[str, Any]]: """Get listings that fit the budget.""" return [l for l in self.listings.values() if l.get("budget_fit") == "fits_budget"] def get_listing(self, divar_id: str) -> Dict[str, Any]: """Get a specific listing by ID.""" return self.listings.get(divar_id, {}) def add_favorite(self, divar_id: str) -> bool: """Add a listing to favorites (max 3).""" if len(self.favorites) >= 3 and divar_id not in self.favorites: logger.warning("Favorites limit (3) reached") return False self.favorites.add(divar_id) self._save_favorites() logger.info(f"Added {divar_id} to favorites") return True def remove_favorite(self, divar_id: str) -> bool: """Remove a listing from favorites.""" if divar_id in self.favorites: self.favorites.remove(divar_id) self._save_favorites() logger.info(f"Removed {divar_id} from favorites") return True return False def get_favorites(self) -> List[Dict[str, Any]]: """Get all favorite listings.""" return [self.listings[fid] for fid in self.favorites if fid in self.listings] def is_favorite(self, divar_id: str) -> bool: """Check if a listing is in favorites.""" return divar_id in self.favorites def clear_all(self): """Clear all data (for testing).""" self.listings.clear() self.favorites.clear() self.seen_ids.clear() self._save_listings() self._save_favorites() logger.info("Cleared all data") def get_statistics(self) -> Dict[str, Any]: """Get statistics about stored listings.""" listings = self.get_all_listings() budget_fit = len(self.get_budget_fit_listings()) return { "total_listings": len(listings), "budget_fit_count": budget_fit, "favorites_count": len(self.favorites), "neighborhoods": list(set(l.get("neighborhood", "") for l in listings)), "last_updated": datetime.now().isoformat() } # Global instance _data_manager = None def get_data_manager() -> DataManager: """Factory function to get or create data manager instance.""" global _data_manager if _data_manager is None: _data_manager = DataManager() return _data_manager if __name__ == "__main__": manager = get_data_manager() print(f"Statistics: {manager.get_statistics()}")