Spaces:
Running
Running
| """ | |
| models.py β Shared data models used across all modules. | |
| """ | |
| from dataclasses import dataclass, field, asdict | |
| from typing import Optional, Dict, Any | |
| from datetime import datetime | |
| class Lead: | |
| """ | |
| Represents a single business lead extracted from any source. | |
| All fields are optional except ``business_name`` and ``service_category``. | |
| The ``score`` field is populated by the scoring processor. | |
| """ | |
| # ββ Core identity ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| business_name : str = "" | |
| service_category : str = "" # "web" | "app" | "ai" | |
| # ββ Contact ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| phone : str = "" | |
| email : str = "" | |
| address : str = "" | |
| website : str = "" | |
| contact_person : str = "" # Contact person name | |
| # ββ Meta βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| source : str = "" # e.g. "Google Maps", "JustDial" | |
| lead_source_scraper: str = "" # e.g. "google_maps", "justdial", "indiamart" | |
| notes : str = "" # human-readable signals | |
| user_notes : str = "" # notes added by sales team | |
| score : str = "LOW" # "HIGH" | "MEDIUM" | "LOW" | |
| score_points : int = 0 # raw numeric score | |
| industry : str = "" # auto-detected industry | |
| product_category : str = "" # product/service category from IndiaMART | |
| # ββ Lead lifecycle βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| status : str = "New" # "New" | "Contacted" | "Converted" | "Rejected" | |
| db_id : Optional[int] = None # SQLite row ID | |
| # ββ Timestamps βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| scraped_at : str = "" # ISO format datetime | |
| updated_at : str = "" # ISO format datetime | |
| # ββ Website analysis flags (set by WebsiteAnalyser) ββββββββββββββββββββ | |
| has_https : bool = False | |
| is_mobile_friendly: bool = True | |
| is_outdated : bool = False | |
| has_chatbot : bool = False | |
| has_online_booking: bool = False | |
| # ββ Extended analysis (v2) βββββββββββββββββββββββββββββββββββββββββββββ | |
| social_facebook : str = "" # Facebook page URL | |
| social_instagram : str = "" # Instagram page URL | |
| has_contact_form : bool = False | |
| ssl_expiry_days : Optional[int] = None # Days until SSL cert expires | |
| page_speed_score : Optional[float] = None # Google PageSpeed 0-100 | |
| load_time_sec : Optional[float] = None # Page load time in seconds | |
| site_status : str = "" # "live" | "dead" | "404" | "timeout" | |
| gmb_exists : bool = False # Google My Business listing exists | |
| review_count : int = 0 # Number of Google reviews | |
| opportunity_pitch : str = "" # 1-line sales pitch | |
| # ββ Dedup key (populated by deduplicator) ββββββββββββββββββββββββββββββ | |
| fingerprint: str = "" | |
| # ββ Raw extra data from scraper ββββββββββββββββββββββββββββββββββββββββ | |
| extra: Dict[str, Any] = field(default_factory=dict) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def to_dict(self) -> Dict[str, Any]: | |
| d = asdict(self) | |
| d.pop("extra", None) # omit raw blob from exports | |
| return d | |
| def display_name(self) -> str: | |
| return self.business_name or "Unknown Business" | |
| def service_label(self) -> str: | |
| labels = {"web": "Web Development", "app": "App Development", "ai": "AI Automation"} | |
| return labels.get(self.service_category, self.service_category.title()) | |
| def whatsapp_link(self) -> str: | |
| """Generate WhatsApp click-to-chat link from phone number.""" | |
| if not self.phone: | |
| return "" | |
| digits = "".join(c for c in self.phone if c.isdigit()) | |
| if digits.startswith("91") and len(digits) >= 12: | |
| return f"https://wa.me/{digits}" | |
| elif len(digits) == 10: | |
| return f"https://wa.me/91{digits}" | |
| return "" | |
| def stamp_scraped(self) -> None: | |
| """Set scraped_at to current time.""" | |
| self.scraped_at = datetime.now().isoformat() | |
| def stamp_updated(self) -> None: | |
| """Set updated_at to current time.""" | |
| self.updated_at = datetime.now().isoformat() | |
| def __repr__(self) -> str: | |
| return ( | |
| f"<Lead {self.business_name!r} " | |
| f"cat={self.service_category} score={self.score} status={self.status}>" | |
| ) | |