Start refactoring code
[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.add_events(events)
127         
128     def get_xml(self):
129         """ Return xml from lastfm """
130         method = "geo.getevents"
131         params = urllib.urlencode({'method': method,
132                                    'api_key': self.api_key,
133                                    'distance': self.distance,
134                                    'long': self.long,
135                                    'lat': self.lat})
136         response = urllib.urlopen(self.url_base, params)
137         return response.read()
138
139     def get_lat_long(self):
140         """ Access gps and return current long lats """
141         # TODO: Improve geolocation code, very crude atm
142         control = location.GPSDControl.get_default()
143         device = location.GPSDevice()
144         control.start()
145         fix = device.fix
146         control.stop()
147         self.lat, self.long = fix[4:6]
148         #self.lat, self.long = ('51.546228', '-0.075016')
149
150     def show_details(self, widget, data):
151         """ Open new window showing gig details """
152         win = hildon.StackableWindow()
153         win.set_title(data['title'])
154
155         win.vbox = gtk.VBox()
156         win.add(win.vbox)
157
158         scroll = hildon.PannableArea()
159         win.vbox.pack_start(scroll, True, True, 0)
160
161         view = hildon.TextView()
162         view.set_editable(False)
163         view.unset_flags(gtk.CAN_FOCUS)
164         view.set_wrap_mode(gtk.WRAP_WORD)
165         buffer = view.get_buffer()
166         end = buffer.get_end_iter()
167         buffer.insert(end, '%s\n' % data['title'])
168         buffer.insert(end, 'Artists: %s\n' % data['artists'])
169         buffer.insert(end, 'Venue: %s\n' % data['venue'])
170         buffer.insert(end, data['address'])
171         buffer.insert(end, 'When: %s\n' % data['date'].strftime('%H:%M'))
172         buffer.insert(end, '\n')
173         scroll.add_with_viewport(view)
174
175         win.show_all()
176         
177     def add_events(self, events):
178         """ Return table of buttons """
179         pos = 0
180         for event in events:
181             button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, 
182                                    hildon.BUTTON_ARRANGEMENT_VERTICAL)
183             button.set_text(event['title'], "distance: %0.02f km" % event['distance'])
184             button.connect("clicked", self.show_details, event)
185             self.table.attach(button, 0, 1, pos, pos+1)
186             pos += 1
187
188
189
190     #hildon.hildon_gtk_window_set_progress_indicator(win, 1)
191     #banner = hildon.hildon_banner_show_information(win,
192     #                                               "Updating", 
193     #                                               "Retrieving gig info")
194     #banner.hide()
195     #hildon.hildon_gtk_window_set_progress_indicator(win, 0)
196
197    
198 if __name__ == "__main__":
199     finder = GigFinder()
200     finder.main()