From 92b49d9f28cef32b507a42fdf9b2d244d469a5f5 Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 3 Oct 2025 08:33:23 +0200 Subject: [PATCH 01/15] Initial commit --- main.py | 0 phone.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 main.py create mode 100644 phone.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..e69de29 diff --git a/phone.py b/phone.py new file mode 100644 index 0000000..e69de29 From ef966c244453578094058db586fb3d9888837284 Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 3 Oct 2025 09:18:51 +0200 Subject: [PATCH 02/15] phone num --- phone.py | 1 + 1 file changed, 1 insertion(+) diff --git a/phone.py b/phone.py index e69de29..3c24afe 100644 --- a/phone.py +++ b/phone.py @@ -0,0 +1 @@ +number = "+27760932272" \ No newline at end of file From dff454c062cc04fbefbc0e6a58c216a45666af12 Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 3 Oct 2025 09:26:11 +0200 Subject: [PATCH 03/15] main updates --- .vscode/settings.json | 5 +++++ main.py | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7277d20 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "phonemumbers" + ] +} \ No newline at end of file diff --git a/main.py b/main.py index e69de29..1b408bb 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,3 @@ +import phonemumbers +from phonenumbers import geocoder, carrier, timezone +from phone import number \ No newline at end of file From 1eea7f8daa46608191575387e76909611d7b56f0 Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 3 Oct 2025 11:01:37 +0200 Subject: [PATCH 04/15] vscode settings --- .vscode/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7277d20..a35b370 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "cSpell.words": [ + "geocoder", "phonemumbers" - ] + ], + "python-envs.defaultEnvManager": "ms-python.python:venv", + "python-envs.pythonProjects": [] } \ No newline at end of file From a9c227c957d3638fecb54fd8134690b8641edc0f Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 3 Oct 2025 11:10:01 +0200 Subject: [PATCH 05/15] main updates --- .vscode/settings.json | 3 ++- main.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a35b370..170b19d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,9 @@ { "cSpell.words": [ "geocoder", + "pepnumber", "phonemumbers" ], - "python-envs.defaultEnvManager": "ms-python.python:venv", + "python-envs.defaultEnvManager": "ms-python.python:system", "python-envs.pythonProjects": [] } \ No newline at end of file diff --git a/main.py b/main.py index 1b408bb..f89067a 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,9 @@ -import phonemumbers +import phonenumbers from phonenumbers import geocoder, carrier, timezone -from phone import number \ No newline at end of file +from phone import number + +pepnumber = phonenumbers.parse(number) +location = geocoder.description_for_number(pepnumber, "en") +service_provider = carrier.name_for_number(pepnumber, "en") +time_zone = timezone.time_zones_for_number(pepnumber) +print(location) \ No newline at end of file From b74f002fbfdba3a20570224a7657fe4d2937ae58 Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 3 Oct 2025 11:19:13 +0200 Subject: [PATCH 06/15] contacts --- __pycache__/phone.cpython-313.pyc | Bin 0 -> 177 bytes phone.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 __pycache__/phone.cpython-313.pyc diff --git a/__pycache__/phone.cpython-313.pyc b/__pycache__/phone.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42a96d09d27f13a23421b0e8b6a854548fb2f3a5 GIT binary patch literal 177 zcmey&%ge<81P7iRMWd1DT literal 0 HcmV?d00001 diff --git a/phone.py b/phone.py index 3c24afe..24966cc 100644 --- a/phone.py +++ b/phone.py @@ -1 +1 @@ -number = "+27760932272" \ No newline at end of file +number = "+27684798035" \ No newline at end of file From 3559c3c760fb10926f702c643b83c1a844322601 Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 17 Oct 2025 14:20:13 +0200 Subject: [PATCH 07/15] updates to main for better tracking, environment variable for key safety, and gitignore --- .env.example | 1 + .gitignore | 1 + .hintrc | 11 +++ .vscode/settings.json | 3 + __pycache__/phone.cpython-313.pyc | Bin 177 -> 177 bytes main.py | 54 +++++++++++- mylocation.html | 137 ++++++++++++++++++++++++++++++ phone.py | 2 +- requirements.txt | 6 ++ 9 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .hintrc create mode 100644 mylocation.html create mode 100644 requirements.txt diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4c1b7a3 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +OPENCAGE_API_KEY=your_opencage_api_key_here \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/.hintrc b/.hintrc new file mode 100644 index 0000000..e205f72 --- /dev/null +++ b/.hintrc @@ -0,0 +1,11 @@ +{ + "extends": [ + "development" + ], + "hints": { + "meta-charset-utf-8": "off", + "axe/text-alternatives": "off", + "axe/language": "off", + "meta-viewport": "off" + } +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 170b19d..90980ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,9 @@ { "cSpell.words": [ + "dotenv", "geocoder", + "mylocation", + "opencage", "pepnumber", "phonemumbers" ], diff --git a/__pycache__/phone.cpython-313.pyc b/__pycache__/phone.cpython-313.pyc index 42a96d09d27f13a23421b0e8b6a854548fb2f3a5..765196585ca9648dea9608cca9123df1b2bf933d 100644 GIT binary patch delta 32 mcmdnUxRH_jGcPX}0}$-E^?V|?9;dmPfu*sLk-5=Cr+fgE0SM~= delta 32 mcmdnUxRH_jGcPX}0}vdXa(^PX9;cawiMgePfwAdCr+fg9o(Qr4 diff --git a/main.py b/main.py index f89067a..874f369 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,59 @@ import phonenumbers from phonenumbers import geocoder, carrier, timezone from phone import number +from opencage.geocoder import OpenCageGeocode +import folium +from dotenv import load_dotenv +import os +# Load environment variables from .env file +load_dotenv() + +# Get the API key from .env +key = os.getenv("OPENCAGE_API_KEY") +if not key: + raise ValueError("OPENCAGE_API_KEY not found in .env file") + +# Parse the phone number pepnumber = phonenumbers.parse(number) -location = geocoder.description_for_number(pepnumber, "en") + +# Get country name and detailed location (e.g., city or region) +location = geocoder.country_name_for_number(pepnumber, "en") +detailed_location = geocoder.description_for_number(pepnumber, "en") + +# Get service provider and time zone service_provider = carrier.name_for_number(pepnumber, "en") time_zone = timezone.time_zones_for_number(pepnumber) -print(location) \ No newline at end of file + +# Set up OpenCage Geocoder +geocoder_api = OpenCageGeocode(key) + +# Build query: prefer detailed location with country for accuracy +query = detailed_location if detailed_location else location +if detailed_location and location: + query = f"{detailed_location}, {location}" + +# Geocode the location +results = geocoder_api.geocode(query) + +if results and len(results): + lat = results[0]['geometry']['lat'] + lng = results[0]['geometry']['lng'] + + # Print details + print(f"Phone Number: {number}") + print(f"Country and Location: {location} in {detailed_location if detailed_location else 'Not available'}") + print(f"Service Provider: {service_provider}") + print(f"Time Zone: {time_zone}") + print(f"Latitude and Longitude: {lat}, {lng}") + print(f"Exact Location: https://www.google.com/maps/place/{lat},{lng}") + + # Create map centered on coordinates and adjust zoom + myMap = folium.Map(location=[lat, lng], zoom_start=9) + folium.Marker([lat, lng], popup=query).add_to(myMap) # Add marker for location + + # Save map to HTML file + myMap.save("mylocation.html") + print("Map saved!") +else: + print("Geocoding failed, No coordinates") \ No newline at end of file diff --git a/mylocation.html b/mylocation.html new file mode 100644 index 0000000..f203376 --- /dev/null +++ b/mylocation.html @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/phone.py b/phone.py index 24966cc..3c24afe 100644 --- a/phone.py +++ b/phone.py @@ -1 +1 @@ -number = "+27684798035" \ No newline at end of file +number = "+27760932272" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..78e4498 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +phonenumbers +requests +folium +geopy +opencage +python-dotenv \ No newline at end of file From 2e12a8384614e4c06a6b04296742bf0fd36eb584 Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 17 Oct 2025 14:26:07 +0200 Subject: [PATCH 08/15] phone modification --- .gitignore | 3 +- __pycache__/phone.cpython-313.pyc | Bin 177 -> 177 bytes mylocation.html | 230 ++++++++++++++---------------- phone.py | 2 +- 4 files changed, 112 insertions(+), 123 deletions(-) diff --git a/.gitignore b/.gitignore index 2eea525..4b0dca3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +phone.py \ No newline at end of file diff --git a/__pycache__/phone.cpython-313.pyc b/__pycache__/phone.cpython-313.pyc index 765196585ca9648dea9608cca9123df1b2bf933d..59494244d0de41480b8ba04bd3e40232b7fd82de 100644 GIT binary patch delta 29 jcmdnUxRH_jGcPX}0}zy$ewxT_z-DG-YHVsa(XjvkZc+zq delta 29 jcmdnUxRH_jGcPX}0}$-E^?V|?0h@uPv5}GaM8^UEdzuJK diff --git a/mylocation.html b/mylocation.html index f203376..3b668f2 100644 --- a/mylocation.html +++ b/mylocation.html @@ -1,137 +1,125 @@ - - + - - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + - - - -
- + + +
+ - \ No newline at end of file diff --git a/phone.py b/phone.py index 3c24afe..b0bc103 100644 --- a/phone.py +++ b/phone.py @@ -1 +1 @@ -number = "+27760932272" \ No newline at end of file +number = "+27766253512" \ No newline at end of file From 2ab7121f6bde0f3cd5f3020c3c4381defe2aa3bb Mon Sep 17 00:00:00 2001 From: Ashley K Motsie Date: Fri, 17 Oct 2025 15:10:18 +0200 Subject: [PATCH 09/15] Implemented visual gui for ux, enhanced number location tracking, saves history, and export phone tracking info --- .gitignore | 6 +- .vscode/settings.json | 6 +- main.py | 203 ++++++++++++++++++++++++++++++++++-------- mylocation.html | 32 +++---- requirements.txt | 3 +- 5 files changed, 195 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 4b0dca3..a8bbb54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ .env -phone.py \ No newline at end of file +geocode_cache.json +history.json +phone_lookup_export.csv +mylocation.html +history_map.html \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 90980ab..27e6973 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,15 @@ { "cSpell.words": [ + "dateutil", "dotenv", "geocoder", + "geopy", "mylocation", "opencage", + "pady", "pepnumber", - "phonemumbers" + "phonemumbers", + "phonenumbers" ], "python-envs.defaultEnvManager": "ms-python.python:system", "python-envs.pythonProjects": [] diff --git a/main.py b/main.py index 874f369..e532ff4 100644 --- a/main.py +++ b/main.py @@ -1,59 +1,190 @@ import phonenumbers from phonenumbers import geocoder, carrier, timezone -from phone import number from opencage.geocoder import OpenCageGeocode import folium from dotenv import load_dotenv import os +import json +import csv +from datetime import datetime +import webbrowser -# Load environment variables from .env file +# Load environment variables load_dotenv() - -# Get the API key from .env key = os.getenv("OPENCAGE_API_KEY") if not key: raise ValueError("OPENCAGE_API_KEY not found in .env file") -# Parse the phone number -pepnumber = phonenumbers.parse(number) +# Load or initialize cache +cache_file = "geocode_cache.json" +try: + with open(cache_file, "r") as f: + content = f.read().strip() + cache = json.loads(content) if content else {} +except (FileNotFoundError, json.JSONDecodeError): + cache = {} + +def save_cache(): + with open(cache_file, "w") as f: + json.dump(cache, f, indent=4) + +# Load or initialize history +history_file = "history.json" +try: + with open(history_file, "r") as f: + content = f.read().strip() + history = json.loads(content) if content else [] +except (FileNotFoundError, json.JSONDecodeError): + history = [] + +def save_history(number, location, detailed_location, service_provider, time_zone, lat, lng): + history.append({ + "number": number, + "country": location, + "detailed_location": detailed_location if detailed_location else "Not available", + "service_provider": service_provider if service_provider else "Unknown", + "time_zone": str(time_zone) if time_zone else "Unknown", + "latitude": lat, + "longitude": lng, + "timestamp": datetime.now().isoformat() + }) + with open(history_file, "w") as f: + json.dump(history, f, indent=4) + +def view_history(): + if not history: + print("No history available.") + return + for entry in history: + print(f"Number: {entry['number']}, Time: {entry['timestamp']}") + print(f"Country: {entry['country']}, Location: {entry['detailed_location']}") + print(f"Provider: {entry['service_provider']}, Time Zone: {entry['time_zone']}") + print(f"Coordinates: ({entry['latitude']}, {entry['longitude']})\n") + +def view_history_map(): + if not history: + print("No history available to map.") + return + # Create a map centered on the first historical location + first_entry = history[0] + myMap = folium.Map(location=[first_entry["latitude"], first_entry["longitude"]], zoom_start=3) + for entry in history: + folium.Marker( + [entry["latitude"], entry["longitude"]], + popup=f"{entry['number']} ({entry['detailed_location']}, {entry['country']})", + icon=folium.Icon(color="blue") + ).add_to(myMap) + myMap.save("history_map.html") + print("History map saved as history_map.html") + webbrowser.open("history_map.html") + +def clear_history(): + global history + history = [] + with open(history_file, "w") as f: + json.dump(history, f, indent=4) + print("History cleared.") + +def export_to_csv(number, location, detailed_location, service_provider, time_zone, lat, lng): + filename = "phone_lookup_export.csv" + file_exists = os.path.isfile(filename) + with open(filename, "a", newline="") as f: + writer = csv.writer(f) + if not file_exists: + writer.writerow(["Phone Number", "Country", "Detailed Location", "Service Provider", "Time Zone", "Latitude", "Longitude", "Timestamp"]) + writer.writerow([number, location, detailed_location if detailed_location else "N/A", + service_provider if service_provider else "N/A", str(time_zone) if time_zone else "N/A", + lat, lng, datetime.now().isoformat()]) + +# Get phone number from user +while True: + number = input("Enter phone number (with country code, e.g., +1234567890): ").strip() + if not number.startswith("+"): + print("Phone number must include country code (e.g., +1 for USA).") + continue + try: + pepnumber = phonenumbers.parse(number) + if not phonenumbers.is_valid_number(pepnumber): + print("Invalid phone number. Please try again.") + continue + break + except phonenumbers.NumberParseException: + print("Error parsing phone number. Ensure it includes a valid country code.") + continue -# Get country name and detailed location (e.g., city or region) +# Get location details location = geocoder.country_name_for_number(pepnumber, "en") detailed_location = geocoder.description_for_number(pepnumber, "en") - -# Get service provider and time zone service_provider = carrier.name_for_number(pepnumber, "en") time_zone = timezone.time_zones_for_number(pepnumber) -# Set up OpenCage Geocoder -geocoder_api = OpenCageGeocode(key) - -# Build query: prefer detailed location with country for accuracy +# Build geocoding query query = detailed_location if detailed_location else location if detailed_location and location: query = f"{detailed_location}, {location}" -# Geocode the location -results = geocoder_api.geocode(query) - -if results and len(results): - lat = results[0]['geometry']['lat'] - lng = results[0]['geometry']['lng'] - - # Print details - print(f"Phone Number: {number}") - print(f"Country and Location: {location} in {detailed_location if detailed_location else 'Not available'}") - print(f"Service Provider: {service_provider}") - print(f"Time Zone: {time_zone}") - print(f"Latitude and Longitude: {lat}, {lng}") - print(f"Exact Location: https://www.google.com/maps/place/{lat},{lng}") - - # Create map centered on coordinates and adjust zoom - myMap = folium.Map(location=[lat, lng], zoom_start=9) - folium.Marker([lat, lng], popup=query).add_to(myMap) # Add marker for location - - # Save map to HTML file - myMap.save("mylocation.html") - print("Map saved!") +# Check cache for geocoding result +if query in cache: + lat, lng = cache[query]["lat"], cache[query]["lng"] + confidence = cache[query].get("confidence", "N/A") + print("Using cached coordinates.") else: - print("Geocoding failed, No coordinates") \ No newline at end of file + try: + geocoder_api = OpenCageGeocode(key) + results = geocoder_api.geocode(query) + if results and len(results): + lat = results[0]["geometry"]["lat"] + lng = results[0]["geometry"]["lng"] + confidence = results[0].get("confidence", "N/A") + cache[query] = {"lat": lat, "lng": lng, "confidence": confidence} + save_cache() + else: + print("Geocoding failed, no coordinates found.") + exit() + except Exception as e: + print(f"Geocoding error: {str(e)}") + exit() + +# Save to history and export +save_history(number, location, detailed_location, service_provider, time_zone, lat, lng) +export_to_csv(number, location, detailed_location, service_provider, time_zone, lat, lng) + +# Print details +print(f"\nPhone Number: {number}") +print(f"Country and Location: {location} in {detailed_location if detailed_location else 'Not available'}") +print(f"Service Provider: {service_provider if service_provider else 'Unknown'}") +print(f"Time Zone: {time_zone if time_zone else 'Unknown'}") +print(f"Latitude and Longitude: {lat}, {lng}") +print(f"Geocoding Confidence: {confidence}") +print(f"Exact Location: https://www.google.com/maps/place/{lat},{lng}") + +# Create and save map with style option +map_style = input("Choose map style (1: Standard, 2: Satellite, 3: Terrain): ").strip() +tiles = "OpenStreetMap" +if map_style == "2": + tiles = "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" +elif map_style == "3": + tiles = "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}" + +zoom_level = 9 if detailed_location else 5 +myMap = folium.Map(location=[lat, lng], zoom_start=zoom_level, tiles=tiles, attr="Map data © OpenStreetMap contributors, Esri") +folium.Marker([lat, lng], popup=query).add_to(myMap) +myMap.save("mylocation.html") +print("Map saved as mylocation.html") + +# Open map in browser +webbrowser.open("mylocation.html") + +# History options +while True: + action = input("\nOptions: [v]iew history, [m]ap history, [c]lear history, [e]xit: ").lower() + if action == "v": + view_history() + elif action == "m": + view_history_map() + elif action == "c": + clear_history() + elif action == "e": + break + else: + print("Invalid option.") \ No newline at end of file diff --git a/mylocation.html b/mylocation.html index 3b668f2..023f66b 100644 --- a/mylocation.html +++ b/mylocation.html @@ -17,7 +17,7 @@