~ubuntu-branches/ubuntu/precise/agtl/precise

« back to all changes in this revision

Viewing changes to files/advancedcaching/core.py

  • Committer: Bazaar Package Importer
  • Author(s): Angel Abad
  • Date: 2011-01-23 13:14:08 UTC
  • mfrom: (4.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20110123131408-4wy5aefap3o6a4lw
Tags: 0.8.0.3-1ubuntu1
* Merge from debian unstable.  Remaining changes:
  - debian/control: Switch section from python to misc
  - Add category Geography to .desktop

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
2
# -*- coding: utf-8 -*-
3
3
 
4
 
#        Copyright (C) 2009 Daniel Fett
5
 
#         This program is free software: you can redistribute it and/or modify
 
4
#   Copyright (C) 2010 Daniel Fett
 
5
#   This program is free software: you can redistribute it and/or modify
6
6
#   it under the terms of the GNU General Public License as published by
7
7
#   the Free Software Foundation, either version 3 of the License, or
8
8
#   (at your option) any later version.
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/>.
17
17
#
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
19
21
#
20
22
 
21
 
usage = r'''Here's how to use this app:
22
 
 
23
 
If you want to use the gui:
24
 
%(name)s --simple
25
 
    Simple User Interface, for mobile devices such as the Openmoko Freerunner
26
 
%(name)s --desktop
27
 
    Full User Interface, for desktop usage (not implemented yet)
28
 
    
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".
42
 
options:
43
 
        --user(name) username
44
 
        --pass(word) password
45
 
                Your geocaching.com login data. 
46
 
importactions:
47
 
        --in coord1 coord2
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.
57
 
 
58
 
filter-options:
59
 
        --in coord1 coord2
60
 
        --around coord1 radius-in-km
61
 
                See import actions.
62
 
        -f|--found
63
 
        -F|--not-found
64
 
                Filter out geocaches which have (not) been found by the user.
65
 
        -w|--was-downloaded
66
 
                caches which have full detail information available
67
 
        
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.
81
 
        --new
82
 
                Caches which were downloaded in current session. Useful to
83
 
                get alerted when new caches arrive.
84
 
actions:
85
 
        --print 
86
 
                Default action, prints tab-separated list of geocaches
87
 
        --fetch-details
88
 
                Downloads Descriptions etc. for selected geocaches
89
 
        --export-html folder
90
 
                Dumps HTML pages to given folder
91
 
        --command command
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.
94
 
 
95
 
        Not implemented yet:
96
 
        --export-gpx folder
97
 
                Dumps geocaches into separate GPX files
98
 
        --export-single-gpx file
99
 
                Dumps selected geocaches into a single GPX file
100
 
        --draw-map zoom 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. 
104
 
        
105
 
Preferred format for coordinates:
106
 
    'N49 44.111 E6 29.123'
107
 
    or
108
 
    'N49.123456 E6.043212'
109
 
 
110
 
Instead of a coordinate, you may also query geonames.com for a place name.
111
 
Just start the string with 'q:':
112
 
    q:London
113
 
    'q:Brisbane, Australia'
114
 
 
115
 
'''
 
23
from __future__ import with_statement
 
24
 
 
25
VERSION = "0.8.0.3"
116
26
   
117
27
 
 
28
import logging
 
29
logging.basicConfig(level=logging.WARNING,
 
30
                    format='%(relativeCreated)6d %(levelname)10s %(name)-20s %(message)s',
 
31
                    )
118
32
 
119
33
from geo import Coordinate
120
 
import json
121
 
import sys
 
34
try:
 
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
122
40
 
123
41
import downloader
124
42
import geocaching
125
43
import gobject
126
44
import gpsreader
127
 
import os
 
45
from os import path, mkdir, extsep, remove, walk
128
46
import provider
129
 
import math
130
 
 
131
 
#import cProfile
132
 
#import pstats
133
 
 
134
 
 
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
 
52
 
 
53
if len(argv) == 1:
 
54
    import cli
 
55
    print cli.usage % ({'name': argv[0]})
137
56
    exit()
138
 
        
139
 
arg = sys.argv[1].strip()
140
 
if arg == '--simple':
 
57
 
 
58
if '-v' in argv:
 
59
    import colorer
 
60
    import logging.handlers
 
61
    logging.getLogger('').setLevel(logging.DEBUG)
 
62
    logging.debug("Set log level to DEBUG")
 
63
 
 
64
if '--simple' in argv:
141
65
    import simplegui
142
66
    gui = simplegui.SimpleGui
143
 
elif arg == '--desktop':
 
67
elif '--desktop' in argv:
144
68
    import biggui
145
69
    gui = biggui.BigGui
 
70
elif '--qt' in argv:
 
71
    import qtgui
 
72
    gui = qtgui.QtGui
 
73
elif '--hildon' in argv:
 
74
    import hildongui
 
75
    gui = hildongui.HildonGui
146
76
else:
147
77
    import cli
148
78
    gui = cli.Cli
149
79
 
150
 
        
151
 
class Standbypreventer():
152
 
    STATUS_NONE = 0
153
 
    STATUS_ALIVE = 1
154
 
    STATUS_SCREEN_ON = 2
155
 
 
156
 
    def __init__(self):
157
 
        self.requested_status = self.STATUS_NONE
158
 
                
159
 
    def __del__(self):
160
 
        self.__unrequest_all()
161
 
                
162
 
    def set_status(self, status):
163
 
        if status != self.requested_status:
164
 
            self.__unrequest_all()
165
 
            self.__request(status)
166
 
        
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')
172
 
                        
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')
178
 
                        
179
 
    def __try_run(self, command):
180
 
        try:
181
 
            os.system(command)
182
 
        except Exception as e:
183
 
            print "Could not prevent Standby: %s" % e
184
 
 
185
 
class Core():
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')
 
81
 
 
82
class Core(gobject.GObject):
 
83
 
 
84
    __gsignals__ = {
 
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, ()),
 
96
        }
 
97
 
 
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')
 
102
 
 
103
    MAEMO_HOME = path.expanduser(path.join('~', 'MyDocs', '.'))
 
104
    MAPS_DIR = path.join('Maps', '')
 
105
 
 
106
    DATA_DIR = path.expanduser(path.join('~', '')) if not path.exists(MAEMO_HOME) else MAEMO_HOME
 
107
 
 
108
    UPDATE_MODULES = [cachedownloader, fieldnotesuploader]
189
109
    
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,
201
122
        'map_zoom': 7,
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,
 
137
        'map_providers': [
 
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'})
 
140
 
 
141
        ],
 
142
        'options_map_double_size': False,
 
143
        'options_rotate_screen': 0,
 
144
        'tts_interval': 0,
 
145
        'options_default_log_text' : 'TFTC!\n\nLogged at %X from my %(machine)s using AGTL.',
213
146
    }
214
147
            
215
148
    def __init__(self, guitype, root):
216
 
        if not os.path.exists(self.SETTINGS_DIR):
217
 
            os.mkdir(self.SETTINGS_DIR)
218
 
 
219
 
        dataroot = os.path.join(root, 'data')
220
 
        
221
 
        #self.standbypreventer = Standbypreventer()
222
 
        # seems to crash dbus/fso/whatever
223
 
                        
 
149
        gobject.GObject.__init__(self)
 
150
        self.current_target = None
 
151
        self.current_position = None
 
152
        self.create_recursive(self.SETTINGS_DIR)
 
153
 
 
154
        self.dataroot = path.join(root, 'data')
 
155
 
 
156
        self._install_updates()
 
157
 
224
158
        self.__read_config()
225
 
                
226
 
        #self.standbypreventer.set_status(Standbypreventer.STATUS_SCREEN_ON)
227
 
                
228
 
        self.downloader = downloader.FileDownloader(self.settings['options_username'], self.settings['options_password'], self.COOKIE_FILE)
229
 
                
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'])
 
163
                
 
164
 
 
165
        self.downloader = downloader.FileDownloader(self.settings['options_username'], self.settings['options_password'], self.COOKIE_FILE, cachedownloader.GeocachingComCacheDownloader.login_callback)
 
166
                
 
167
        self.pointprovider = provider.PointProvider(self.CACHES_DB, geocaching.GeocacheCoordinate, 'geocaches')
 
168
 
 
169
        self.gui = guitype(self, self.dataroot)
 
170
 
237
171
        
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')  
 
177
 
 
178
 
 
179
        if '--sim' in argv:
 
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)
244
 
 
 
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:
246
191
            import geonames
247
192
            self.geonames = geonames.Geonames(self.downloader)
248
 
        
 
193
 
 
194
        if '--startup-only' in argv:
 
195
            return
 
196
 
 
197
 
249
198
        self.gui.show()
250
 
                
251
 
                
252
 
                
 
199
        if not '--profile' in argv:
 
200
            exit()
 
201
 
 
202
    ##############################################
 
203
    #
 
204
    # Misc
 
205
    #
 
206
    ##############################################
 
207
 
 
208
    @staticmethod
 
209
    def create_recursive(dpath):
 
210
        if dpath != '/':
 
211
            if not path.exists(dpath):
 
212
                head, tail = path.split(dpath)
 
213
                Core.create_recursive(head)
 
214
                try:
 
215
                    mkdir(dpath)
 
216
                except Exception, e:
 
217
                    logging.info("Could not create directory; %s" % e)
 
218
                    pass
 
219
 
 
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()
 
227
            if len(images) == 0:
 
228
                continue
 
229
            for filename, caption in images.items():
 
230
                fullpath = path.join(self.settings['download_output_dir'], filename)
 
231
                try:
 
232
                    remove(fullpath)
 
233
                except Exception:
 
234
                    logging.warning("Could not remove " + fullpath)
 
235
        self.pointprovider.remove_geocaches(old_geocaches)
 
236
        self.pointprovider.optimize()
 
237
 
 
238
    def get_file_sizes(self):
 
239
        folder = self.settings['download_output_dir']
 
240
        folder_size = 0
 
241
        for (p, dirs, files) in walk(folder):
 
242
            for file in files:
 
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}
 
247
 
 
248
    @staticmethod
 
249
    def format_file_size(size):
 
250
        if size < 1024:
 
251
            return "%d B" % 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))
 
256
        else:
 
257
            return "%d GiB" % (size / (1024 * 1024 * 1024))
 
258
 
 
259
 
 
260
    ##############################################
 
261
    #
 
262
    # Handling Updates
 
263
    #
 
264
    ##############################################
 
265
 
 
266
    def _install_updates(self):
 
267
        updated_modules = 0
 
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:
 
275
                        for line in f:
 
276
                            if line.startswith('VERSION'):
 
277
                                exec line in v_dict
 
278
                                break
 
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))
 
281
                        reload(m)
 
282
                        updated_modules += 1
 
283
                    else:
 
284
                        logging.info("Not reloading Module '%s', current version number: %d, version number of update file: %d" % (m.__name__, v_dict['VERSION'], m.VERSION))
 
285
                else:
 
286
                    logging.info("Skipping nonexistant update from" + path.join(self.UPDATE_DIR, "%s%spy" % (m.__name__, extsep)))
 
287
        return updated_modules
 
288
 
 
289
    def try_update(self):
 
290
        from urllib import urlretrieve
 
291
        from urllib2 import HTTPError
 
292
        import tempfile
 
293
        import hashlib
 
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...")
 
299
        try:
 
300
            try:
 
301
                reader = self.downloader.get_reader(url, login=False)
 
302
            except HTTPError, e:
 
303
                logging.exception(e)
 
304
                raise Exception("No updates available.")
 
305
            except Exception, e:
 
306
                logging.exception(e)
 
307
                raise Exception("Could not connect to update server.")
 
308
 
 
309
            try:
 
310
                files = []
 
311
                for line in reader:
 
312
                    md5, name = line.strip().split('  ')
 
313
                    handle, temp = tempfile.mkstemp()
 
314
                    files.append((md5, name, temp))
 
315
            except Exception, e:
 
316
                logging.exception(e)
 
317
                raise Exception("No updates were found. (Could not process index file.)")
 
318
 
 
319
            if len(files) == 0:
 
320
                raise Exception("No updates available.")
 
321
 
 
322
            for md5sum, name, temp in files:
 
323
                url = '%s/%s' % (baseurl, name)
 
324
                try:
 
325
                    urlretrieve(url, temp)
 
326
                except Exception, e:
 
327
                    logging.exception(e)
 
328
                    raise Exception("Could not download file '%s'" % name)
 
329
 
 
330
                hash = hashlib.md5(open(temp).read()).hexdigest()
 
331
                if hash != md5sum:
 
332
                    raise Exception("There was an error downloading the file. (MD5 sum mismatch in %s)" % name)
 
333
 
 
334
            for md5sum, name, temp in files:
 
335
                file = path.join(self.UPDATE_DIR, name)
 
336
                try:
 
337
                    copyfile(temp, file)
 
338
                except Exception, e:
 
339
                    logging.exception(e)
 
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)
 
341
                finally:
 
342
                    try:
 
343
                        remove(tmpfile)
 
344
                    except Exception:
 
345
                        pass
 
346
        except Exception, e:
 
347
            logging.exception(e)
 
348
            def same_thread(error):
 
349
                self.emit('hide-progress')
 
350
                self.emit('error', error)
 
351
                return False
 
352
            gobject.idle_add(same_thread, e)
 
353
            return None
 
354
            
 
355
        def same_thread():
 
356
            self.emit('hide-progress')
 
357
            return False
 
358
        gobject.idle_add(same_thread)
 
359
        return self._install_updates()
 
360
 
 
361
 
 
362
 
 
363
    ##############################################
 
364
    #
 
365
    # Settings
 
366
    #
 
367
    ##############################################
 
368
 
 
369
    '''
 
370
    This should be called to update the settings throughout all components.
 
371
    '''
 
372
    def save_settings(self, settings, source):
 
373
        logger.debug("Got settings update from %s" % source)
 
374
        
 
375
        self.settings.update(settings)
 
376
        self.emit('settings-changed', settings, source)
 
377
 
 
378
    '''
 
379
    This is called when settings have changed.
 
380
    '''
 
381
    def __on_settings_changed(self, caller, settings, source):
 
382
        logger.debug("Settings where changed by %s." % source)
 
383
        if source == self:
 
384
            return
 
385
        if 'options_username' in settings and 'options_password' in settings:
 
386
            self.downloader.update_userdata(settings['options_username'], settings['options_password'])
 
387
 
 
388
    '''
 
389
    This is called when settings shall be saved, calling save_settings afterwards.
 
390
    '''
 
391
    def __on_save_settings(self, caller):
 
392
        logger.debug("Assembling update for settings, on behalf of %s" % caller)
 
393
        settings = {
 
394
            'last_target_lat': self.current_target.lat,
 
395
            'last_target_lon': self.current_target.lon
 
396
        }
 
397
        caller.save_settings(settings, self)
 
398
    
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()
 
403
 
 
404
    # called by gui
 
405
    def on_destroy(self):
 
406
        logger.debug("Somebody is being killed, saving the settings.")
 
407
        self.emit('save-settings')
 
408
        self.__write_config()
 
409
 
 
410
    # called by gui
 
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()
 
415
 
 
416
 
 
417
 
 
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
 
423
            return
 
424
        f = file(filename, 'r')
 
425
        string = f.read()
 
426
        self.settings = {}
 
427
        if string != '':
 
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]
 
432
                else:
 
433
                    self.settings[k] = v
 
434
        else:
 
435
            self.settings = self.DEFAULT_SETTINGS
 
436
 
 
437
 
 
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)
 
443
        f.write(config)
 
444
 
 
445
 
 
446
    ##############################################
 
447
    #
 
448
    # Target & GPS
 
449
    #
 
450
    ##############################################
 
451
 
 
452
 
 
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')
 
458
 
 
459
 
 
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)
 
464
        else:
 
465
            distance = None
 
466
            bearing = None
 
467
        return distance, bearing
 
468
 
 
469
    def __read_gps(self):
 
470
        fix = self.gps_thread.get_data()
 
471
 
 
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)
 
476
        else:
 
477
            self.emit('no-fix', fix, self.gps_thread.status)
 
478
        return True
 
479
 
 
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)
 
484
        return True
 
485
 
 
486
    def __read_gps_cb_changed(self, device):
 
487
        fix = self.gps_thread.fix_from_tuple(device.fix, device)
 
488
        # @type fix gpsreader.Fix
 
489
 
 
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)
 
494
        else:
 
495
            self.emit('no-fix', fix, 'No Fix')
 
496
        return True
 
497
 
 
498
 
 
499
    ##############################################
 
500
    #
 
501
    # Geonames & Routing
 
502
    #
 
503
    ##############################################
256
504
                                
257
505
    def get_coord_by_name(self, query):
258
506
        return self.geonames.search(query)
259
507
 
 
508
 
 
509
 
 
510
    def search_place(self, search):
 
511
        return self.geonames.search_all(search)
 
512
 
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])
273
526
                
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
278
 
                or abs(brg) < 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
 
531
                     or abs(brg) < 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])
282
535
            else:
 
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))
292
546
                together = []
293
 
        print "* Needing %d unique queries" % len(out)
 
547
        logging.info("Needing %d unique queries" % len(out))
294
548
        return out
295
549
 
 
550
    ##############################################
 
551
    #
 
552
    # Deprecated
 
553
    #
 
554
    ##############################################
 
555
 
296
556
    # called by gui
297
557
    def on_cache_selected(self, cache):
298
558
        self.gui.show_cache(cache)
 
559
 
 
560
    ##############################################
 
561
    #
 
562
    # Filters, Searching & Pointprovider
 
563
    #
 
564
    ##############################################
299
565
                
300
566
    # called by gui
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)
303
569
        #if m != None:
304
570
        self.__try_show_cache_by_search('%' + text + '%')
305
 
                
306
 
    # called by gui
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):
308
 
                
309
 
                
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)
 
571
 
 
572
    # called by gui
 
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')
 
576
                
 
577
    # called by gui
 
578
    def reset_filter(self):
 
579
        self.pointprovider.set_filter()
 
580
        self.emit('map-marks-changed')
 
581
 
 
582
    # called by gui
 
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)
313
 
                                
314
 
 
315
 
    # called by gui
316
 
    def on_destroy(self):
317
 
        self.settings = self.gui.read_settings()
318
 
        self.__write_config()
319
 
 
320
 
    # called by gui
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'])
324
 
        try:
325
 
            caches = cd.get_geocaches(location)
326
 
        except Exception as e:
327
 
            self.gui.show_error(e)
328
 
            print e
329
 
            return []
 
587
 
 
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)
 
595
 
 
596
 
 
597
    def get_geocache_by_name(self, name):
 
598
        return self.pointprovider.get_by_name(name)
 
599
 
 
600
    def __try_show_cache_by_search(self, idstring):
 
601
        cache = self.pointprovider.find_by_string(idstring)
 
602
        if cache != None:
 
603
            self.gui.show_cache(cache)
 
604
            self.gui.set_center(cache)
 
605
            return True
 
606
        return False
 
607
 
 
608
    ##############################################
 
609
    #
 
610
    # Downloading
 
611
    #
 
612
    ##############################################
 
613
 
 
614
    # called by gui
 
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)
 
620
        if not sync:
 
621
            def same_thread(arg1, arg2):
 
622
                gobject.idle_add(self.on_download_complete, arg1, arg2)
 
623
                return False
 
624
 
 
625
            cd.connect("finished-overview", same_thread)
 
626
            t = Thread(target=cd.get_geocaches, args=[location])
 
627
            t.daemon = True
 
628
            t.start()
 
629
            return False
330
630
        else:
331
 
            new_caches = []
332
 
            for c in caches:
333
 
                point_new = self.pointprovider.add_point(c)
334
 
                if point_new:
335
 
                    new_caches.append(c)
336
 
            self.pointprovider.save()
337
 
            
 
631
            return self.on_download_complete(None, cd.get_geocaches(location))
 
632
 
 
633
    # called on signal by downloading thread
 
634
    def on_download_complete(self, something, caches, sync=False):
 
635
        new_caches = []
 
636
        for c in caches:
 
637
            point_new = self.pointprovider.add_point(c)
 
638
            if point_new:
 
639
                new_caches.append(c)
 
640
        self.pointprovider.save()
 
641
        self.emit('hide-progress')
 
642
        self.emit('map-marks-changed')
 
643
        if sync:
338
644
            return (caches, new_caches)
339
 
        finally:
340
 
            self.gui.hide_progress()
 
645
        else:
 
646
            return False
 
647
 
 
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)
 
652
            return False
 
653
        gobject.idle_add(same_thread, error)
 
654
        #self.emit('error', error)
 
655
 
 
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)
 
662
            return False
 
663
        gobject.idle_add(same_thread, error)
341
664
 
342
665
    # called by gui
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):
 
667
        #
 
668
        self.emit('progress', 0.5, "Downloading %s..." % cache.name)
345
669
 
346
 
        try:
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)
 
673
        if not sync:
 
674
            def same_thread(arg1, arg2):
 
675
                gobject.idle_add(self.on_download_cache_complete, arg1, arg2)
 
676
                return False
 
677
            cd.connect("finished-single", same_thread)
 
678
            t = Thread(target=cd.update_coordinate, args=[cache])
 
679
            t.daemon = True
 
680
            t.start()
 
681
            #t.join()
 
682
            return False
 
683
        else:
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)
353
 
            return cache
354
 
        finally:
355
 
            self.gui.hide_progress()
356
 
        return full
357
 
                
358
 
    def on_export_cache(self, cache, folder = None):
359
 
        self.gui.set_download_progress(0.5, "Exporting %s..." % cache.name)
360
 
        try:
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)
365
 
        finally:
366
 
            self.gui.hide_progress()
367
 
        
368
 
                
369
 
                
370
 
                
 
685
            return full
 
686
 
 
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)
 
693
        return False
 
694
 
 
695
 
 
696
 
371
697
    # called by gui
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'])
 
699
 
374
700
        #exporter = geocaching.HTMLExporter(self.downloader, self.settings['download_output_dir'])
375
 
                
 
701
 
376
702
        self.pointprovider.push_filter()
377
 
                        
 
703
 
378
704
        if self.settings['download_notfound'] or visibleonly:
379
705
            found = False
380
706
        else:
381
707
            found = None
382
 
                        
 
708
 
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
387
713
        else:
388
714
            has_details = None
389
 
                
390
 
                
 
715
 
 
716
 
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)
394
720
        else:
395
721
            self.pointprovider.set_filter(found=found, has_details=has_details, adapt_filter=False)
396
722
            caches = self.pointprovider.get_points_filter()
397
 
                
398
 
        print caches       
399
 
                
400
 
        count = len(caches)
401
 
        i = 0.0
 
723
 
 
724
        self.pointprovider.pop_filter()
 
725
        self.update_coordinates(caches)
 
726
 
 
727
 
 
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)
 
732
 
 
733
 
 
734
        def same_thread(arg1, arg2):
 
735
            gobject.idle_add(self.on_download_descriptions_complete, arg1, arg2)
 
736
            return False
 
737
 
 
738
        def same_thread_progress (arg1, arg2, arg3, arg4):
 
739
            gobject.idle_add(self.on_download_progress, arg1, arg2, arg3, arg4)
 
740
            return False
 
741
 
 
742
        cd.connect('progress', self.on_download_progress)
 
743
        cd.connect('finished-multiple', same_thread)
 
744
 
 
745
        t = Thread(target=cd.update_coordinates, args=[caches])
 
746
        t.daemon = True
 
747
        t.start()
 
748
 
 
749
 
 
750
    # called on signal by downloading thread
 
751
    def on_download_descriptions_complete(self, something, caches):
 
752
        for c in caches:
 
753
            self.pointprovider.add_point(c, True)
 
754
        self.pointprovider.save()
 
755
        self.emit('hide-progress')
 
756
        for c in caches:
 
757
            self.emit('cache-changed', c)
 
758
        self.emit('map-marks-changed')
 
759
        return False
 
760
 
 
761
 
 
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))
 
765
        return False
 
766
 
 
767
    ##############################################
 
768
    #
 
769
    # Exporting
 
770
    #
 
771
    ##############################################
 
772
                
 
773
    def on_export_cache(self, cache, format, folder):
 
774
        from exporter import GpxExporter
 
775
        if (format == 'gpx'):
 
776
            exporter = GpxExporter()
 
777
        else:
 
778
            raise Exception("Format currently not supported: %s" % format)
 
779
 
 
780
        self.emit('progress', 0.5, "Exporting %s..." % cache.name)
402
781
        try:
403
 
            for cache in caches:
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)
408
 
                i += 1.0
409
 
                                
410
 
        except Exception as e:
411
 
            self.gui.show_error(e)
 
782
            exporter.export(cache, folder)
 
783
        except Exception, e:
 
784
            self.emit('error', e)
412
785
        finally:
413
 
            self.gui.hide_progress()
414
 
            self.pointprovider.pop_filter()
415
 
            self.pointprovider.save()
416
 
                
417
 
                
418
 
        #if self.settings['download_create_index']:
419
 
        #        all_caches = pointprovider.get_points_filter(None, None, True)
420
 
        #        exporter.write_index(all_caches)
421
 
 
422
 
                
423
 
 
424
 
                        
425
 
    # called by gui
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()
430
 
 
431
 
 
432
 
    def on_notes_changed(self, cache, new_notes):
433
 
        self.pointprovider.update_field(cache, 'notes', new_notes)
434
 
 
435
 
    def on_fieldnotes_changed(self, cache, new_notes):
436
 
        self.pointprovider.update_field(cache, 'fieldnotes', new_notes)
437
 
 
438
 
                
 
786
            self.emit('hide-progress')
 
787
        
 
788
                
 
789
 
 
790
    ##############################################
 
791
    #
 
792
    # Fieldnotes
 
793
    #
 
794
    ##############################################
 
795
 
 
796
 
 
797
    def save_fieldnote(self, cache):
 
798
        if cache.logas == geocaching.GeocacheCoordinate.LOG_AS_FOUND:
 
799
            cache.found = 1
 
800
 
 
801
        elif cache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND:
 
802
            cache.found = 0
 
803
 
 
804
        self.save_cache_attribute(cache, ('logas', 'logdate', 'fieldnotes'))
 
805
        self.emit('fieldnotes-changed')        
 
806
 
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...")
441
809
 
442
810
        caches = self.pointprovider.get_new_fieldnotes()
443
 
        fn = geocaching.FieldnotesUploader(self.downloader)
444
 
        try:
445
 
            for c in caches:
446
 
                fn.add_fieldnote(c)
447
 
            fn.upload()
448
 
                        
449
 
        except Exception as e:
450
 
            self.gui.show_error(e)
451
 
        else:
452
 
            #self.gui.show_success("Field notes uploaded successfully.")
453
 
            for c in caches:
454
 
                self.pointprovider.update_field(c, 'logas', geocaching.GeocacheCoordinate.LOG_NO_LOG)
455
 
        finally:
456
 
            self.gui.hide_progress()
457
 
                
458
 
    def __read_gps(self):
459
 
        fix = self.gps_thread.get_data()
460
 
        if fix.position != None:
461
 
            self.gui.on_good_fix(fix)
462
 
        else:
463
 
            self.gui.on_no_fix(fix, self.gps_thread.status)
464
 
        return True
465
 
                
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
470
 
            return
471
 
        f = file(filename, 'r')
472
 
        string = f.read()
473
 
        self.settings = {}
474
 
        if string != '':
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]
479
 
                else:
480
 
                    self.settings[k] = v
481
 
        else:
482
 
            self.settings = self.DEFAULT_SETTINGS
483
 
                
484
 
                
485
 
                
486
 
    def __try_show_cache_by_search(self, idstring):
487
 
        cache = self.pointprovider.find_by_string(idstring)
488
 
        if cache != None:
489
 
            self.gui.show_cache(cache)
490
 
            self.gui.set_center(cache)
491
 
            return True
492
 
        return False
493
 
                
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)
 
813
        
 
814
        def same_thread(arg1):
 
815
            gobject.idle_add(self.on_upload_fieldnotes_finished, arg1, caches)
 
816
            return False
 
817
            
 
818
        fn.connect('finished-uploading', same_thread)
 
819
        
 
820
        for c in caches:
 
821
            fn.add_fieldnote(c)
 
822
        t = Thread(target=fn.upload)
 
823
        t.daemon = True
 
824
        t.start()
 
825
        
 
826
    def on_upload_fieldnotes_finished(self, widget, caches):
 
827
        for c in 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')
 
832
 
 
833
    def get_new_fieldnotes_count(self):
 
834
        return self.pointprovider.get_new_fieldnotes_count()
 
835
 
 
836
 
 
837
    ##############################################
 
838
    #
 
839
    # Geocache Handling
 
840
    #
 
841
    ##############################################
 
842
 
 
843
    def save_cache_attribute(self, cache, attribute):
 
844
        if type(attribute) == tuple:
 
845
            for a in attribute:
 
846
                self.pointprovider.update_field(cache, a, cache.serialize_one(a), save=False)
 
847
            self.pointprovider.save()
 
848
        else:
 
849
            self.pointprovider.update_field(cache, attribute, cache.serialize_one(attribute))
 
850
 
 
851
 
 
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')
 
856
 
498
857
 
499
858
 
500
859
def determine_path ():
501
860
    """Borrowed from wxglade.py"""
502
861
    try:
503
862
        root = __file__
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))
507
866
    except:
508
 
        print "I'm sorry, but something is wrong."
509
 
        print "There is no __file__ variable. Please contact the author."
510
 
        sys.exit()
 
867
        logging.error("I'm sorry, but something is wrong.")
 
868
        logging.error("There is no __file__ variable. Please contact the author.")
 
869
        exit()
511
870
 
512
871
                        
 
872
 
513
873
def start():
 
874
    gobject.threads_init()
514
875
    Core(gui, determine_path())
515
876
 
 
877
def start_profile(what):
 
878
    import cProfile
 
879
    p = cProfile.Profile()
 
880
    p.run(what)
 
881
    stats = p.getstats()
 
882
    print "BY CALLS:\n------------------------------------------------------------"
 
883
    def c(x, y):
 
884
        if x.callcount < y.callcount:
 
885
            return 1
 
886
        elif x.callcount == y.callcount:
 
887
            return 0
 
888
        else:
 
889
            return -1
 
890
    stats.sort(cmp=c)
 
891
    for line in stats[:100]:
 
892
        print "%d %4f %s" % (line.callcount, line.totaltime, line.code)
 
893
        if line.calls == None:
 
894
            continue
 
895
        line.calls.sort(cmp=c)
 
896
        for line in line.calls[:10]:
 
897
            print "-- %d %4f %s" % (line.callcount, line.totaltime, line.code)
 
898
 
 
899
    
 
900
    print "BY TOTALTIME:\n------------------------------------------------------------"
 
901
    def c(x, y):
 
902
        if x.totaltime < y.totaltime:
 
903
            return 1
 
904
        elif x.totaltime == y.totaltime:
 
905
            return 0
 
906
        else:
 
907
            return -1
 
908
    stats.sort(cmp=c)
 
909
    for line in stats[:30]:
 
910
        print "%d %4f %s" % (line.callcount, line.totaltime, line.code)
 
911
        if line.calls == None:
 
912
            continue
 
913
        line.calls.sort(cmp=c)
 
914
        for line in line.calls[:10]:
 
915
            print "-- %d %4f %s" % (line.callcount, line.totaltime, line.code)
 
916
 
 
917
 
 
918
 
 
919
        
 
920
 
 
921
if __name__ == "__main__":
 
922
    if '--profile' in argv:
 
923
        start_profile('start()')
 
924
    else:
 
925
        start()
 
926