f5d92f1d36
Major Features: - Multi-format GPS data import (Wigle CSV, GPS JSON, filename GPS) - Data source tracking (test/mock/production) - Database cleanup tools for managing test data - JSON file persistence for simplified server - UI improvements for map controls Backend: - Added wardriving_importer.py with WigleCSVImporter, GPSJSONImporter, BatchImporter - Added data_source and session_id tracking to captures - New API endpoints: DELETE /api/v1/admin/cleanup - Auto-save/load functionality for captures_simple.json - Updated stats endpoint to show data source breakdown CLI Tools: - scripts/import_wardriving_data.py - Batch import with --data-source flag - scripts/cleanup_database.py - Clean by source, session, or all - scripts/create_test_dataset.py - Generate GPS-tagged test data - scripts/test_wardriving_import.py - Test suite for importers Frontend: - Fixed map controls z-index and positioning issues - Moved controls to top-right to avoid zoom button overlap - Fixed Leaflet zoom controls rendering over header - Changed controls to position:fixed for persistent visibility - Export map object to window.map for proper invalidateSize Documentation: - docs/WARDRIVING_IMPORT.md - Complete import guide - docs/DATA_CLEANUP_GUIDE.md - Cleanup system documentation - docs/TEST_LOCATIONS.md - Test GPS coordinates reference - TEST_RESULTS_SUMMARY.md - Format testing results Test Data: - Created test_dataset_gps with 15 captures across 5 US cities - All test data marked with data_source="test" for easy cleanup Testing: - Verified Wigle CSV import (1 capture from West LA) - Verified GPS filename import (3 captures from UCLA area) - Verified GPS JSON companion files (15 captures, 5 cities) - Verified cleanup functionality (deleted 15 test captures) - Verified data persistence across server restarts 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
151 lines
4.1 KiB
Python
Executable File
151 lines
4.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Test Wardriving Data Import
|
|
|
|
Tests importing GPS-tagged SubGhz captures from various formats
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add project root to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from src.parser.wardriving_importer import (
|
|
WigleCSVImporter,
|
|
GPSJSONImporter,
|
|
BatchImporter
|
|
)
|
|
|
|
|
|
def test_wigle_csv_import():
|
|
"""Test Wigle CSV format import"""
|
|
print("=" * 80)
|
|
print("TEST 1: Wigle CSV Import")
|
|
print("=" * 80)
|
|
|
|
csv_path = "signatures/t-embed-rf/test_export_wigle.csv"
|
|
|
|
if not Path(csv_path).exists():
|
|
print(f"❌ CSV file not found: {csv_path}")
|
|
return
|
|
|
|
importer = WigleCSVImporter()
|
|
captures = importer.parse_csv(csv_path)
|
|
|
|
print(f"\n✅ Imported {len(captures)} captures from CSV")
|
|
|
|
for i, capture in enumerate(captures, 1):
|
|
print(f"\nCapture {i}:")
|
|
print(f" Filename: {capture.filename}")
|
|
print(f" Frequency: {capture.frequency / 1e6:.2f} MHz")
|
|
print(f" Protocol: {capture.protocol}")
|
|
print(f" GPS: {capture.latitude}, {capture.longitude}")
|
|
print(f" Accuracy: {capture.accuracy}m")
|
|
print(f" Timestamp: {capture.timestamp}")
|
|
print(f" GPS Source: {capture.gps_source}")
|
|
|
|
|
|
def test_gps_json_import():
|
|
"""Test GPS JSON format import"""
|
|
print("\n" + "=" * 80)
|
|
print("TEST 2: GPS JSON Import")
|
|
print("=" * 80)
|
|
|
|
json_files = list(Path("signatures/t-embed-rf").glob("gps_coordinates_*.json"))
|
|
|
|
if not json_files:
|
|
print("❌ No GPS JSON files found")
|
|
return
|
|
|
|
print(f"\nFound {len(json_files)} GPS JSON files")
|
|
|
|
for json_file in json_files[:3]: # Test first 3
|
|
coords = GPSJSONImporter.parse_json(str(json_file))
|
|
|
|
if coords:
|
|
print(f"\n✅ {json_file.name}")
|
|
print(f" Latitude: {coords.latitude}")
|
|
print(f" Longitude: {coords.longitude}")
|
|
print(f" Accuracy: {coords.accuracy}m")
|
|
print(f" Source: {coords.source}")
|
|
else:
|
|
print(f"\n❌ {json_file.name} - No GPS data")
|
|
|
|
|
|
def test_batch_import():
|
|
"""Test batch directory import"""
|
|
print("\n" + "=" * 80)
|
|
print("TEST 3: Batch Directory Import")
|
|
print("=" * 80)
|
|
|
|
importer = BatchImporter()
|
|
captures = importer.import_from_directory("signatures/t-embed-rf")
|
|
|
|
print(f"\n✅ Imported {len(captures)} total captures")
|
|
|
|
# Group by GPS source
|
|
print("\nGPS Sources:")
|
|
sources = {}
|
|
for capture in captures:
|
|
sources[capture.gps_source] = sources.get(capture.gps_source, 0) + 1
|
|
|
|
for source, count in sorted(sources.items()):
|
|
print(f" {source}: {count}")
|
|
|
|
# Show sample captures
|
|
print("\nSample Captures:")
|
|
for i, capture in enumerate(captures[:5], 1):
|
|
print(f"\n {i}. {capture.filename}")
|
|
print(f" Frequency: {capture.frequency / 1e6:.2f} MHz")
|
|
print(f" GPS: {capture.latitude}, {capture.longitude}")
|
|
print(f" Source: {capture.gps_source}")
|
|
|
|
|
|
def test_upload_manifest():
|
|
"""Test generating upload manifest"""
|
|
print("\n" + "=" * 80)
|
|
print("TEST 4: Upload Manifest Generation")
|
|
print("=" * 80)
|
|
|
|
importer = BatchImporter()
|
|
captures = importer.import_from_directory("signatures/t-embed-rf")
|
|
|
|
manifest = importer.to_upload_manifest()
|
|
|
|
print(f"\n✅ Generated manifest with {len(manifest['captures'])} captures")
|
|
print(f"Session UUID: {manifest['session_uuid']}")
|
|
|
|
print("\nSample capture data:")
|
|
if manifest['captures']:
|
|
import json
|
|
print(json.dumps(manifest['captures'][0], indent=2))
|
|
|
|
|
|
def main():
|
|
"""Run all tests"""
|
|
print("\n🧪 WARDRIVING DATA IMPORT TESTS")
|
|
print("=" * 80)
|
|
|
|
try:
|
|
test_wigle_csv_import()
|
|
test_gps_json_import()
|
|
test_batch_import()
|
|
test_upload_manifest()
|
|
|
|
print("\n" + "=" * 80)
|
|
print("✅ ALL TESTS COMPLETE")
|
|
print("=" * 80)
|
|
|
|
except Exception as e:
|
|
print(f"\n❌ ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|