File size: 6,566 Bytes
ecef140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"""
check_call_tz.py — what timezone does validation.call_timezone() give for a
number, and how many of the CRM's real leads resolve to a zone at all?

Read-only. Two parts:

  1. A few known numbers with the zone they SHOULD map to (PASS/FAIL).
  2. Every lead in the DB: how many resolve to a timezone, the distribution,
     and a sample of the ones that come back unknown (toll-free, area codes
     spanning two zones, no country).

    uv run python scripts/check_call_tz.py
    uv run python scripts/check_call_tz.py --unknown 30     # print more unknown rows
    uv run python scripts/check_call_tz.py --sample 20      # cross-check real leads by eye

--sample N picks N random leads that resolved to a timezone and prints, for each,
what libphonenumber's OWN geography database says the number belongs to (city /
state, from the area code) next to the timezone we derived and the local time
right now -- so you can check "212 -> New York -> 6:04 AM" against a world clock
yourself instead of trusting the code.
"""

from __future__ import annotations

import argparse
import random
import sys
from collections import Counter
from datetime import datetime, timezone
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from sqlalchemy import select  # noqa: E402

from app.database import SessionLocal  # noqa: E402
from app.models import Lead  # noqa: E402
from app.services.validation import call_timezone  # noqa: E402

# (phone, country hint, expected zone or "" for unknown)
KNOWN = [
    ("+1 214-555-0142", "US", "America/Chicago"),        # Dallas
    ("+1 212-555-0142", "US", "America/New_York"),       # New York
    ("+1 310-555-0142", "US", "America/Los_Angeles"),    # Los Angeles
    ("+1 604-555-0142", "CA", "America/Vancouver"),      # Vancouver
    ("+44 20 7946 0958", "GB", "Europe/London"),
    ("+971 4 555 0142", "AE", "Asia/Dubai"),
    ("+1 800-555-0142", "US", ""),                       # toll-free, US multi-zone -> unknown
    ("+1 800-555-0142", "", ""),
    ("", "GB", "Europe/London"),                         # no number, single-zone country
    ("", "US", ""),
]


def _print_sample(leads, n: int) -> None:
    import phonenumbers
    from phonenumbers import geocoder

    try:
        from zoneinfo import ZoneInfo
    except ImportError:  # pragma: no cover
        ZoneInfo = None  # type: ignore[assignment]

    pool = [l for l in leads if call_timezone(l.contact_phone or "", l.phone_country or l.country or "")]
    picks = random.sample(pool, min(n, len(pool)))
    now_utc = datetime.now(timezone.utc)
    print(f"\n3. Cross-check sample ({len(picks)} random leads). UTC now: {now_utc:%Y-%m-%d %H:%M}")
    print("   Compare 'number belongs to' and 'local time' with a world clock / a quick search.")
    warned = False
    for lead in picks:
        raw = (lead.contact_phone or "").split(",")[0].strip()
        if raw.startswith("00"):
            raw = "+" + raw[2:]
        region = (lead.phone_country or lead.country or "").upper() or None
        where = "?"
        try:
            parsed = phonenumbers.parse(raw, None if raw.startswith("+") else region)
            where = geocoder.description_for_number(parsed, "en") or "(country only)"
        except Exception:
            pass
        tz = call_timezone(lead.contact_phone or "", lead.phone_country or lead.country or "")
        local = "?"
        if ZoneInfo is not None:
            try:
                t = now_utc.astimezone(ZoneInfo(tz))
                weekday = t.strftime("%a")
                is_open = weekday not in ("Sat", "Sun") and 9 <= t.hour < 17
                local = f"{t:%a %H:%M}  {'OPEN ' if is_open else 'closed'}"
            except Exception:
                if not warned:
                    print("   (Windows has no IANA timezone data for Python: run  uv pip install tzdata  to see local times here)")
                    warned = True
        print(f"   #{lead.id:<5} {lead.company_name[:26]:26} {raw[:19]:19} belongs to: {where[:22]:22} tz={tz:20} local={local}")


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("--unknown", type=int, default=15, help="how many unknown-zone leads to print")
    ap.add_argument("--sample", type=int, default=0, help="cross-check N random leads (geography + local time)")
    args = ap.parse_args()

    print("1. Known numbers")
    bad = 0
    for phone, country, want in KNOWN:
        got = call_timezone(phone, country)
        ok = got == want
        bad += 0 if ok else 1
        print(f"   {'PASS' if ok else 'FAIL'}  {phone or '(none)':20} {country or '-':3} -> {got or 'unknown':22} (want {want or 'unknown'})")

    db = SessionLocal()
    try:
        leads = list(db.scalars(select(Lead)).all())
    finally:
        db.close()

    zones: Counter = Counter()
    unknown: list[Lead] = []
    for lead in leads:
        tz = call_timezone(lead.contact_phone or "", lead.phone_country or lead.country or "")
        zones[tz or "(unknown)"] += 1
        if not tz:
            unknown.append(lead)

    total = len(leads)
    known = total - len(unknown)
    no_phone = sum(1 for l in unknown if not (l.contact_phone or "").strip())
    has_phone_total = sum(1 for l in leads if (l.contact_phone or "").strip())
    print(f"\n2. {total} lead(s) in the CRM: {known} resolve to a timezone, {len(unknown)} unknown"
          + (f" ({100 * known // total}% known)" if total else ""))
    print(f"   of the {len(unknown)} unknown: {no_phone} have NO phone number at all, "
          f"{len(unknown) - no_phone} have a number but no single timezone (toll-free / multi-zone area code)")
    print(f"   leads with any phone number: {has_phone_total} of {total}"
          + (f" ({100 * has_phone_total // total}%)" if total else ""))
    for tz, n in zones.most_common():
        print(f"   {n:>5}  {tz}")
    if unknown:
        print(f"\n   Unknown sample (first {min(args.unknown, len(unknown))}):")
        for lead in unknown[: args.unknown]:
            print(
                f"     #{lead.id:<5} {lead.company_name[:32]:32} phone={lead.contact_phone[:22]:22} "
                f"country={lead.country or '-':3} phone_country={lead.phone_country or '-'}"
            )

    if args.sample:
        _print_sample(leads, args.sample)

    print("\nRESULT:", "all known-number checks passed" if not bad else f"{bad} known-number check(s) FAILED")
    return 0 if not bad else 1


if __name__ == "__main__":
    sys.exit(main())