cf966ad8e5baffc41e130fef7eaf1f8e22344ed1
[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
22 class GigParser:
23
24     def parse_xml(self, xml, lat, long):
25         """ Parse xml into a dict """
26         # TODO: filter to todays events only
27         events_list = []
28         today = date.today()
29         dom = parseString(xml)
30
31         events = dom.getElementsByTagName('event')
32         for event in events:
33             title = event.getElementsByTagName('title')[0].childNodes[0].data
34             
35             artists_element = event.getElementsByTagName('artists')[0]
36             artist_list = []
37             for artist in artists_element.getElementsByTagName('artist'):
38                 artist_list.append(artist.childNodes[0].data)
39             artists = ', '.join(artist_list)
40
41             venue_details = event.getElementsByTagName('venue')[0]
42             venue_name = venue_details.getElementsByTagName('name')[0].childNodes[0].data
43             address = self.get_address(venue_details.getElementsByTagName('location')[0])
44             geo_data = venue_details.getElementsByTagName('geo:point')[0]
45             venue_lat = geo_data.getElementsByTagName('geo:lat')[0].childNodes[0].data
46             venue_long = geo_data.getElementsByTagName('geo:long')[0].childNodes[0].data
47             distance = location.distance_between(float(lat), 
48                                                  float(long), 
49                                                  float(venue_lat), 
50                                                  float(venue_long))
51             
52             start_date = self.parse_date(event.getElementsByTagName('startDate')[0].childNodes[0].data)
53             
54             events_list.append({'title': title,
55                                 'venue': venue_name,
56                                 'address': address,
57                                 'distance': distance,
58                                 'artists': artists,
59                                 'date': start_date})
60         return events_list
61     
62     def get_address(self, location):
63         """ Return the venues address details from the xml element """
64         street = ''
65         city = ''
66         country = ''
67         postalcode = ''
68         if location.getElementsByTagName('street')[0].childNodes:
69             street = location.getElementsByTagName('street')[0].childNodes[0].data
70         if location.getElementsByTagName('city')[0].childNodes:
71             city = location.getElementsByTagName('city')[0].childNodes[0].data
72         if location.getElementsByTagName('country')[0].childNodes:
73             country = location.getElementsByTagName('country')[0].childNodes[0].data
74         if location.getElementsByTagName('postalcode')[0].childNodes:
75             postalcode = location.getElementsByTagName('postalcode')[0].childNodes[0].data
76         return '\n'.join([street, city, country, postalcode])
77
78     def parse_date(self, date_string):
79         """ Parse date string into datetime object """
80         fmt =  "%a, %d %b %Y %H:%M:%S"
81         result = time.strptime(date_string, fmt)
82         return datetime(result.tm_year, 
83                         result.tm_mon, 
84                         result.tm_mday, 
85                         result.tm_hour, 
86                         result.tm_min, 
87                         result.tm_sec)
88
89
90 class GigFinder:
91
92     def __init__(self):
93         self.lat = None
94         self.long = None
95         self.url_base = "http://ws.audioscrobbler.com/2.0/"
96         self.api_key = "1928a14bdf51369505530949d8b7e1ee"
97         self.distance = '10'
98
99         program = hildon.Program.get_instance()
100         self.win = hildon.StackableWindow()
101         self.win.set_title('Gig Finder')
102         self.win.connect("destroy", gtk.main_quit, None)
103
104         pannable_area = hildon.PannableArea()
105
106         self.table = gtk.Table(columns=1)
107         self.table.set_row_spacings(10)
108         self.table.set_col_spacings(10)
109
110         pannable_area.add_with_viewport(self.table)
111
112         self.win.add(pannable_area)
113
114         self.update_gigs()
115
116     def main(self):
117         self.win.show_all()
118         gtk.main()
119
120     def update_gigs(self):
121         """ Get gig info """
122         parser = GigParser()
123         self.get_lat_long()
124         xml = self.get_xml()
125         events = parser.parse_xml(xml, self.lat, self.long)
126         self.win.set_title('Gig Finder (%s)' % len(events))
127         self.add_events(events)
128         
129     def get_xml(self):
130         """ Return xml from lastfm """
131         method = "geo.getevents"
132         params = urllib.urlencode({'method': method,
133                                    'api_key': self.api_key,
134                                    'distance': self.distance,
135                                    'long': self.long,
136                                    'lat': self.lat})
137         response = urllib.urlopen(self.url_base, params)
138         return response.read()
139
140     def get_lat_long(self):
141         """ Access gps and return current long lats """
142         # TODO: Improve geolocation code, very crude atm
143         control = location.GPSDControl.get_default()
144         device = location.GPSDevice()
145         control.start()
146         fix = device.fix
147         control.stop()
148         #self.lat, self.long = fix[4:6]
149         self.lat, self.long = ('51.546228', '-0.075016')
150
151     def show_details(self, widget, data):
152         """ Open new window showing gig details """
153         win = hildon.StackableWindow()
154         win.set_title(data['title'])
155
156         win.vbox = gtk.VBox()
157         win.add(win.vbox)
158
159         scroll = hildon.PannableArea()
160         win.vbox.pack_start(scroll, True, True, 0)
161
162         view = hildon.TextView()
163         view.set_editable(False)
164         view.unset_flags(gtk.CAN_FOCUS)
165         view.set_wrap_mode(gtk.WRAP_WORD)
166         buffer = view.get_buffer()
167         end = buffer.get_end_iter()
168         buffer.insert(end, '%s\n' % data['title'])
169         buffer.insert(end, 'Artists: %s\n' % data['artists'])
170         buffer.insert(end, 'Venue: %s\n' % data['venue'])
171         buffer.insert(end, '%s\n' % data['address'])
172         buffer.insert(end, 'When: %s\n' % data['date'].strftime('%H:%M'))
173         buffer.insert(end, '\n')
174         scroll.add_with_viewport(view)
175
176         win.show_all()
177         
178     def add_events(self, events):
179         """ Return table of buttons """
180         pos = 0
181         for event in events:
182             button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, 
183                                    hildon.BUTTON_ARRANGEMENT_VERTICAL)
184             button.set_text(event['title'], "distance: %0.02f km" % event['distance'])
185             button.connect("clicked", self.show_details, event)
186             self.table.attach(button, 0, 1, pos, pos+1)
187             pos += 1
188
189
190
191     #hildon.hildon_gtk_window_set_progress_indicator(win, 1)
192     #banner = hildon.hildon_banner_show_information(win,
193     #                                               "Updating", 
194     #                                               "Retrieving gig info")
195     #banner.hide()
196     #hildon.hildon_gtk_window_set_progress_indicator(win, 0)
197
198    
199 if __name__ == "__main__":
200     finder = GigFinder()
201     finder.main()