source: src/django_gheat/gheat/management/commands/import_droidstumbler.py@ 9579

Last change on this file since 9579 was 9579, checked in by rick, 13 years ago

Map a SSID to an organization to play nice with differnt SSIDs.

  • Property svn:executable set to *
File size: 6.1 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Script for importing DroidStumbler .csv files, which takes the best value of
5# each measurement point.
6#
7# Original by Dennis Wagenaar <d.wagenaar@gmail.com>
8#
9# Rick van der Zwet <info@rickvanderzwet.nl>
10#
11from django.core.management import setup_environ
12from django.core.management.base import BaseCommand, CommandError
13from django.db import connection, transaction
14from django.db.utils import IntegrityError
15from _mysql_exceptions import OperationalError
16from optparse import OptionParser, make_option
17from gheat.models import *
18import csv
19import datetime
20import gzip
21import logging
22import os
23import sys
24
25logger = logging.getLogger(__name__)
26logger.setLevel(logging.DEBUG)
27
28def bulk_sql(sql_table, sql_values):
29 if len(sql_values) == 0:
30 raise ValueError, "No data to import"
31
32 cursor = connection.cursor()
33 try:
34 sql = "INSERT INTO %s VALUES %s" % (sql_table, ','.join(sql_values))
35 count = cursor.execute(sql)
36 transaction.commit_unless_managed()
37 except OperationalError, e:
38 logger.error("%s - %s ", sql_table, sql_values[0])
39 raise
40 except IntegrityError, e:
41 logger.error("Unable to import - %s" % e)
42 raise
43 return count
44
45
46def import_droidstumbler(filename, meetrondje):
47 """ Import all points, return tuple with summary"""
48
49 # Open file for reading
50 if filename.endswith('.gz'):
51 fh = gzip.open(filename,'rb')
52 else:
53 fh = open(filename,'rb')
54 csvfile = csv.reader(fh, delimiter=',')
55
56 #Various statistics
57 counters = {'meting_added' : 0, 'meting_total' : 0, 'ap_added' : 0, 'ap_total' : 0}
58
59 # Temponary holders
60 meting_pool = {}
61 ap_pool = {}
62 # Process file, preparing new access points and measurements
63 for row in csvfile:
64 try:
65 epoch, msg_type, lat, lon, accuracy, ssid, bssid, level, frequency, capabilities = row
66 bssid = bssid.upper()
67 except ValueError:
68 # Known error, please ignore
69 if row[1] == 'gps' and len(row) == 12: continue
70 logger.error("Unable to parse line:%i '%s'" % (csvfile.line_num, row))
71 continue
72 if msg_type == "data" and lat and lon:
73 counters['meting_total'] += 1
74 if not ap_pool.has_key(bssid):
75 encryption = 'WPA' in capabilities or 'WEP' in capabilities
76 ap_pool[bssid] = (ssid, encryption)
77
78 # We store the best value found
79 key = (bssid, lat, lon)
80 signaal=(100 + int(level))
81 if meting_pool.has_key(key):
82 meting_pool[key] = max(meting_pool[key], signaal)
83 else:
84 meting_pool[key] = signaal
85
86
87 # Determine which entries we need to add
88 counters['ap_total'] = len(ap_pool)
89 bssid_list_present = Accespoint.objects.filter(mac__in=ap_pool.keys()).values_list('mac', flat=True)
90 bssid_list_insert = set(ap_pool.keys()) - set(bssid_list_present)
91
92 # Create a bulk import list and import
93 if bssid_list_insert:
94 sql_values = []
95 for bssid in bssid_list_insert:
96 ssid, encryption = ap_pool[bssid]
97 # Special trick in SSID ts avoid escaping in later stage
98 item = str((bssid.upper(),ssid.replace('%','%%'),encryption,Accespoint.get_organization(ssid)))
99 sql_values.append(item)
100 counters['ap_added'] = bulk_sql('gheat_accespoint (`mac`, `ssid`, `encryptie`, `organization`)',sql_values)
101
102 # Build mapping for meting import
103 mac2id = {}
104 for mac,id in Accespoint.objects.filter(mac__in=ap_pool.keys()).values_list('mac','id'):
105 mac2id[mac] = int(id)
106
107 sql_values = []
108 for (bssid,lat,lon),signal in meting_pool.iteritems():
109 item = str((int(meetrondje.id),mac2id[bssid],float(lat),float(lon),int(signaal)))
110 sql_values.append(item)
111
112 # Bulk Import data if possible
113 if sql_values:
114 counters['meting_added'] = bulk_sql('gheat_meting (`meetrondje_id`, `accespoint_id`, `lat`, `lng`, `signaal`)',sql_values)
115 return counters
116
117
118class Command(BaseCommand):
119 args = '<csvfile>[.gz] [csvfile2[.gz] [csvfile3[.gz] ...] '
120 option_list = BaseCommand.option_list + (
121 make_option('-m', '--meetrondje', dest='meetrondje', default=None),
122 make_option('-g', '--gebruiker', dest='gebruiker', default=os.environ['USER']),
123 make_option('-e', '--email', dest='email', default=os.environ['USER'] + '@example.org'),
124 make_option('-k', '--kaart', dest='kaart', default='onbekend', help="Kaart gebruikt"),
125 make_option('-d', '--datum', dest='datum', default=None, help="Provide date \
126 in following format: %Y-%m-%d-%H%M%S, by default it will be generated from \
127 the filename"),
128 )
129
130 def handle(self, *args, **options):
131 if len(args) == 0:
132 self.print_help(sys.argv[0],sys.argv[1])
133 raise CommandError("Not all arguments are provided")
134
135 for csv_file in args:
136 # Make sure to check files before we going to do importing at all
137 if not os.path.isfile(csv_file):
138 raise CommandError("csv file '%s' does not exists" % csv_file)
139 logger.info("Processing '%s'" % csv_file)
140
141 # Meetrondje from filename if needed
142 if options['meetrondje'] == None:
143 meetrondje = os.path.basename(csv_file).rstrip('.gz').rstrip('.csv')
144 else:
145 meetrondje = options['meetrondje']
146 # Date from filename if needed
147 if options['datum'] == None:
148 datum = os.path.basename(csv_file).lstrip('ScanResult-').rstrip('.csv.gz')
149 else:
150 datum = options['datum']
151 try:
152 datum = datetime.datetime.strptime(datum,'%Y-%m-%d-%H%M%S')
153 except ValueError:
154 raise CommandError("Invalid date '%s'" % options['datum'])
155
156 # Create meetrondje object
157 g, created = Gebruiker.objects.get_or_create(naam=options['gebruiker'] , email=options['email'])
158 a, created = Apparatuur.objects.get_or_create(kaart=options['kaart'])
159 mr, created = MeetRondje.objects.get_or_create(datum=datum , naam=meetrondje , gebruiker=g , apparatuur=a)
160 logger.info('Meetrondje: %s @ %s' % (meetrondje, datum))
161 if not created:
162 logger.error("Meetrondje '%s' already imported" % mr)
163 sys.exit(1)
164 counters = import_droidstumbler(csv_file,mr)
165 logger.info("summary accespoints: added:%(ap_added)-6s processed:%(ap_total)-6s" % counters)
166 logger.info("summary metingen : added:%(meting_added)-6s processed:%(meting_total)-6s" % counters)
Note: See TracBrowser for help on using the repository browser.