adding fremantle version, and portfolio section
[stockthis] / stockthis.py
1 #!/usr/bin/env python
2 # -*- coding: UTF8 -*-
3 # Copyright (C) 2008 by Daniel Martin Yerga
4 # <dyerga@gmail.com>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #
19 # StocksPy: Application to get stocks data from Yahoo Finance.
20 # Version 0.1
21 #
22
23 import urllib2
24 import gtk, gobject
25
26 import hildon
27
28 #import osso
29 #osso_c = osso.Context("net.yerga.stockthis", "0.2", False)
30
31 from marketdata import markets, idmarket, localmarkets, localids
32
33 #detect if is ran locally or not
34 import sys
35 runningpath = sys.path[0]
36
37 if '/usr/share' in runningpath:
38     running_locally = False
39 else:
40     running_locally = True
41
42 if running_locally:
43     imgdir = 'pixmaps/'
44 else:
45     imgdir = '/usr/share/stockthis/pixmaps/'
46
47 loading_img = imgdir + 'loading.gif'
48
49 fhsize = gtk.HILDON_SIZE_FINGER_HEIGHT
50 horbtn = hildon.BUTTON_ARRANGEMENT_HORIZONTAL
51 ui_normal = gtk.HILDON_UI_MODE_NORMAL
52 winprogind = hildon.hildon_gtk_window_set_progress_indicator
53
54 gtk.gdk.threads_init()
55
56 class StocksPy:
57
58     def __init__(self):
59         self.program = hildon.Program()
60         self.program.__init__()
61         gtk.set_application_name("StockThis")
62         self.window = hildon.StackableWindow()
63         self.window.set_default_size(800, 480)
64         self.program.add_window(self.window)
65         self.window.connect("destroy", gtk.main_quit)
66
67         menu = hildon.AppMenu()
68         self.window.set_app_menu(menu)
69         button = gtk.Button("About")
70         button.connect("clicked", self.on_about)
71         menu.append(button)
72         menu.show_all()
73
74         vbox = gtk.VBox()
75         toolbar = self.main_toolbar(False)
76
77         parea = hildon.PannableArea()
78         tv = hildon.GtkTreeView(ui_normal)
79         inmodel = self.__create_model(markets, idmarket)
80         tv.connect("row-activated", self.show_instrument_view, inmodel)
81         tv.set_model(inmodel)
82         self._tv_columns(tv)
83         parea.add(tv)
84
85         vbox.pack_start(parea, True, True, 0)
86         vbox.pack_start(gtk.HSeparator(), False, False, 5)
87         vbox.pack_start(toolbar, False, False, 0)
88
89         self.window.add(vbox)
90         self.window.show_all()
91
92     def show_instrument_view(self, widget, path, column, inmodel):
93         market = inmodel[path][0]
94
95         names = localmarkets[idmarket.index(market)]
96         ids = localids[idmarket.index(market)]
97
98         window = hildon.StackableWindow()
99
100         vbox = gtk.VBox()
101         toolbar = self.main_toolbar(False)
102
103         parea = hildon.PannableArea()
104         tv = hildon.GtkTreeView(ui_normal)
105         model = self.__create_model(names, ids)
106         tv.connect("row-activated", self.show_quotes_view, model)
107         tv.set_model(model)
108         self._tv_columns(tv)
109         parea.add(tv)
110
111         vbox.pack_start(parea, True, True, 0)
112         vbox.pack_start(gtk.HSeparator(), False, False, 5)
113         vbox.pack_start(toolbar, False, False, 0)
114
115         window.add(vbox)
116         window.show_all()
117
118
119     def show_quotes_view(self, widget, path, column, model):
120         quote = model[path][0], model[path][1]
121
122         self.stocks_id = quote[0]
123         self.stocks_name = quote[1]
124         print quote
125
126         self.quotes_win = hildon.StackableWindow()
127
128         vbox = gtk.VBox()
129         toolbar = self.main_toolbar(True)
130
131
132         self.titlelbl = gtk.Label('')
133         self.titlelbl.set_markup('<b><big>' + quote[1].replace('&', '') +
134                                  '</big></b>')
135         color = gtk.gdk.color_parse("#03A5FF")
136         self.titlelbl.modify_fg(gtk.STATE_NORMAL, color)
137
138         parea = hildon.PannableArea()
139
140         vbox1 = gtk.VBox()
141
142         hbox = gtk.HBox()
143         label = gtk.Label('')
144         label.set_markup('<b><big>Price:</big></b>')
145         self.lprice = gtk.Label('')
146         hbox.pack_start(label, False, False, 50)
147         hbox.pack_start(self.lprice, False, False, 185)
148         vbox1.pack_start(hbox, True, True, 0)
149
150         hbox = gtk.HBox()
151         label = gtk.Label('')
152         label.set_markup('<b><big>Change:</big></b>')
153         self.lchange = gtk.Label('')
154         self.lpercent = gtk.Label('')
155         hbox.pack_start(label, False, False, 50)
156         hbox.pack_start(self.lchange, False, False, 145)
157         hbox.pack_start(self.lpercent, False, False, 0)
158         vbox1.pack_start(hbox, True, True, 0)
159
160         hbox = gtk.HBox()
161         label = gtk.Label('')
162         label.set_markup('<b><big>Volume:</big></b>')
163         self.lvolume = gtk.Label('')
164         hbox.pack_start(label, False, False, 50)
165         hbox.pack_start(self.lvolume, False, False, 145)
166         vbox1.pack_start(hbox, True, True, 0)
167
168         hbox = gtk.HBox()
169         label = gtk.Label('')
170         label.set_markup('<b><big>52 week high:</big></b>')
171         self.l52whigh = gtk.Label('')
172         hbox.pack_start(label, False, False, 50)
173         hbox.pack_start(self.l52whigh, False, False, 55)
174         vbox1.pack_start(hbox, True, True, 0)
175
176         hbox = gtk.HBox()
177         label = gtk.Label('')
178         label.set_markup('<b><big>52 week low:</big></b>')
179         self.l52wlow = gtk.Label('')
180         hbox.pack_start(label, False, False, 50)
181         hbox.pack_start(self.l52wlow, False, False, 70)
182         vbox1.pack_start(hbox, True, True, 0)
183
184         hbox = gtk.HBox()
185
186         button = hildon.PickerButton(fhsize, horbtn)
187         #FIXME: touchentry selector
188         data = ["50", "100", "200", "300", "400", "500", "600", "700", "800",
189                 "900", "1000"]
190         selector = self.create_selector(data)
191         button.set_selector(selector)
192         button.set_title("Shares")
193         #FIXME: check portfolio and see if there's quotes
194         button.set_value(data[0])
195         hbox.pack_start(button, True, True, 0)
196
197         button = hildon.Button(fhsize, horbtn)
198         button.set_title("Add to Portfolio")
199         #button.connect("clicked", self.publish_opt_screen)
200         hbox.pack_start(button, True, True, 0)
201         vbox1.pack_start(hbox, False, False, 0)
202
203         parea.add_with_viewport(vbox1)
204
205         vbox.pack_start(self.titlelbl, False, False, 0)
206         vbox.pack_start(gtk.HSeparator(), False, False, 0)
207         vbox.pack_start(parea, True, True, 0)
208         vbox.pack_start(gtk.HSeparator(), False, False, 5)
209         vbox.pack_start(toolbar, False, False, 0)
210
211         self.quotes_win.add(vbox)
212         self.quotes_win.show_all()
213         self.show_data(quote[0], self.quotes_win)
214
215     def create_selector(self, data):
216         selector = hildon.hildon_touch_selector_new_text()
217         for i in range(len(data)):
218             selector.append_text(data[i])
219
220         return selector
221
222     def show_data(self, quote, win):
223         import thread
224         winprogind(win, 1)
225         thread.start_new_thread(self.get_data, (quote, win))
226
227     def get_data(self, quote, win):
228         from ystockquote import ystockquote as yt
229         try:
230             data = yt.get_all(quote)
231         except:
232             print 'Failed to get internet data'
233             data = {'price': 'N/A', 'change': 'N/A', 'volume':'N/A',
234                     '52_week_high': 'N/A', '52_week_low': 'N/A'}
235             self.titlelbl.set_markup('<b><big>Failed to get data</big></b>')
236
237         try:
238             ch_percent = \
239                     100.0 * float(data['change'])/(float(data['price']) - \
240                     float(data['change']))
241         except ValueError:
242             ch_percent = 0.0
243
244         self.lprice.set_label(data['price'])
245         self.lchange.set_label(data['change'])
246         self.lpercent.set_label('%6.2f %%' % ch_percent)
247
248         if '-' in data['change']:
249             color = gtk.gdk.color_parse("#FF0000")
250         else:
251             color = gtk.gdk.color_parse("#16EB78")
252
253         self.lpercent.modify_fg(gtk.STATE_NORMAL, color)
254         self.lchange.modify_fg(gtk.STATE_NORMAL, color)
255
256         self.lvolume.set_label(data['volume'])
257         self.l52whigh.set_label(data['52_week_high'])
258         self.l52wlow.set_label(data['52_week_low'])
259
260         winprogind(win, 0)
261
262     def refresh_stock_data(self, widget):
263         self.show_data(self.stocks_id, self.quotes_win)
264
265
266     def show_graph_view(self, widget):
267         win = hildon.StackableWindow()
268
269         vbox = gtk.VBox()
270         toolbar = self.main_toolbar(False)
271
272         self.graphs_title = gtk.Label(self.stocks_name)
273         color = gtk.gdk.color_parse("#03A5FF")
274         self.graphs_title.modify_fg(gtk.STATE_NORMAL, color)
275
276         parea = hildon.PannableArea()
277
278         hbox = gtk.HBox()
279         hbox.set_homogeneous(True)
280
281         button = hildon.Button(fhsize, horbtn)
282         button.set_label('1d')
283         button.connect("clicked", self.show_graph, '1d', win)
284         hbox.pack_start(button)
285
286         button = hildon.Button(fhsize, horbtn)
287         button.set_label('5d')
288         button.connect("clicked", self.show_graph, '5d', win)
289         hbox.pack_start(button)
290
291         button = hildon.Button(fhsize, horbtn)
292         button.set_label('3m')
293         button.connect("clicked", self.show_graph, '3m', win)
294         hbox.pack_start(button)
295
296         button = hildon.Button(fhsize, horbtn)
297         button.set_label('6m')
298         button.connect("clicked", self.show_graph, '6m', win)
299         hbox.pack_start(button)
300
301         button = hildon.Button(fhsize, horbtn)
302         button.set_label('1y')
303         button.connect("clicked", self.show_graph, '1y', win)
304         hbox.pack_start(button)
305
306         button = hildon.Button(fhsize, horbtn)
307         button.set_label('2y')
308         button.connect("clicked", self.show_graph, '2y', win)
309         hbox.pack_start(button)
310
311         button = hildon.Button(fhsize, horbtn)
312         button.set_label('5y')
313         button.connect("clicked", self.show_graph, '5y', win)
314         hbox.pack_start(button)
315
316         button = hildon.Button(fhsize, horbtn)
317         button.set_label('Max')
318         button.connect("clicked", self.show_graph, 'max', win)
319         hbox.pack_start(button)
320
321         vbox1 = gtk.VBox()
322         vbox1.pack_start(hbox, False, False, 0)
323
324         self.graph = gtk.Image()
325         vbox1.pack_start(self.graph, True, True, 0)
326
327         parea.add_with_viewport(vbox1)
328
329         vbox.pack_start(self.graphs_title, False, False, 0)
330         vbox.pack_start(gtk.HSeparator(), False, False, 0)
331         vbox.pack_start(parea, True, True, 0)
332         vbox.pack_start(gtk.HSeparator(), False, False, 5)
333         vbox.pack_start(toolbar, False, False, 0)
334
335         win.add(vbox)
336         win.show_all()
337
338         self.show_graph(None, '1d', win)
339
340     def show_graph(self, widget, option, win):
341         import thread
342         winprogind(win, 1)
343         thread.start_new_thread(self.get_graph_data, (option, win))
344
345     def get_graph_data(self, option, win):
346         kind = self.stocks_id
347         if option == '1d':
348             url = 'http://uk.ichart.yahoo.com/b?s=%s' % kind
349         elif option == '5d':
350             url = 'http://uk.ichart.yahoo.com/w?s=%s' % kind
351         elif option == '3m':
352             url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % kind.lower()
353         elif option == '6m':
354             url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % kind.lower()
355         elif option == '1y':
356             url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % kind.lower()
357         elif option == '2y':
358             url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % kind.lower()
359         elif option == '5y':
360             url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % kind.lower()
361         elif option == 'max':
362             url = 'http://chart.finance.yahoo.com/c/my/s/%s' % kind.lower()
363
364         try:
365             myimg = urllib2.urlopen(url)
366             imgdata = myimg.read()
367
368             pbl = gtk.gdk.PixbufLoader()
369             pbl.write(imgdata)
370
371             pbuf = pbl.get_pixbuf()
372             pbl.close()
373             self.graph.set_from_pixbuf(pbuf)
374             winprogind(win, 0)
375         except:
376             winprogind(win, 0)
377             self.graphs_title.set_label('Failed to get data')
378             self.graph.destroy()
379
380
381
382     def _tv_columns(self, treeview):
383         column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
384         column.set_visible(False)
385         treeview.append_column(column)
386
387         column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
388         treeview.append_column(column)
389
390     def __create_model(self, names, ids):
391         lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
392         for item in range(len(names)):
393             iter = lstore.append()
394             lstore.set(iter, 0, ids[item], 1, names[item])
395         return lstore
396
397     def main_toolbar(self, quotesview):
398         toolbar = gtk.HBox()
399         toolbar.set_homogeneous(True)
400
401         portfolio_btn = hildon.Button(fhsize, horbtn)
402         portfolio_btn.set_title("Portfolio")
403         portfolio_btn.connect("clicked", self.show_portfolio_view)
404
405         graph_btn = hildon.Button(fhsize, horbtn)
406         graph_btn.set_title("Graph")
407         graph_btn.connect("clicked", self.show_graph_view)
408
409         refresh_btn = hildon.Button(fhsize, horbtn)
410         refresh_btn.set_title("Refresh")
411         refresh_btn.connect("clicked", self.refresh_stock_data)
412
413         toolbar.pack_start(portfolio_btn)
414         if quotesview:
415             toolbar.pack_start(graph_btn)
416             toolbar.pack_start(refresh_btn)
417
418         toolbar.show_all()
419
420         return toolbar
421
422     def show_portfolio_view(self, widget):
423         #FIXME: get data from sqlite settings
424         data = [["SAN.MC", "BANCO SANTANDER R", "200", "-"],
425                 ["BBVA.MC", "BBVA R", "300", "-"]]
426         win = hildon.StackableWindow()
427
428         vbox = gtk.VBox()
429
430         parea = hildon.PannableArea()
431         tv = hildon.GtkTreeView(ui_normal)
432         tv.set_headers_visible(True)
433         inmodel = self._create_portfolio_model(data)
434         #tv.connect("row-activated", self.show_instrument_view, inmodel)
435         tv.set_model(inmodel)
436         self._tv_portfolio_columns(tv)
437         parea.add(tv)
438
439         button = hildon.Button(fhsize, horbtn)
440         button.set_title("Refresh All")
441         button.connect("clicked", self.refresh_portfolio, tv, win, data)
442
443         vbox.pack_start(parea, True, True, 0)
444         vbox.pack_start(button, False, False, 0)
445         win.add(vbox)
446         win.show_all()
447
448     def refresh_portfolio(self, widget, tv, win, data):
449         import thread
450         winprogind(win, 1)
451         thread.start_new_thread(self._do_refresh_portfolio, (data, tv, win))
452
453
454     def _do_refresh_portfolio(self, data, tv, win):
455         for item in data:
456             item[3] = self.get_price(item[0])
457
458         model = self._create_portfolio_model(data)
459         tv.set_model(model)
460         winprogind(win, 0)
461
462     def get_price(self, symbol):
463         from ystockquote import ystockquote as yt
464         price = yt.get_price(symbol)
465
466         return price
467
468     def _create_portfolio_model(self, data):
469         lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
470                                 gobject.TYPE_STRING, gobject.TYPE_STRING)
471         for item in data:
472             iter = lstore.append()
473             lstore.set(iter, 0, item[0], 1, item[1], 2, item[2], 3, item[3])
474         return lstore
475
476     def _tv_portfolio_columns(self, treeview):
477         column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
478         column.set_visible(False)
479         treeview.append_column(column)
480
481         column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
482         column.set_property("expand", True)
483         treeview.append_column(column)
484
485         column = gtk.TreeViewColumn('Shares', gtk.CellRendererText(), text=2)
486         treeview.append_column(column)
487
488         column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3)
489         treeview.append_column(column)
490
491     def on_about(self, widget):
492         dialog = gtk.AboutDialog()
493         dialog.set_name("StockThis")
494         dialog.set_version("0.2")
495         dialog.set_copyright("Copyright © 2009")
496         dialog.set_website("http://stockthis.garage.maemo.org")
497         dialog.set_authors(["Daniel Martin Yerga <dyerga@gmail.com>"])
498         logo = gtk.gdk.pixbuf_new_from_file(imgdir + "stockthis.png")
499         dialog.set_logo(logo)
500         dialog.set_license("This program is released under the GNU\nGeneral Public License. Please visit \nhttp://www.gnu.org/copyleft/gpl.html\nfor details.")
501         dialog.set_artists(["Logo by Daniel Martin Yerga"])
502         dialog.run()
503         dialog.destroy()
504
505 if __name__ == "__main__":
506     stockspy = StocksPy()
507     gtk.gdk.threads_enter()
508     gtk.main()
509     gtk.gdk.threads_leave()