f9e9625f1e3a47cdb9e1ace238d222d90d837e00
[gigfinder] / gig_finder.py
1 #!/usr/bin/env python2.5
2
3 """Simple program to display local gigs
4
5 Intended for use on the N900, uses the devices gps to find local gigs.
6 """
7
8 __author__ = "Jon Staley"
9 __copyright__ = "Copyright 2010 Jon Staley"
10 __license__ = "MIT"
11 __version__ = "0.0.1"
12
13 from xml.dom.minidom import parseString
14 from datetime import datetime, date
15 import urllib
16 import time
17 import gtk
18 import hildon
19 import location
20 import time
21 import gobject
22
23 class GigParser:
24
25     def parse_xml(self, xml, lat, long):
26         """ Parse xml into a dict """
27         # TODO: filter to todays events only
28         events_list = []
29         today = date.today()
30         dom = parseString(xml)
31
32         events = dom.getElementsByTagName('event')
33         for event in events:
34             title = event.getElementsByTagName('title')[0].childNodes[0].data
35             
36             artists_element = event.getElementsByTagName('artists')[0]
37             artist_list = []
38             for artist in artists_element.getElementsByTagName('artist'):
39                 artist_list.append(artist.childNodes[0].data)
40             artists = ', '.join(artist_list)
41
42             venue_details = event.getElementsByTagName('venue')[0]
43             venue_name = venue_details.getElementsByTagName('name')[0].childNodes[0].data
44             address = self.get_address(venue_details.getElementsByTagName('location')[0])
45             geo_data = venue_details.getElementsByTagName('geo:point')[0]
46             venue_lat = geo_data.getElementsByTagName('geo:lat')[0].childNodes[0].data
47             venue_long = geo_data.getElementsByTagName('geo:long')[0].childNodes[0].data
48             distance = location.distance_between(float(lat), 
49                                                  float(long), 
50                                                  float(venue_lat), 
51                                                  float(venue_long))
52             
53             start_date = self.parse_date(event.getElementsByTagName('startDate')[0].childNodes[0].data)
54             
55             events_list.append({'title': title,
56                                 'venue': venue_name,
57                                 'address': address,
58                                 'distance': distance,
59                                 'artists': artists,
60                                 'date': start_date})
61         return events_list
62     
63     def get_address(self, location):
64         """ Return the venues address details from the xml element """
65         street = ''
66         city = ''
67         country = ''
68         postalcode = ''
69         if location.getElementsByTagName('street')[0].childNodes:
70             street = location.getElementsByTagName('street')[0].childNodes[0].data
71         if location.getElementsByTagName('city')[0].childNodes:
72             city = location.getElementsByTagName('city')[0].childNodes[0].data
73         if location.getElementsByTagName('country')[0].childNodes:
74             country = location.getElementsByTagName('country')[0].childNodes[0].data
75         if location.getElementsByTagName('postalcode')[0].childNodes:
76             postalcode = location.getElementsByTagName('postalcode')[0].childNodes[0].data
77         return '\n'.join([street, city, country, postalcode])
78
79     def parse_date(self, date_string):
80         """ Parse date string into datetime object """
81         fmt =  "%a, %d %b %Y %H:%M:%S"
82         result = time.strptime(date_string, fmt)
83         return datetime(result.tm_year, 
84                         result.tm_mon, 
85                         result.tm_mday, 
86                         result.tm_hour, 
87                         result.tm_min, 
88                         result.tm_sec)
89
90
91 class GigFinder:
92
93     def __init__(self):
94         self.lat = None
95         self.long = None
96         self.url_base = "http://ws.audioscrobbler.com/2.0/"
97         self.api_key = "1928a14bdf51369505530949d8b7e1ee"
98         self.distance = '10'
99         self.loop = gobject.MainLoop()
100
101         program = hildon.Program.get_instance()
102         self.win = hildon.StackableWindow()
103         self.win.set_title('Gig Finder')
104         self.win.connect("destroy", gtk.main_quit, None)
105
106         pannable_area = hildon.PannableArea()
107
108         self.table = gtk.Table(columns=1)
109         self.table.set_row_spacings(10)
110         self.table.set_col_spacings(10)
111
112         pannable_area.add_with_viewport(self.table)
113
114         self.win.add(pannable_area)
115         self.update_gigs()
116
117     def main(self):
118         self.win.show_all()
119         gtk.main()
120
121     def update_gigs(self):
122         """ Get gig info """
123         self.get_lat_long()
124         parser = GigParser()
125         xml = self.get_xml()
126         events = parser.parse_xml(xml, self.lat, self.long)
127         self.win.set_title('Gig Finder (%s)' % len(events))
128         self.add_events(events)
129         
130     def get_xml(self):
131         """ Return xml from lastfm """
132         method = "geo.getevents"
133         params = urllib.urlencode({'method': method,
134                                    'api_key': self.api_key,
135                                    'distance': self.distance,
136                                    'long': self.long,
137                                    'lat': self.lat})
138         response = urllib.urlopen(self.url_base, params)
139         return response.read()
140
141     def on_error(self, control, error, data):
142         print "location error: %d... quitting" % error
143         data.quit()
144
145     def on_changed(self, device, data):
146         if not device:
147             return
148         if device.fix:
149             if device.fix[1] & device.status:
150                 self.lat, self.long = device.fix[4:6]
151                 data.stop()
152
153     def on_stop(self, control, data):
154         print "quitting"
155         data.quit()
156
157     def start_location(self, data):
158         data.start()
159         return False
160
161     def get_lat_long(self):
162         """ Access gps and return current long lats """
163         # TODO: Improve geolocation code, very crude atm
164         control = location.GPSDControl.get_default()
165         device = location.GPSDevice()
166         device.reset_last_known()
167         control.set_properties(preferred_method=location.METHOD_USER_SELECTED,
168                                preferred_interval=location.INTERVAL_DEFAULT)
169         control.connect("error-verbose", self.on_error, self.loop)
170         device.connect("changed", self.on_changed, control)
171         control.connect("gpsd-stopped", self.on_stop, self.loop)
172         gobject.idle_add(self.start_location, control)
173         self.loop.run()
174         #self.lat, self.long = ('51.546228', '-0.075016')
175
176     def show_details(self, widget, data):
177         """ Open new window showing gig details """
178         win = hildon.StackableWindow()
179         win.set_title(data['title'])
180
181         win.vbox = gtk.VBox()
182         win.add(win.vbox)
183
184         scroll = hildon.PannableArea()
185         win.vbox.pack_start(scroll, True, True, 0)
186
187         view = hildon.TextView()
188         view.set_editable(False)
189         view.unset_flags(gtk.CAN_FOCUS)
190         view.set_wrap_mode(gtk.WRAP_WORD)
191         buffer = view.get_buffer()
192         end = buffer.get_end_iter()
193         buffer.insert(end, '%s\n' % data['title'])
194         buffer.insert(end, 'Artists: %s\n' % data['artists'])
195         buffer.insert(end, 'Venue: %s\n' % data['venue'])
196         buffer.insert(end, '%s\n' % data['address'])
197         buffer.insert(end, 'When: %s\n' % data['date'].strftime('%H:%M'))
198         buffer.insert(end, '\n')
199         scroll.add_with_viewport(view)
200
201         win.show_all()
202         
203     def add_events(self, events):
204         """ Return table of buttons """
205         pos = 0
206         for event in events:
207             button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, 
208                                    hildon.BUTTON_ARRANGEMENT_VERTICAL)
209             button.set_text(event['title'], "distance: %0.02f km" % event['distance'])
210             button.connect("clicked", self.show_details, event)
211             self.table.attach(button, 0, 1, pos, pos+1)
212             pos += 1
213
214
215
216     #hildon.hildon_gtk_window_set_progress_indicator(win, 1)
217     #banner = hildon.hildon_banner_show_information(win,
218     #                                               "Updating", 
219     #                                               "Retrieving gig info")
220     #banner.hide()
221     #hildon.hildon_gtk_window_set_progress_indicator(win, 0)
222
223    
224 if __name__ == "__main__":
225     finder = GigFinder()
226     finder.main()