1 #!/usr/bin/env python2.5
3 """Simple program to display local gigs
5 Intended for use on the N900, uses the devices gps to find local gigs.
8 __author__ = "Jon Staley"
9 __copyright__ = "Copyright 2010 Jon Staley"
13 from xml.dom.minidom import parseString
14 from datetime import datetime, date
23 gtk.gdk.threads_init()
27 def parse_xml(self, xml, lat, long):
28 """ Parse xml into a dict """
29 # TODO: filter to todays events only
32 dom = parseString(xml)
34 events = dom.getElementsByTagName('event')
36 title = event.getElementsByTagName('title')[0].childNodes[0].data
38 artists_element = event.getElementsByTagName('artists')[0]
40 for artist in artists_element.getElementsByTagName('artist'):
41 artist_list.append(artist.childNodes[0].data)
42 artists = ', '.join(artist_list)
44 venue_details = event.getElementsByTagName('venue')[0]
45 venue_name = venue_details.getElementsByTagName('name')[0].childNodes[0].data
46 address = self.get_address(venue_details.getElementsByTagName('location')[0])
47 geo_data = venue_details.getElementsByTagName('geo:point')[0]
48 venue_lat = geo_data.getElementsByTagName('geo:lat')[0].childNodes[0].data
49 venue_long = geo_data.getElementsByTagName('geo:long')[0].childNodes[0].data
50 distance = location.distance_between(float(lat),
55 start_date = self.parse_date(event.getElementsByTagName('startDate')[0].childNodes[0].data)
57 events_list.append({'title': title,
65 def get_address(self, location):
66 """ Return the venues address details from the xml element """
71 if location.getElementsByTagName('street')[0].childNodes:
72 street = location.getElementsByTagName('street')[0].childNodes[0].data
73 if location.getElementsByTagName('city')[0].childNodes:
74 city = location.getElementsByTagName('city')[0].childNodes[0].data
75 if location.getElementsByTagName('country')[0].childNodes:
76 country = location.getElementsByTagName('country')[0].childNodes[0].data
77 if location.getElementsByTagName('postalcode')[0].childNodes:
78 postalcode = location.getElementsByTagName('postalcode')[0].childNodes[0].data
79 return '\n'.join([street, city, country, postalcode])
81 def parse_date(self, date_string):
82 """ Parse date string into datetime object """
83 fmt = "%a, %d %b %Y %H:%M:%S"
84 result = time.strptime(date_string, fmt)
85 return datetime(result.tm_year,
92 class LocationUpdater:
97 self.loop = gobject.MainLoop()
99 self.control = location.GPSDControl.get_default()
100 self.control.set_properties(preferred_method=location.METHOD_USER_SELECTED,
101 preferred_interval=location.INTERVAL_DEFAULT)
102 self.control.connect("error-verbose", self.on_error, self.loop)
103 self.control.connect("gpsd-stopped", self.on_stop, self.loop)
105 self.device = location.GPSDevice()
106 self.device.connect("changed", self.on_changed, self.control)
108 def update_location(self):
109 """ Run the loop and update lat and long """
110 self.device.reset_last_known()
111 gobject.idle_add(self.start_location, self.control)
114 def on_error(self, control, error, data):
115 print "location error: %d... quitting" % error
118 def on_changed(self, device, data):
122 if device.fix[1] & location.GPS_DEVICE_LATLONG_SET:
123 self.lat, self.long = device.fix[4:6]
126 def on_stop(self, control, data):
130 def start_location(self, data):
139 self.url_base = "http://ws.audioscrobbler.com/2.0/"
140 self.api_key = "1928a14bdf51369505530949d8b7e1ee"
143 program = hildon.Program.get_instance()
144 self.win = hildon.StackableWindow()
145 self.win.set_title('Gig Finder')
146 self.win.connect("destroy", gtk.main_quit, None)
148 pannable_area = hildon.PannableArea()
150 self.table = gtk.Table(columns=1)
151 self.table.set_row_spacings(10)
152 self.table.set_col_spacings(10)
154 pannable_area.add_with_viewport(self.table)
156 self.win.add(pannable_area)
159 self.location_updater = LocationUpdater()
160 self.location_updater.update_location()
161 gobject.idle_add(self.update_gigs)
167 def update_gigs(self):
169 hildon.hildon_gtk_window_set_progress_indicator(self.win, 1)
172 while not location.GPS_DEVICE_LATLONG_SET:
177 events = parser.parse_xml(xml, self.location_updater.lat,
178 self.location_updater.long)
179 events = self.sort_gigs(events)
180 self.win.set_title('Gig Finder (%s)' % len(events))
181 self.add_events(events)
182 hildon.hildon_gtk_window_set_progress_indicator(self.win, 0)
184 def distance_cmp(self, x, y):
185 """ compare distances for list sort """
193 def sort_gigs(self, events):
194 """ sort gig by distance """
195 events.sort(cmp=self.distance_cmp, key=lambda x: x['distance'])
199 """ Return xml from lastfm """
200 method = "geo.getevents"
201 params = urllib.urlencode({'method': method,
202 'api_key': self.api_key,
203 'distance': self.distance,
204 'long': self.location_updater.long,
205 'lat': self.location_updater.lat})
206 response = urllib.urlopen(self.url_base, params)
207 return response.read()
210 def show_details(self, widget, data):
211 """ Open new window showing gig details """
212 win = hildon.StackableWindow()
213 win.set_title(data['title'])
215 win.vbox = gtk.VBox()
218 scroll = hildon.PannableArea()
219 win.vbox.pack_start(scroll, True, True, 0)
221 view = hildon.TextView()
222 view.set_editable(False)
223 view.unset_flags(gtk.CAN_FOCUS)
224 view.set_wrap_mode(gtk.WRAP_WORD)
225 buffer = view.get_buffer()
226 end = buffer.get_end_iter()
227 buffer.insert(end, '%s\n' % data['title'])
228 buffer.insert(end, 'Artists: %s\n' % data['artists'])
229 buffer.insert(end, 'Venue: %s\n' % data['venue'])
230 buffer.insert(end, '%s\n' % data['address'])
231 buffer.insert(end, 'When: %s\n' % data['date'].strftime('%H:%M'))
232 buffer.insert(end, '\n')
233 scroll.add_with_viewport(view)
237 def add_events(self, events):
238 """ Return table of buttons """
241 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
242 hildon.BUTTON_ARRANGEMENT_VERTICAL)
243 button.set_text(event['title'], "distance: %0.02f km" % event['distance'])
244 button.connect("clicked", self.show_details, event)
245 self.table.attach(button, 0, 1, pos, pos+1)
247 self.table.show_all()
249 #banner = hildon.hildon_banner_show_information(win,
251 # "Retrieving gig info")
253 if __name__ == "__main__":