15
15
# You should have received a copy of the GNU General Public License
16
16
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18
# Author: Daniel Fett simplecaching@fragcom.de
18
# Author: Daniel Fett agtl@danielfett.de
19
# Jabber: fett.daniel@jaber.ccc.de
20
# Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching
21
usage = r'''Here's how to use this app:
23
If you want to use the gui:
25
Simple User Interface, for mobile devices such as the Openmoko Freerunner
27
Full User Interface, for desktop usage (not implemented yet)
29
If you don't like your mouse:
30
%(name)s set [options]
31
Change the configuration.
32
%(name)s import [importactions]
33
Fetch geocaches from geocaching.com and write to the internal database.
34
%(name)s import [importactions] do [actions]
35
Fetch geocaches from geocaching.com, put them into the internal database and do whatever actions are listed.
36
%(name)s filter [filter-options] do [actions]
37
Query the internal database for geocaches and do the desired actions.
38
%(name)s import [importactions] filter [filter-options] do [actions]
39
Import geocaches, put them into the internal database, filter the imported geocaches and run the actions.
40
%(name)s sql "SELECT * FROM geocaches WHERE ... ORDER BY ... LIMIT ..." do [actions]
41
Select geocaches from local database and run the actions afterwards. Additional use of the filter is also supported. To get more information, run "%(name)s sql".
45
Your geocaching.com login data.
48
Fetches the index of geocaches between the given coordinates.
49
These are interpreted as the corners of a rectangle. All caches
50
within the rectangle are retrieved. No details are retrieved.
51
--around coord radius-in-km
52
Fetches the index of geocaches at the given coordinate and radius
53
kilometers around it. No details are retrieved.
54
--at-route coord1 coord2 radius-in-km
55
Find caches along the route from coord1 to coord2. Uses OpenRouteService
56
and is not available for routes outside of europe.
60
--around coord1 radius-in-km
64
Filter out geocaches which have (not) been found by the user.
66
caches which have full detail information available
68
-s|--size (min|max) 1..4|micro|small|regular|huge|other
69
Specify a minimum or maximum size. If min/max is not given, show
70
only geocaches with the given size.
71
-d|--difficulty (min|max) 1.0..5.0
72
-t|--terrain (min|max) 1.0..5.0
73
Filter out geocaches by difficulty or terrain.
74
-T|--type type,type,...
75
type: virtual|regular|unknown|multi|event
76
Only show geocaches of the given type(s)
77
-o|--owner owner-search-string
78
-n|--name name-search-string
79
-i|--id id-search-string
80
Search owner, name (title) or id of the geocaches.
82
Caches which were downloaded in current session. Useful to
83
get alerted when new caches arrive.
86
Default action, prints tab-separated list of geocaches
88
Downloads Descriptions etc. for selected geocaches
90
Dumps HTML pages to given folder
92
Runs command if more than one geocache has survived the filtering.
93
The placeholder %%s is replaced by a shell-escaped list of geocaches.
97
Dumps geocaches into separate GPX files
98
--export-single-gpx file
99
Dumps selected geocaches into a single GPX file
101
Draws one big JPEG file with the positions of the selected geocaches
102
--draw-maps zoom folder [tiles]
103
Draws a small JPEG image for every geocache.
105
Preferred format for coordinates:
106
'N49 44.111 E6 29.123'
108
'N49.123456 E6.043212'
110
Instead of a coordinate, you may also query geonames.com for a place name.
111
Just start the string with 'q:':
113
'q:Brisbane, Australia'
23
from __future__ import with_statement
29
logging.basicConfig(level=logging.WARNING,
30
format='%(relativeCreated)6d %(levelname)10s %(name)-20s %(message)s',
119
33
from geo import Coordinate
35
from json import loads, dumps
36
except (ImportError, AttributeError):
37
from simplejson import loads, dumps
38
from sys import argv, exit
39
from sys import path as sys_path
45
from os import path, mkdir, extsep, remove, walk
135
if len(sys.argv) == 1:
136
print usage % ({'name' : sys.argv[0]})
47
from threading import Thread
48
import cachedownloader
49
import fieldnotesuploader
50
from actors.tts import TTS
51
#from actors.notify import Notify
55
print cli.usage % ({'name': argv[0]})
139
arg = sys.argv[1].strip()
140
if arg == '--simple':
60
import logging.handlers
61
logging.getLogger('').setLevel(logging.DEBUG)
62
logging.debug("Set log level to DEBUG")
64
if '--simple' in argv:
142
66
gui = simplegui.SimpleGui
143
elif arg == '--desktop':
67
elif '--desktop' in argv:
145
69
gui = biggui.BigGui
73
elif '--hildon' in argv:
75
gui = hildongui.HildonGui
151
class Standbypreventer():
157
self.requested_status = self.STATUS_NONE
160
self.__unrequest_all()
162
def set_status(self, status):
163
if status != self.requested_status:
164
self.__unrequest_all()
165
self.__request(status)
167
def __unrequest_all(self):
168
if self.requested_status == self.STATUS_ALIVE:
169
self.__try_run('dbus-send --system --type=method_call --dest=org.shr.ophonekitd.Usage /org/shr/ophonekitd/Usage org.shr.ophonekitd.Usage.ReleaseResource string:CPU')
170
elif self.requested_status == self.STATUS_SCREEN_ON:
171
self.__try_run('dbus-send --system --type=method_call --dest=org.shr.ophonekitd.Usage /org/shr/ophonekitd/Usage org.shr.ophonekitd.Usage.ReleaseResource string:Display')
173
def __request(self, status):
174
if status == self.STATUS_ALIVE:
175
self.__try_run('dbus-send --system --type=method_call --dest=org.shr.ophonekitd.Usage /org/shr/ophonekitd/Usage org.shr.ophonekitd.Usage.RequestResource string:CPU')
176
elif status == self.STATUS_SCREEN_ON:
177
self.__try_run('dbus-send --system --type=method_call --dest=org.shr.ophonekitd.Usage /org/shr/ophonekitd/Usage org.shr.ophonekitd.Usage.RequestResource string:Display')
179
def __try_run(self, command):
182
except Exception as e:
183
print "Could not prevent Standby: %s" % e
186
SETTINGS_DIR = os.path.expanduser('~/.agtl')
187
CACHES_DB = os.path.join(SETTINGS_DIR, "caches.db")
188
COOKIE_FILE = os.path.join(SETTINGS_DIR, "cookies.lwp")
80
logger = logging.getLogger('core')
82
class Core(gobject.GObject):
85
'map-marks-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
86
'cache-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )),
87
'fieldnotes-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
88
'good-fix': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
89
'no-fix': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
90
'target-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
91
'settings-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, )),
92
'save-settings': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
93
'error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )),
94
'progress': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, )),
95
'hide-progress': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
98
SETTINGS_DIR = path.expanduser(path.join('~', '.agtl'))
99
CACHES_DB = path.join(SETTINGS_DIR, "caches.db")
100
COOKIE_FILE = path.join(SETTINGS_DIR, "cookies.lwp")
101
UPDATE_DIR = path.join(SETTINGS_DIR, 'updates')
103
MAEMO_HOME = path.expanduser(path.join('~', 'MyDocs', '.'))
104
MAPS_DIR = path.join('Maps', '')
106
DATA_DIR = path.expanduser(path.join('~', '')) if not path.exists(MAEMO_HOME) else MAEMO_HOME
108
UPDATE_MODULES = [cachedownloader, fieldnotesuploader]
190
110
DEFAULT_SETTINGS = {
191
111
'download_visible': True,
192
112
'download_notfound': True,
193
113
'download_new': True,
194
114
'download_nothing': False,
195
'download_create_index': True,
115
'download_create_index': False,
196
116
'download_run_after': False,
197
117
'download_run_after_string': '',
198
'download_output_dir': os.path.expanduser('~/caches/'),
118
'download_output_dir': path.expanduser(path.join(DATA_DIR, 'geocaches', '')),
199
119
'map_position_lat': 49.7540,
200
120
'map_position_lon': 6.66135,
121
'map_follow_position': True,
202
123
'download_resize': True,
203
124
'download_resize_pixel': 400,
204
125
'options_show_name': True,
205
'options_username': "Username",
206
'options_password': "Pass",
126
'options_username': "",
127
'options_password': "",
207
128
'last_target_lat': 50,
208
129
'last_target_lon': 10,
209
130
'last_target_name': 'default',
131
'last_selected_geocache': '',
210
132
'download_noimages': False,
211
'download_map_path': os.path.expanduser('~/Maps/OSM/'),
212
'options_hide_found': False
133
'download_map_path': DATA_DIR + MAPS_DIR,
134
'options_hide_found': False,
135
'options_show_error': True,
136
'options_show_html_description': False,
138
('OpenStreetMaps', {'remote_url': "http://tile.openstreetmap.org/mapnik/%(zoom)d/%(x)d/%(y)d.png", 'prefix': 'OpenStreetMap I'}),
139
('OpenCycleMaps', {'remote_url': 'http://andy.sandbox.cloudmade.com/tiles/cycle/%(zoom)d/%(x)d/%(y)d.png', 'prefix': 'OpenCycleMap'})
142
'options_map_double_size': False,
143
'options_rotate_screen': 0,
145
'options_default_log_text' : 'TFTC!\n\nLogged at %X from my %(machine)s using AGTL.',
215
148
def __init__(self, guitype, root):
216
if not os.path.exists(self.SETTINGS_DIR):
217
os.mkdir(self.SETTINGS_DIR)
219
dataroot = os.path.join(root, 'data')
221
#self.standbypreventer = Standbypreventer()
222
# seems to crash dbus/fso/whatever
149
gobject.GObject.__init__(self)
150
self.current_target = None
151
self.current_position = None
152
self.create_recursive(self.SETTINGS_DIR)
154
self.dataroot = path.join(root, 'data')
156
self._install_updates()
224
158
self.__read_config()
226
#self.standbypreventer.set_status(Standbypreventer.STATUS_SCREEN_ON)
228
self.downloader = downloader.FileDownloader(self.settings['options_username'], self.settings['options_password'], self.COOKIE_FILE)
230
#pointprovider = LiveCacheProvider()
231
self.pointprovider = provider.PointProvider(self.CACHES_DB, self.downloader, geocaching.GeocacheCoordinate, 'geocaches')
232
#self.userpointprovider = provider.PointProvider("%s/caches.db" % self.SETTINGS_DIR, self.downloader, geo.Coordinate, 'userpoints')
233
self.userpointprovider = None
234
#pointprovider = PointProvider(':memory:', self.downloader)
235
#reader = GpxReader(pointprovider)
236
#reader.read_file('../../file.loc')
159
self.connect('settings-changed', self.__on_settings_changed)
160
self.connect('save-settings', self.__on_save_settings)
161
self.create_recursive(self.settings['download_output_dir'])
162
self.create_recursive(self.settings['download_map_path'])
165
self.downloader = downloader.FileDownloader(self.settings['options_username'], self.settings['options_password'], self.COOKIE_FILE, cachedownloader.GeocachingComCacheDownloader.login_callback)
167
self.pointprovider = provider.PointProvider(self.CACHES_DB, geocaching.GeocacheCoordinate, 'geocaches')
169
self.gui = guitype(self, self.dataroot)
238
self.gui = guitype(self, self.pointprovider, self.userpointprovider, dataroot)
239
self.gui.write_settings(self.settings)
240
if 'gpsprovider' in self.gui.USES:
241
self.gps_thread = gpsreader.GpsReader(self)
172
actor_tts = TTS(self)
173
actor_tts.connect('error', lambda caller, msg: self.emit('error', msg))
174
#actor_notify = Notify(self)
175
self.emit('settings-changed', self.settings, self)
176
self.emit('fieldnotes-changed')
180
self.gps_thread = gpsreader.FakeGpsReader(self)
181
gobject.timeout_add(1000, self.__read_gps)
182
self.set_target(gpsreader.FakeGpsReader.get_target())
183
elif 'gpsprovider' in self.gui.USES:
184
self.gps_thread = gpsreader.GpsReader()
242
185
#self.gps_thread = gpsreader.FakeGpsReader(self)
243
186
gobject.timeout_add(1000, self.__read_gps)
187
elif 'locationgpsprovider' in self.gui.USES:
188
self.gps_thread = gpsreader.LocationGpsReader(self.__read_gps_cb_error, self.__read_gps_cb_changed)
189
gobject.idle_add(self.gps_thread.start)
245
190
if 'geonames' in self.gui.USES:
247
192
self.geonames = geonames.Geonames(self.downloader)
194
if '--startup-only' in argv:
199
if not '--profile' in argv:
202
##############################################
206
##############################################
209
def create_recursive(dpath):
211
if not path.exists(dpath):
212
head, tail = path.split(dpath)
213
Core.create_recursive(head)
217
logging.info("Could not create directory; %s" % e)
220
def optimize_data(self):
221
self.pointprovider.push_filter()
222
self.pointprovider.set_filter(found=True)
223
old_geocaches = self.pointprovider.get_points_filter()
224
self.pointprovider.pop_filter()
225
for x in old_geocaches:
226
images = x.get_images()
229
for filename, caption in images.items():
230
fullpath = path.join(self.settings['download_output_dir'], filename)
234
logging.warning("Could not remove " + fullpath)
235
self.pointprovider.remove_geocaches(old_geocaches)
236
self.pointprovider.optimize()
238
def get_file_sizes(self):
239
folder = self.settings['download_output_dir']
241
for (p, dirs, files) in walk(folder):
243
filename = path.join(p, file)
244
folder_size += path.getsize(filename)
245
sqlite_size = path.getsize(self.CACHES_DB)
246
return {'images': folder_size, 'sqlite': sqlite_size}
249
def format_file_size(size):
252
elif size < 1024 * 1024:
253
return "%d KiB" % (size / 1024)
254
elif size < 1024 * 1024 * 1024:
255
return "%d MiB" % (size / (1024 * 1024))
257
return "%d GiB" % (size / (1024 * 1024 * 1024))
260
##############################################
264
##############################################
266
def _install_updates(self):
268
if path.exists(self.UPDATE_DIR):
269
sys_path.insert(0, self.UPDATE_DIR)
270
for m in self.UPDATE_MODULES:
271
modulefile = path.join(self.UPDATE_DIR, "%s%spy" % (m.__name__, extsep))
272
if path.exists(modulefile):
273
v_dict = {'VERSION': -1}
274
with open(modulefile) as f:
276
if line.startswith('VERSION'):
279
if v_dict['VERSION'] > m.VERSION:
280
pass#logging.info("Reloading Module '%s', current version number: %d, new version number: %d" % (m.__name__, v_dict['VERSION'], m.VERSION))
284
logging.info("Not reloading Module '%s', current version number: %d, version number of update file: %d" % (m.__name__, v_dict['VERSION'], m.VERSION))
286
logging.info("Skipping nonexistant update from" + path.join(self.UPDATE_DIR, "%s%spy" % (m.__name__, extsep)))
287
return updated_modules
289
def try_update(self):
290
from urllib import urlretrieve
291
from urllib2 import HTTPError
294
from shutil import copyfile
295
self.create_recursive(self.UPDATE_DIR)
296
baseurl = 'http://www.danielfett.de/files/agtl-updates/%s' % VERSION
297
url = "%s/updates" % baseurl
298
self.emit('progress', 0.5, "Checking for updates...")
301
reader = self.downloader.get_reader(url, login=False)
304
raise Exception("No updates available.")
307
raise Exception("Could not connect to update server.")
312
md5, name = line.strip().split(' ')
313
handle, temp = tempfile.mkstemp()
314
files.append((md5, name, temp))
317
raise Exception("No updates were found. (Could not process index file.)")
320
raise Exception("No updates available.")
322
for md5sum, name, temp in files:
323
url = '%s/%s' % (baseurl, name)
325
urlretrieve(url, temp)
328
raise Exception("Could not download file '%s'" % name)
330
hash = hashlib.md5(open(temp).read()).hexdigest()
332
raise Exception("There was an error downloading the file. (MD5 sum mismatch in %s)" % name)
334
for md5sum, name, temp in files:
335
file = path.join(self.UPDATE_DIR, name)
340
raise Exception("The update process was stopped while copying files. AGTL may run or not. If not, delete all *.py files in %s." % self.UPDATE_DIR)
348
def same_thread(error):
349
self.emit('hide-progress')
350
self.emit('error', error)
352
gobject.idle_add(same_thread, e)
356
self.emit('hide-progress')
358
gobject.idle_add(same_thread)
359
return self._install_updates()
363
##############################################
367
##############################################
370
This should be called to update the settings throughout all components.
372
def save_settings(self, settings, source):
373
logger.debug("Got settings update from %s" % source)
375
self.settings.update(settings)
376
self.emit('settings-changed', settings, source)
379
This is called when settings have changed.
381
def __on_settings_changed(self, caller, settings, source):
382
logger.debug("Settings where changed by %s." % source)
385
if 'options_username' in settings and 'options_password' in settings:
386
self.downloader.update_userdata(settings['options_username'], settings['options_password'])
389
This is called when settings shall be saved, calling save_settings afterwards.
391
def __on_save_settings(self, caller):
392
logger.debug("Assembling update for settings, on behalf of %s" % caller)
394
'last_target_lat': self.current_target.lat,
395
'last_target_lon': self.current_target.lon
397
caller.save_settings(settings, self)
253
399
def __del__(self):
254
self.settings = self.gui.read_settings()
255
self.__write_config()
400
logger.debug("Somebody is trying to kill me, saving the settings.")
401
self.emit('save-settings')
402
self.__write_config()
405
def on_destroy(self):
406
logger.debug("Somebody is being killed, saving the settings.")
407
self.emit('save-settings')
408
self.__write_config()
411
#def on_config_changed(self, new_settings):
412
# self.settings = new_settings
413
# self.downloader.update_userdata(self.settings['options_username'], self.settings['options_password'])
414
# self.__write_config()
418
def __read_config(self):
419
filename = path.join(self.SETTINGS_DIR, 'config')
420
logger.debug("Loading config from %s" % filename)
421
if not path.exists(filename):
422
self.settings = self.DEFAULT_SETTINGS
424
f = file(filename, 'r')
428
tmp_settings = loads(string)
429
for k, v in self.DEFAULT_SETTINGS.items():
430
if k in tmp_settings != None:
431
self.settings[k] = tmp_settings[k]
435
self.settings = self.DEFAULT_SETTINGS
438
def __write_config(self):
439
filename = path.join(self.SETTINGS_DIR, 'config')
440
logger.debug("Writing settings to %s" % filename)
441
f = file(filename, 'w')
442
config = dumps(self.settings, sort_keys=True, indent=4)
446
##############################################
450
##############################################
453
def set_target(self, coordinate):
454
self.current_target = coordinate
455
distance, bearing = self.__get_target_distance_bearing()
456
self.emit('target-changed', coordinate, distance, bearing)
457
self.emit('map-marks-changed')
460
def __get_target_distance_bearing(self):
461
if self.current_position != None and self.current_target != None:
462
distance = self.current_position.distance_to(self.current_target)
463
bearing = self.current_position.bearing_to(self.current_target)
467
return distance, bearing
469
def __read_gps(self):
470
fix = self.gps_thread.get_data()
472
if fix.position != None:
473
self.current_position = fix.position
474
distance, bearing = self.__get_target_distance_bearing()
475
self.emit('good-fix', fix, distance, bearing)
477
self.emit('no-fix', fix, self.gps_thread.status)
480
def __read_gps_cb_error(self, control, error):
481
fix = gpsreader.Fix()
482
msg = gpsreader.LocationGpsReader.get_error_from_code(error)
483
self.emit('no-fix', fix, msg)
486
def __read_gps_cb_changed(self, device):
487
fix = self.gps_thread.fix_from_tuple(device.fix, device)
488
# @type fix gpsreader.Fix
490
if fix.position != None:
491
self.current_position = fix.position
492
distance, bearing = self.__get_target_distance_bearing()
493
self.emit('good-fix', fix, distance, bearing)
495
self.emit('no-fix', fix, 'No Fix')
499
##############################################
503
##############################################
257
505
def get_coord_by_name(self, query):
258
506
return self.geonames.search(query)
510
def search_place(self, search):
511
return self.geonames.search_all(search)
260
513
def get_route(self, c1, c2, r):
261
514
c1 = self.geonames.find_nearest_intersection(c1)
262
515
c2 = self.geonames.find_nearest_intersection(c2)
269
522
if len(together) == 0:
270
523
together = [route[i]]
271
524
if (i < len(route) - 1):
272
brg = route[i].bearing_to(route[i+1])
525
brg = route[i].bearing_to(route[i + 1])
274
527
if len(together) < MAX_TOGETHER \
275
528
and (i < len(route) - 1) \
276
529
and (abs(brg - 90) < TOL
277
or abs(brg + 90) < TOL
279
or abs (brg - 180) < TOL) \
280
and route[i].distance_to(route[i+1]) < (r*1000*2):
281
together.append(route[i+1])
530
or abs(brg + 90) < TOL
532
or abs (brg - 180) < TOL) \
533
and route[i].distance_to(route[i + 1]) < (r * 1000 * 2):
534
together.append(route[i + 1])
536
from math import sqrt
283
537
min_lat = min([x.lat for x in together])
284
538
min_lon = min([x.lon for x in together])
285
539
max_lat = max([x.lat for x in together])
286
540
max_lon = max([x.lon for x in together])
287
541
c1 = Coordinate(min_lat, min_lon)
288
542
c2 = Coordinate(max_lat, max_lon)
289
new_c1 = c1.transform(-45, r * 1000 * math.sqrt(2))
290
new_c2 = c2.transform(-45 + 180, r * 1000 * math.sqrt(2))
543
new_c1 = c1.transform(-45, r * 1000 * sqrt(2))
544
new_c2 = c2.transform(-45 + 180, r * 1000 * sqrt(2))
291
545
out.append((new_c1, new_c2))
293
print "* Needing %d unique queries" % len(out)
547
logging.info("Needing %d unique queries" % len(out))
550
##############################################
554
##############################################
297
557
def on_cache_selected(self, cache):
298
558
self.gui.show_cache(cache)
560
##############################################
562
# Filters, Searching & Pointprovider
564
##############################################
301
567
def on_start_search_simple(self, text):
302
568
#m = re.search(r'/([NS]?)\s*(\d{1,2})\.(\d{1,2})\D+(\d+)\s*([WE]?)\s*(\d{1,3})\.(\d{1,2})\D+(\d+)', text, re.I)
304
570
self.__try_show_cache_by_search('%' + text + '%')
307
def on_start_search_advanced(self, found=None, owner_search='', name_search='', size=None, terrain=None, diff=None, ctype=None, location = None, marked = None):
310
self.pointprovider.set_filter(found=found, owner_search=owner_search, name_search=name_search, size=size, terrain=terrain, diff=diff, ctype=ctype, marked = marked)
573
def set_filter(self, found=None, owner_search='', name_search='', size=None, terrain=None, diff=None, ctype=None, location=None, marked=None):
574
self.pointprovider.set_filter(found=found, owner_search=owner_search, name_search=name_search, size=size, terrain=terrain, diff=diff, ctype=ctype, marked=marked)
575
self.emit('map-marks-changed')
578
def reset_filter(self):
579
self.pointprovider.set_filter()
580
self.emit('map-marks-changed')
583
def on_start_search_advanced(self, found=None, owner_search='', name_search='', size=None, terrain=None, diff=None, ctype=None, location=None, marked=None):
584
self.pointprovider.set_filter(found=found, owner_search=owner_search, name_search=name_search, size=size, terrain=terrain, diff=diff, ctype=ctype, marked=marked)
311
585
points = self.pointprovider.get_points_filter(location)
312
586
self.gui.display_results_advanced(points)
316
def on_destroy(self):
317
self.settings = self.gui.read_settings()
318
self.__write_config()
321
def on_download(self, location):
322
self.gui.set_download_progress(0.5, "Downloading...")
323
cd = geocaching.CacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages'])
325
caches = cd.get_geocaches(location)
326
except Exception as e:
327
self.gui.show_error(e)
588
def get_points_filter(self, found=None, owner_search='', name_search='', size=None, terrain=None, diff=None, ctype=None, location=None, marked=None):
589
self.pointprovider.push_filter()
590
self.pointprovider.set_filter(found=found, owner_search=owner_search, name_search=name_search, size=size, terrain=terrain, diff=diff, ctype=ctype, marked=marked)
591
points = self.pointprovider.get_points_filter(location)
592
truncated = (len(points) >= self.pointprovider.MAX_RESULTS)
593
self.pointprovider.pop_filter()
594
return (points, truncated)
597
def get_geocache_by_name(self, name):
598
return self.pointprovider.get_by_name(name)
600
def __try_show_cache_by_search(self, idstring):
601
cache = self.pointprovider.find_by_string(idstring)
603
self.gui.show_cache(cache)
604
self.gui.set_center(cache)
608
##############################################
612
##############################################
615
def on_download(self, location, sync=False):
616
self.emit('progress', 0.5, "Downloading...")
617
cd = cachedownloader.GeocachingComCacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages'])
618
cd.connect("download-error", self.on_download_error)
619
cd.connect("already-downloading-error", self.on_already_downloading_error)
621
def same_thread(arg1, arg2):
622
gobject.idle_add(self.on_download_complete, arg1, arg2)
625
cd.connect("finished-overview", same_thread)
626
t = Thread(target=cd.get_geocaches, args=[location])
333
point_new = self.pointprovider.add_point(c)
336
self.pointprovider.save()
631
return self.on_download_complete(None, cd.get_geocaches(location))
633
# called on signal by downloading thread
634
def on_download_complete(self, something, caches, sync=False):
637
point_new = self.pointprovider.add_point(c)
640
self.pointprovider.save()
641
self.emit('hide-progress')
642
self.emit('map-marks-changed')
338
644
return (caches, new_caches)
340
self.gui.hide_progress()
648
# called on signal by downloading thread
649
def on_already_downloading_error(self, something, error):
650
def same_thread(error):
651
self.emit('error', error)
653
gobject.idle_add(same_thread, error)
654
#self.emit('error', error)
656
# called on signal by downloading thread
657
def on_download_error(self, something, error):
658
logging.exception(error)
659
def same_thread(error):
660
self.emit('hide-progress')
661
self.emit('error', error)
663
gobject.idle_add(same_thread, error)
343
def on_download_cache(self, cache):
344
self.gui.set_download_progress(0.5, "Downloading %s..." % cache.name)
666
def on_download_cache(self, cache, sync=False):
668
self.emit('progress', 0.5, "Downloading %s..." % cache.name)
347
cd = geocaching.CacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages'])
670
cd = cachedownloader.GeocachingComCacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages'])
671
cd.connect("download-error", self.on_download_error)
672
cd.connect("already-downloading-error", self.on_already_downloading_error)
674
def same_thread(arg1, arg2):
675
gobject.idle_add(self.on_download_cache_complete, arg1, arg2)
677
cd.connect("finished-single", same_thread)
678
t = Thread(target=cd.update_coordinate, args=[cache])
348
684
full = cd.update_coordinate(cache)
349
self.pointprovider.add_point(full, True)
350
self.pointprovider.save()
351
except Exception as e:
352
self.gui.show_error(e)
355
self.gui.hide_progress()
358
def on_export_cache(self, cache, folder = None):
359
self.gui.set_download_progress(0.5, "Exporting %s..." % cache.name)
361
exporter = geocaching.HTMLExporter(self.downloader, self.settings['download_output_dir'])
362
exporter.export(cache, folder)
363
except Exception as e:
364
self.gui.show_error(e)
366
self.gui.hide_progress()
687
# called on signal by downloading thread
688
def on_download_cache_complete(self, something, cache):
689
self.pointprovider.add_point(cache, True)
690
self.pointprovider.save()
691
self.emit('hide-progress')
692
self.emit('cache-changed', cache)
372
698
def on_download_descriptions(self, location, visibleonly=False):
373
cd = geocaching.CacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages'])
374
700
#exporter = geocaching.HTMLExporter(self.downloader, self.settings['download_output_dir'])
376
702
self.pointprovider.push_filter()
378
704
if self.settings['download_notfound'] or visibleonly:
383
709
if self.settings['download_new'] or visibleonly:
384
710
has_details = False
385
711
elif self.settings['download_nothing']:
386
712
has_details = True
388
714
has_details = None
391
717
if self.settings['download_visible'] or visibleonly:
392
718
self.pointprovider.set_filter(found=found, has_details=has_details, adapt_filter=True)
393
719
caches = self.pointprovider.get_points_filter(location)
395
721
self.pointprovider.set_filter(found=found, has_details=has_details, adapt_filter=False)
396
722
caches = self.pointprovider.get_points_filter()
724
self.pointprovider.pop_filter()
725
self.update_coordinates(caches)
728
def update_coordinates(self, caches):
729
cd = cachedownloader.GeocachingComCacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages'])
730
cd.connect("download-error", self.on_download_error)
731
cd.connect("already-downloading-error", self.on_already_downloading_error)
734
def same_thread(arg1, arg2):
735
gobject.idle_add(self.on_download_descriptions_complete, arg1, arg2)
738
def same_thread_progress (arg1, arg2, arg3, arg4):
739
gobject.idle_add(self.on_download_progress, arg1, arg2, arg3, arg4)
742
cd.connect('progress', self.on_download_progress)
743
cd.connect('finished-multiple', same_thread)
745
t = Thread(target=cd.update_coordinates, args=[caches])
750
# called on signal by downloading thread
751
def on_download_descriptions_complete(self, something, caches):
753
self.pointprovider.add_point(c, True)
754
self.pointprovider.save()
755
self.emit('hide-progress')
757
self.emit('cache-changed', c)
758
self.emit('map-marks-changed')
762
# called on signal by downloading thread
763
def on_download_progress(self, something, cache_name, i, max_i):
764
self.emit('progress', float(i) / float(max_i), "Downloading %s (%d of %d)..." % (cache_name, i, max_i))
767
##############################################
771
##############################################
773
def on_export_cache(self, cache, format, folder):
774
from exporter import GpxExporter
775
if (format == 'gpx'):
776
exporter = GpxExporter()
778
raise Exception("Format currently not supported: %s" % format)
780
self.emit('progress', 0.5, "Exporting %s..." % cache.name)
404
self.gui.set_download_progress(i / count, "Downloading %s..." % cache.name)
405
full = cd.update_coordinate(cache)
406
self.pointprovider.add_point(full, True)
407
#exporter.export(full)
410
except Exception as e:
411
self.gui.show_error(e)
782
exporter.export(cache, folder)
784
self.emit('error', e)
413
self.gui.hide_progress()
414
self.pointprovider.pop_filter()
415
self.pointprovider.save()
418
#if self.settings['download_create_index']:
419
# all_caches = pointprovider.get_points_filter(None, None, True)
420
# exporter.write_index(all_caches)
426
def on_config_changed(self, new_settings):
427
self.settings = new_settings
428
self.downloader.update_userdata(self.settings['options_username'], self.settings['options_password'])
429
self.__write_config()
432
def on_notes_changed(self, cache, new_notes):
433
self.pointprovider.update_field(cache, 'notes', new_notes)
435
def on_fieldnotes_changed(self, cache, new_notes):
436
self.pointprovider.update_field(cache, 'fieldnotes', new_notes)
786
self.emit('hide-progress')
790
##############################################
794
##############################################
797
def save_fieldnote(self, cache):
798
if cache.logas == geocaching.GeocacheCoordinate.LOG_AS_FOUND:
801
elif cache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND:
804
self.save_cache_attribute(cache, ('logas', 'logdate', 'fieldnotes'))
805
self.emit('fieldnotes-changed')
439
807
def on_upload_fieldnotes(self):
440
self.gui.set_download_progress(0.5, "Uploading Fieldnotes...")
808
self.emit('progress', 0.5, "Uploading Fieldnotes...")
442
810
caches = self.pointprovider.get_new_fieldnotes()
443
fn = geocaching.FieldnotesUploader(self.downloader)
449
except Exception as e:
450
self.gui.show_error(e)
452
#self.gui.show_success("Field notes uploaded successfully.")
454
self.pointprovider.update_field(c, 'logas', geocaching.GeocacheCoordinate.LOG_NO_LOG)
456
self.gui.hide_progress()
458
def __read_gps(self):
459
fix = self.gps_thread.get_data()
460
if fix.position != None:
461
self.gui.on_good_fix(fix)
463
self.gui.on_no_fix(fix, self.gps_thread.status)
466
def __read_config(self):
467
filename = os.path.join(self.SETTINGS_DIR, 'config')
468
if not os.path.exists(filename):
469
self.settings = self.DEFAULT_SETTINGS
471
f = file(filename, 'r')
475
tmp_settings = json.loads(string)
476
for k, v in self.DEFAULT_SETTINGS.items():
477
if k in tmp_settings.keys() != None:
478
self.settings[k] = tmp_settings[k]
482
self.settings = self.DEFAULT_SETTINGS
486
def __try_show_cache_by_search(self, idstring):
487
cache = self.pointprovider.find_by_string(idstring)
489
self.gui.show_cache(cache)
490
self.gui.set_center(cache)
494
def __write_config(self):
495
filename = os.path.join(self.SETTINGS_DIR, 'config')
496
f = file(filename, 'w')
497
f.write(json.dumps(self.settings, sort_keys=True, indent=4))
811
fn = fieldnotesuploader.FieldnotesUploader(self.downloader)
812
fn.connect("upload-error", self.on_download_error)
814
def same_thread(arg1):
815
gobject.idle_add(self.on_upload_fieldnotes_finished, arg1, caches)
818
fn.connect('finished-uploading', same_thread)
822
t = Thread(target=fn.upload)
826
def on_upload_fieldnotes_finished(self, widget, caches):
828
c.logas = geocaching.GeocacheCoordinate.LOG_NO_LOG
829
self.save_cache_attribute(c, 'logas')
830
self.emit('hide-progress')
831
self.emit('fieldnotes-changed')
833
def get_new_fieldnotes_count(self):
834
return self.pointprovider.get_new_fieldnotes_count()
837
##############################################
841
##############################################
843
def save_cache_attribute(self, cache, attribute):
844
if type(attribute) == tuple:
846
self.pointprovider.update_field(cache, a, cache.serialize_one(a), save=False)
847
self.pointprovider.save()
849
self.pointprovider.update_field(cache, attribute, cache.serialize_one(attribute))
852
def set_alternative_position(self, cache, ap):
853
cache.set_alternative_position(ap)
854
self.save_cache_attribute(cache, ('alter_lat', 'alter_lon'))
855
self.emit('map-marks-changed')
500
859
def determine_path ():
501
860
"""Borrowed from wxglade.py"""
504
if os.path.islink (root):
505
root = os.path.realpath (root)
506
return os.path.dirname(os.path.abspath (root))
863
if path.islink (root):
864
root = path.realpath (root)
865
return path.dirname(path.abspath (root))
508
print "I'm sorry, but something is wrong."
509
print "There is no __file__ variable. Please contact the author."
867
logging.error("I'm sorry, but something is wrong.")
868
logging.error("There is no __file__ variable. Please contact the author.")
874
gobject.threads_init()
514
875
Core(gui, determine_path())
877
def start_profile(what):
879
p = cProfile.Profile()
882
print "BY CALLS:\n------------------------------------------------------------"
884
if x.callcount < y.callcount:
886
elif x.callcount == y.callcount:
891
for line in stats[:100]:
892
print "%d %4f %s" % (line.callcount, line.totaltime, line.code)
893
if line.calls == None:
895
line.calls.sort(cmp=c)
896
for line in line.calls[:10]:
897
print "-- %d %4f %s" % (line.callcount, line.totaltime, line.code)
900
print "BY TOTALTIME:\n------------------------------------------------------------"
902
if x.totaltime < y.totaltime:
904
elif x.totaltime == y.totaltime:
909
for line in stats[:30]:
910
print "%d %4f %s" % (line.callcount, line.totaltime, line.code)
911
if line.calls == None:
913
line.calls.sort(cmp=c)
914
for line in line.calls[:10]:
915
print "-- %d %4f %s" % (line.callcount, line.totaltime, line.code)
921
if __name__ == "__main__":
922
if '--profile' in argv:
923
start_profile('start()')