-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathexport_locations.py
More file actions
178 lines (143 loc) · 5.69 KB
/
export_locations.py
File metadata and controls
178 lines (143 loc) · 5.69 KB
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env python3
"""
Export Locations - Standalone Script
Reads a CSV file with router IDs, retrieves location data from NCM API v2,
and adds/updates location columns in the same CSV file.
Usage:
Double-click on Windows, or run: python export_locations.py
Optionally pass a CSV filename: python export_locations.py my_routers.csv
Configuration:
Set your API keys below, or as environment variables.
"""
import os
import sys
import csv
script_dir = os.path.dirname(os.path.abspath(__file__))
try:
import ncm
except ImportError:
print("Error: 'ncm' library not found. Install it with: pip install ncm")
input("Press Enter to exit...")
sys.exit(1)
# ============================================================================
# CONFIGURATION - Set your API keys here
# ============================================================================
api_keys = {
"X-CP-API-ID": "",
"X-CP-API-KEY": "",
"X-ECM-API-ID": "",
"X-ECM-API-KEY": "",
}
CSV_FILENAME = "router_grid.csv" # Default CSV filename
# ============================================================================
def load_api_keys():
"""Load API keys from config or environment variables."""
keys = {}
env_map = {
"X-CP-API-ID": "X_CP_API_ID",
"X-CP-API-KEY": "X_CP_API_KEY",
"X-ECM-API-ID": "X_ECM_API_ID",
"X-ECM-API-KEY": "X_ECM_API_KEY",
}
for header, env_var in env_map.items():
val = api_keys.get(header, "") or os.environ.get(env_var, "")
if val:
keys[header] = val
return keys
def find_id_column(fieldnames):
"""Find the router ID column from common variants."""
candidates = ["id", "router", "routerid", "router id", "router_id"]
# Strip BOM, whitespace, underscores, and spaces for matching
normalized = {
col.lstrip('\ufeff').lower().strip().replace(" ", "").replace("_", ""): col
for col in fieldnames
}
for name in candidates:
key = name.lower().strip().replace(" ", "").replace("_", "")
if key in normalized:
return normalized[key]
return None
def extract_router_id(value):
"""Extract router ID from a value that might be a URL or plain ID."""
value = str(value).strip()
if "/" in value:
return value.rstrip("/").split("/")[-1]
return value
def main():
# Resolve CSV path relative to the script's own directory
csv_filename = sys.argv[1] if len(sys.argv) > 1 else CSV_FILENAME
filepath = os.path.join(script_dir, csv_filename) if not os.path.isabs(csv_filename) else csv_filename
if not os.path.exists(filepath):
print(f"Error: File not found: {filepath}")
input("Press Enter to exit...")
sys.exit(1)
# Load and validate API keys
keys = load_api_keys()
if not keys:
print("Error: No API keys found. Set them in the script or as environment variables:")
print(" X_CP_API_ID, X_CP_API_KEY, X_ECM_API_ID, X_ECM_API_KEY")
input("Press Enter to exit...")
sys.exit(1)
# Initialize NCM client
n2 = ncm.NcmClientv2(api_keys=keys, log_events=False)
# Read CSV
with open(filepath, "r", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
rows = list(reader)
original_fieldnames = list(reader.fieldnames)
if not rows:
print("Error: CSV file is empty.")
input("Press Enter to exit...")
sys.exit(1)
id_column = find_id_column(original_fieldnames)
if not id_column:
print(f"Error: Could not find router ID column. Available: {', '.join(original_fieldnames)}")
print("Expected one of: id, router, routerid, router id, router_id (case-insensitive)")
input("Press Enter to exit...")
sys.exit(1)
router_ids = [extract_router_id(row[id_column]) for row in rows]
print(f"Processing {len(router_ids)} routers...", flush=True)
# Fetch locations in batches with progress
batch_size = 100
locations = []
total = len(router_ids)
total_batches = (total + batch_size - 1) // batch_size
for i in range(0, total, batch_size):
batch = router_ids[i:i + batch_size]
batch_num = i // batch_size + 1
print(f"Fetching locations batch {batch_num}/{total_batches} ({len(batch)} routers)...", flush=True)
try:
batch_locations = n2.get_locations(router__in=batch)
locations.extend(batch_locations)
except Exception as e:
print(f" Warning: Batch {batch_num} failed: {e}", flush=True)
print(f"Retrieved {len(locations)} locations.", flush=True)
# Build lookup by router ID
location_map = {}
for loc in locations:
router_url = loc.get("router", "")
rid = extract_router_id(router_url) if router_url else ""
if rid:
location_map[rid] = loc
# Location columns to add/update
loc_columns = ["latitude", "longitude", "altitude_meters", "accuracy", "method"]
# Build fieldnames: add new columns only if they don't already exist
all_fieldnames = list(original_fieldnames)
for col in loc_columns:
if col not in all_fieldnames:
all_fieldnames.append(col)
# Merge location data into rows
for row in rows:
rid = extract_router_id(row[id_column])
loc = location_map.get(rid, {})
for col in loc_columns:
row[col] = loc.get(col, "")
# Write back
with open(filepath, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=all_fieldnames)
writer.writeheader()
writer.writerows(rows)
print(f"\nDone! Location columns added/updated in: {filepath}", flush=True)
input("Press Enter to exit...")
if __name__ == "__main__":
main()