1 #!/usr/bin/env python2.5
3 # Copyright (C) 2008 by Daniel Martin Yerga
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.
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.
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.
19 # StocksPy: Application to get stocks data from Yahoo Finance.
23 _version = "StockThis 0.3 alpha1 rev1"
35 osso_c = osso.Context("net.yerga.stockthis", "0.3", False)
37 #detect if is ran locally or not
38 runningpath = sys.path[0]
40 if '/usr/share' in runningpath:
41 runninglocally = False
45 HOME = os.path.expanduser("~")
47 settingsdb, imgdir, configdir, logfile = \
48 settings.define_paths(runninglocally, HOME)
51 logger = logging.getLogger('st')
52 logging.basicConfig(filename=logfile,level=logging.ERROR, filemode='w')
57 #set the main logger to DEBUG
58 logger.setLevel(logging.DEBUG)
60 #Create a handler for console debug
61 console = logging.StreamHandler()
62 console.setLevel(logging.DEBUG)
63 # set a format which is simpler for console use
64 formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
65 # tell the handler to use this format
66 console.setFormatter(formatter)
67 logging.getLogger('').addHandler(console)
69 fhsize = gtk.HILDON_SIZE_FINGER_HEIGHT
70 horbtn = hildon.BUTTON_ARRANGEMENT_HORIZONTAL
71 ui_normal = gtk.HILDON_UI_MODE_NORMAL
72 ui_edit = gtk.HILDON_UI_MODE_EDIT
73 winprogind = hildon.hildon_gtk_window_set_progress_indicator
75 logger.debug("test this log")
77 gtk.gdk.threads_init()
82 self.program = hildon.Program()
83 self.program.__init__()
84 gtk.set_application_name("StockThis")
85 self.window = hildon.StackableWindow()
86 self.window.set_default_size(800, 480)
87 self.program.add_window(self.window)
88 self.window.connect("destroy", gtk.main_quit)
90 self.create_menu(self.window)
93 toolbar = self.main_toolbar(False, False, None, '', '')
95 parea = hildon.PannableArea()
96 tv = hildon.GtkTreeView(ui_normal)
97 inmodel = self.__create_model(marketdata.main, marketdata.idmain)
98 tv.connect("row-activated", self.show_instrument_view, inmodel,
99 marketdata.localmarkets, marketdata.localids,
101 tv.set_model(inmodel)
105 vbox.pack_start(parea, True, True, 0)
106 vbox.pack_start(gtk.HSeparator(), False, False, 5)
107 vbox.pack_start(toolbar, False, False, 0)
109 self.window.add(vbox)
110 self.window.show_all()
112 def create_menu(self, window):
113 menu = hildon.AppMenu()
114 window.set_app_menu(menu)
115 button = gtk.Button("About")
116 button.connect("clicked", About)
118 button = gtk.Button("Log")
119 button.connect("clicked", Log, logfile)
123 def show_instrument_view(self, widget, path, column, inmodel, names,
125 market = inmodel[path][0]
126 names = names[mindex.index(market)]
127 ids = ids[mindex.index(market)]
129 window = hildon.StackableWindow()
130 self.create_menu(window)
131 window.set_title("StockThis - " + inmodel[path][1])
134 toolbar = self.main_toolbar(False, False, None, '', '')
136 parea = hildon.PannableArea()
137 tv = hildon.GtkTreeView(ui_normal)
138 model = self.__create_model(names, ids)
139 tv.connect("row-activated", self.show_quotes_view, model, False)
144 vbox.pack_start(parea, True, True, 0)
145 vbox.pack_start(gtk.HSeparator(), False, False, 5)
146 vbox.pack_start(toolbar, False, False, 0)
151 def show_quotes_view(self, widget, path, column, model, portfolio):
152 quote = model[path][0], model[path][1]
153 #print "quote:", quote[0]
154 #('EURUSD=X', 'EUR/USD')
156 #Currencies and ETFs should show the list now -> view = True
157 #Other items show a new list with options
159 for i in marketdata.localids[(len(marketdata.localids)-2):]:
166 if quote[0] in marketdata.idindexes:
167 self.show_instrument_view(widget, path, column, model,
168 marketdata.wnamesindexes,
169 marketdata.widsindexes,
170 marketdata.idindexes)
172 if quote[0] in marketdata.idotmarkets:
173 self.show_instrument_view(widget, path, column, model,
175 marketdata.omsymbols,
176 marketdata.idotmarkets)
178 if quote[0] in marketdata.ideumarkets:
179 self.show_instrument_view(widget, path, column, model,
181 marketdata.eusymbols,
182 marketdata.ideumarkets)
184 if quote[0] in marketdata.idusmarkets:
185 self.show_instrument_view(widget, path, column, model,
187 marketdata.ussymbols,
188 marketdata.idusmarkets)
192 win = hildon.StackableWindow()
193 self.create_menu(win)
194 win.set_title("StockThis - Quotes View - " + quote[1])
198 ltitle = gtk.Label('')
199 ltitle.set_markup('<b><big>' + quote[1].replace('&', '') +
201 color = gtk.gdk.color_parse("#03A5FF")
202 ltitle.modify_fg(gtk.STATE_NORMAL, color)
204 parea = hildon.PannableArea()
209 label = gtk.Label('')
210 label.set_markup('<b><big>Price:</big></b>')
211 lprice = gtk.Label('')
212 hbox.pack_start(label, False, False, 20)
213 hbox.pack_start(lprice, False, False, 245)
214 vbox1.pack_start(hbox, True, True, 0)
217 label = gtk.Label('')
218 label.set_markup('<b><big>Change:</big></b>')
219 lchange = gtk.Label('')
220 lpercent = gtk.Label('')
221 hbox.pack_start(label, False, False, 20)
222 hbox.pack_start(lchange, False, False, 205)
223 hbox.pack_start(lpercent, False, False, 0)
224 vbox1.pack_start(hbox, True, True, 0)
227 label = gtk.Label('')
228 label.set_markup('<b><big>Volume:</big></b>')
229 lvolume = gtk.Label('')
230 hbox.pack_start(label, False, False, 20)
231 hbox.pack_start(lvolume, False, False, 207)
232 vbox1.pack_start(hbox, True, True, 0)
235 label = gtk.Label('')
236 label.set_markup('<b><big>52 week high:</big></b>')
237 l52whigh = gtk.Label('')
238 hbox.pack_start(label, False, False, 20)
239 hbox.pack_start(l52whigh, False, False, 110)
240 vbox1.pack_start(hbox, True, True, 0)
243 label = gtk.Label('')
244 label.set_markup('<b><big>52 week low:</big></b>')
245 l52wlow = gtk.Label('')
246 hbox.pack_start(label, False, False, 20)
247 hbox.pack_start(l52wlow, False, False, 125)
248 vbox1.pack_start(hbox, True, True, 0)
251 button1 = hildon.PickerButton(fhsize, horbtn)
252 data = ["50", "100", "200", "300", "400", "500", "600", "700", "800",
254 selector = self.create_selector(data, True)
255 button1.set_selector(selector)
256 button1.set_title("Your shares")
257 shares = self.get_shares_from_symbol(quote[0])
258 button1.set_value(shares)
259 hbox.pack_start(button1, True, True, 0)
261 button = hildon.Button(fhsize, horbtn)
262 button.set_title("Add to Portfolio")
263 button.connect("clicked", self.add_to_portfolio, button1, quote[0], quote[1])
264 hbox.pack_start(button, True, True, 0)
267 label = gtk.Label('')
268 label.set_markup('<b><big>Shares:</big></b>')
269 lshares = gtk.Label(shares)
270 hbox1.pack_start(label, False, False, 20)
271 hbox1.pack_start(lshares, False, False, 215)
274 label = gtk.Label('')
275 label.set_markup('<b><big>Holdings Value:</big></b>')
276 holdingsvalue = gtk.Label("")
277 hbox2.pack_start(label, False, False, 20)
278 hbox2.pack_start(holdingsvalue, False, False, 85)
281 label = gtk.Label('')
282 label.set_markup("<b><big>Day's Value Change:</big></b>")
283 dayvaluechange = gtk.Label("")
284 hbox3.pack_start(label, False, False, 20)
285 hbox3.pack_start(dayvaluechange, False, False, 10)
288 vbox1.pack_start(hbox, False, False, 0)
290 vbox1.pack_start(hbox1, True, True, 0)
291 vbox1.pack_start(hbox2, True, True, 0)
292 vbox1.pack_start(hbox3, True, True, 0)
294 parea.add_with_viewport(vbox1)
296 widgets = [win, ltitle, lprice, lchange, lpercent, lvolume, l52whigh,
297 l52wlow, lshares, holdingsvalue, dayvaluechange]
299 toolbar = self.main_toolbar(True, portfolio, widgets, quote[0], quote[1])
301 vbox.pack_start(ltitle, False, False, 0)
302 vbox.pack_start(gtk.HSeparator(), False, False, 0)
303 vbox.pack_start(parea, True, True, 0)
304 vbox.pack_start(gtk.HSeparator(), False, False, 5)
305 vbox.pack_start(toolbar, False, False, 0)
310 self.show_data(quote[0], widgets, shares)
312 def get_shares_from_symbol(self, symbol):
315 portfolio_data = settings.load_portfolio(settingsdb)
316 for item in portfolio_data :
321 logger.exception("Getting shares from symbol")
324 def add_to_portfolio(self, widget, button, symbol, name):
325 shares = button.get_value()
328 portfolio = settings.load_portfolio(settingsdb)
330 for item in portfolio:
332 index = portfolio.index(item)
334 item = [symbol, name, shares, '-']
337 settings.insert_new_item_to_portfolio(settingsdb, item)
339 settings.delete_item_from_portfolio(settingsdb, symbol)
340 settings.insert_new_item_to_portfolio(settingsdb, item)
342 self.show_info_banner(widget, "Added to portfolio")
344 logger.exception("Adding to portfolio")
345 self.show_info_banner(widget, "Error adding to portfolio")
348 def create_selector(self, data, entry):
350 selector = hildon.TouchSelectorEntry(text=True)
352 selector = hildon.hildon_touch_selector_new_text()
353 for i in range(len(data)):
354 selector.append_text(data[i])
358 def show_data(self, symbol, widgets, shares):
360 winprogind(widgets[0], 1)
361 thread.start_new_thread(self.get_data, (symbol, widgets, shares))
363 def get_data(self, symbol, widgets, shares):
364 from ystockquote import ystockquote as yt
365 win, ltitle, lprice, lchange, lpercent, lvolume, l52whigh, l52wlow, lshares, holdingsvalue, dayvaluechange = widgets
368 data = yt.get_all(symbol)
370 logger.exception("Getting data from Yahoo")
371 data = {'price': 'N/A', 'change': 'N/A', 'volume':'N/A',
372 '52_week_high': 'N/A', '52_week_low': 'N/A'}
373 ltitle.set_markup('<b><big>Failed to get data</big></b>')
377 100.0 * float(data['change'])/(float(data['price']) - \
378 float(data['change']))
382 lprice.set_label(data['price'])
383 lchange.set_label(data['change'])
384 lpercent.set_label('%6.2f %%' % ch_percent)
386 if '-' in data['change']:
387 color = gtk.gdk.color_parse("#FF0000")
389 color = gtk.gdk.color_parse("#16EB78")
391 lpercent.modify_fg(gtk.STATE_NORMAL, color)
392 lchange.modify_fg(gtk.STATE_NORMAL, color)
394 lvolume.set_label(data['volume'])
395 l52whigh.set_label(data['52_week_high'])
396 l52wlow.set_label(data['52_week_low'])
399 daychange = float(shares)*float(data['change'])
403 holdvalue = float(shares)*float(data['price'])
407 dayvaluechange.set_label(str(daychange))
408 holdingsvalue.set_label(str(holdvalue))
412 def refresh_stock_data(self, widget, portfolio, widgets, symbol):
414 shares = self.get_shares_from_symbol(symbol)
418 self.show_data(symbol, widgets, shares)
420 def show_graph_view(self, widget, symbol, name):
421 win = hildon.StackableWindow()
422 self.create_menu(win)
423 win.set_title("StockThis - Graph View - " + name)
426 toolbar = self.main_toolbar(False, True, None, '', '')
428 self.graphs_title = gtk.Label(name)
429 color = gtk.gdk.color_parse("#03A5FF")
430 self.graphs_title.modify_fg(gtk.STATE_NORMAL, color)
432 parea = hildon.PannableArea()
435 hbox.set_homogeneous(True)
437 button = hildon.Button(fhsize, horbtn)
438 button.set_label('1d')
439 button.connect("clicked", self.show_graph, '1d', win, symbol)
440 hbox.pack_start(button)
442 button = hildon.Button(fhsize, horbtn)
443 button.set_label('5d')
444 button.connect("clicked", self.show_graph, '5d', win, symbol)
445 hbox.pack_start(button)
447 button = hildon.Button(fhsize, horbtn)
448 button.set_label('3m')
449 button.connect("clicked", self.show_graph, '3m', win, symbol)
450 hbox.pack_start(button)
452 button = hildon.Button(fhsize, horbtn)
453 button.set_label('6m')
454 button.connect("clicked", self.show_graph, '6m', win, symbol)
455 hbox.pack_start(button)
457 button = hildon.Button(fhsize, horbtn)
458 button.set_label('1y')
459 button.connect("clicked", self.show_graph, '1y', win, symbol)
460 hbox.pack_start(button)
462 button = hildon.Button(fhsize, horbtn)
463 button.set_label('2y')
464 button.connect("clicked", self.show_graph, '2y', win, symbol)
465 hbox.pack_start(button)
467 button = hildon.Button(fhsize, horbtn)
468 button.set_label('5y')
469 button.connect("clicked", self.show_graph, '5y', win, symbol)
470 hbox.pack_start(button)
472 button = hildon.Button(fhsize, horbtn)
473 button.set_label('Max')
474 button.connect("clicked", self.show_graph, 'max', win, symbol)
475 hbox.pack_start(button)
478 vbox1.pack_start(hbox, False, False, 0)
480 self.graph = gtk.Image()
481 vbox1.pack_start(self.graph, True, True, 0)
483 parea.add_with_viewport(vbox1)
485 vbox.pack_start(self.graphs_title, False, False, 0)
486 vbox.pack_start(gtk.HSeparator(), False, False, 0)
487 vbox.pack_start(parea, True, True, 0)
488 vbox.pack_start(gtk.HSeparator(), False, False, 5)
489 vbox.pack_start(toolbar, False, False, 0)
494 self.show_graph(None, '1d', win, symbol)
496 def show_graph(self, widget, option, win, symbol):
499 thread.start_new_thread(self.get_graph_data, (option, win, symbol))
501 def get_graph_data(self, option, win, symbol):
503 url = 'http://uk.ichart.yahoo.com/b?s=%s' % symbol
505 url = 'http://uk.ichart.yahoo.com/w?s=%s' % symbol
507 url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % symbol.lower()
509 url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % symbol.lower()
511 url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % symbol.lower()
513 url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % symbol.lower()
515 url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % symbol.lower()
516 elif option == 'max':
517 url = 'http://chart.finance.yahoo.com/c/my/s/%s' % symbol.lower()
520 myimg = urllib2.urlopen(url)
521 imgdata = myimg.read()
523 pbl = gtk.gdk.PixbufLoader()
526 pbuf = pbl.get_pixbuf()
528 self.graph.set_from_pixbuf(pbuf)
531 logger.exception("Getting graph data")
533 self.graphs_title.set_label('Failed to get data')
536 def _tv_columns(self, treeview):
537 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
538 column.set_visible(False)
539 treeview.append_column(column)
541 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
542 treeview.append_column(column)
544 def __create_model(self, names, ids):
545 lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
546 for item in range(len(names)):
547 iter = lstore.append()
548 lstore.set(iter, 0, ids[item], 1, names[item])
551 def main_toolbar(self, quotesview, portfolio, widgets, symbol, name):
553 toolbar.set_homogeneous(True)
555 portfolio_btn = hildon.Button(fhsize, horbtn)
556 portfolio_btn.set_title("Portfolio")
557 portfolio_btn.connect("clicked", self.show_portfolio_view)
559 graph_btn = hildon.Button(fhsize, horbtn)
560 graph_btn.set_title("Graph")
561 graph_btn.connect("clicked", self.show_graph_view, symbol, name)
563 refresh_btn = hildon.Button(fhsize, horbtn)
564 refresh_btn.set_title("Refresh")
565 refresh_btn.connect("clicked", self.refresh_stock_data, portfolio,
569 toolbar.pack_start(portfolio_btn)
571 toolbar.pack_start(graph_btn)
572 toolbar.pack_start(refresh_btn)
578 def show_portfolio_view(self, widget):
579 data = settings.load_portfolio(settingsdb)
581 win = hildon.StackableWindow()
582 self.create_menu(win)
583 win.set_title("StockThis - Portfolio")
587 parea = hildon.PannableArea()
588 tv = hildon.GtkTreeView(ui_normal)
589 tv.set_headers_visible(True)
590 self.portfolio_model = self._create_portfolio_model(data)
591 tv.connect("row-activated", self.show_quotes_view, self.portfolio_model, True)
592 tv.set_model(self.portfolio_model)
593 self._tv_portfolio_columns(tv)
597 button = hildon.Button(fhsize, horbtn)
598 button.set_title("Refresh All")
599 button.connect("clicked", self.refresh_portfolio, tv, win)
600 hbox.pack_start(button, True, True, 0)
602 button = hildon.Button(fhsize, horbtn)
603 button.set_title("Remove")
604 button.connect("clicked", self.remove_item)
605 hbox.pack_start(button, True, True, 0)
607 vbox.pack_start(parea, True, True, 0)
608 vbox.pack_start(hbox, False, False, 0)
612 def remove_item(self, widget):
613 win = hildon.StackableWindow()
615 toolbar = hildon.EditToolbar("Choose items to delete", "Delete")
616 win.set_edit_toolbar(toolbar)
619 parea = hildon.PannableArea()
620 tv = hildon.GtkTreeView(ui_edit)
621 selection = tv.get_selection()
622 selection.set_mode(gtk.SELECTION_MULTIPLE)
623 tv.set_model(self.portfolio_model)
624 self._tv_remove_portfolio_columns(tv)
627 toolbar.connect("button-clicked", self.delete_from_portfolio, win, tv,
629 toolbar.connect_object("arrow-clicked", gtk.Window.destroy, win)
631 vbox.pack_start(parea, True, True, 0)
635 def delete_from_portfolio(self, widget, win, tv, selection):
636 if not self.is_treeview_selected(tv):
639 conf = self.show_confirmation(win, "Delete items?")
643 selmodel, selected = selection.get_selected_rows()
644 iters = [selmodel.get_iter(path) for path in selected]
646 symbol = selmodel.get_value(i, 0)
647 settings.delete_item_from_portfolio(settingsdb, symbol)
650 logger.exception("Deleting item from portfolio")
651 self.info_banner(widget, "Error deleting item")
653 def _tv_remove_portfolio_columns(self, treeview):
654 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
655 column.set_visible(False)
656 treeview.append_column(column)
658 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
659 column.set_property("expand", True)
660 treeview.append_column(column)
662 column = gtk.TreeViewColumn('Shares', gtk.CellRendererText(), text=2)
663 column.set_visible(False)
664 treeview.append_column(column)
666 column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3)
667 column.set_visible(False)
668 treeview.append_column(column)
670 def refresh_portfolio(self, widget, tv, win):
671 data = settings.load_portfolio(settingsdb)
674 thread.start_new_thread(self._do_refresh_portfolio, (data, tv, win))
676 def _do_refresh_portfolio(self, data, tv, win):
678 item[3] = self.get_price(item[0])
680 self.portfolio_model = self._create_portfolio_model(data)
681 tv.set_model(self.portfolio_model)
684 def get_price(self, symbol):
685 from ystockquote import ystockquote as yt
687 price = yt.get_price(symbol)
690 logger.exception("Getting price from Yahoo")
693 def _create_portfolio_model(self, data):
694 lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
695 gobject.TYPE_STRING, gobject.TYPE_STRING)
697 iter = lstore.append()
698 lstore.set(iter, 0, item[0], 1, item[1], 2, item[2], 3, item[3])
701 def _tv_portfolio_columns(self, treeview):
702 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
703 column.set_visible(False)
704 treeview.append_column(column)
706 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
707 column.set_property("expand", True)
708 treeview.append_column(column)
710 column = gtk.TreeViewColumn('Shares', gtk.CellRendererText(), text=2)
711 treeview.append_column(column)
713 column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3)
714 treeview.append_column(column)
716 def show_confirmation(self, window, msg):
717 dialog = hildon.hildon_note_new_confirmation(window, msg)
719 result = dialog.run()
720 if result == gtk.RESPONSE_OK:
727 def show_info_banner(self, widget, msg):
728 hildon.hildon_banner_show_information(widget, 'qgn_note_infoprint', msg)
730 def is_treeview_selected(self, treeview):
731 selection = treeview.get_selection()
732 if selection.count_selected_rows() == 0:
733 self.show_info_banner(treeview, 'No selected item')
740 def __init__(self, widget):
741 dialog = gtk.Dialog(title='About', parent=None, flags=0)
742 dialog.set_has_separator(False)
743 dialog.set_size_request(-1, 400)
745 self.info_lb = gtk.Label()
746 self.info_lb.set_line_wrap(True)
752 button = hildon.Button(fhsize, horbtn)
753 button.set_title('Description')
754 button.connect("clicked", self.show_info, 'description')
755 hbox1.pack_start(button, True, True, 0)
757 button = hildon.Button(fhsize, horbtn)
758 button.set_title('Credits')
759 button.connect("clicked", self.show_info, 'credits')
760 hbox1.pack_start(button, True, True, 0)
762 button = hildon.Button(fhsize, horbtn)
763 button.set_title('License')
764 button.connect("clicked", self.show_info, 'license')
765 hbox1.pack_start(button, True, True, 0)
767 button = hildon.Button(fhsize, horbtn)
768 button.set_title('Donate')
769 button.connect("clicked", self.show_info, 'donate')
770 hbox1.pack_start(button, True, True, 0)
772 button = hildon.Button(fhsize, horbtn)
773 button.set_title('Report ')
774 button.connect("clicked", self.show_info, 'report')
775 hbox1.pack_start(button, True, True, 0)
777 button = hildon.Button(fhsize, horbtn)
778 button.set_title(' Vote ')
779 button.connect("clicked", self.show_info, 'vote')
780 hbox1.pack_start(button, True, True, 0)
782 self.action_btn = hildon.Button(fhsize, horbtn)
783 self.image = gtk.Image()
785 self.show_info(None, 'description')
787 dialog.vbox.pack_start(self.action_btn, False, False, 0)
788 dialog.vbox.pack_start(self.image, False, False, 5)
789 dialog.vbox.pack_start(self.info_lb, True, True, 0)
790 dialog.vbox.pack_start(hbox1, False, False, 0)
793 self.action_btn.hide()
798 def do_action(self, widget, action):
800 if action == "donate":
801 url = "http://stockthis.garage.maemo.org/donate.html"
802 elif action == "report":
803 url = "http://stockthis.garage.maemo.org/reporting.html"
804 elif action == "vote":
805 url = "http://maemo.org/downloads/product/stockthis"
806 webbrowser.open_new(url)
808 def show_info(self, widget, kind):
809 if kind == 'license':
810 self.action_btn.hide()
812 info = """<small><b>StockThis</b> is free software. It's using a GPL version 2 license or at your election any later version.
814 Logo by Daniel Martin Yerga.
816 elif kind == 'credits':
817 self.action_btn.hide()
819 info = """<small><b>Written by</b> Daniel Martin Yerga (dyerga@gmail.com)
821 <b>Thanks</b> to everyone who has reported bugs, suggestions, giving spirits, critiques, writing blog articles about StockThis, and so on. Like always the list is extremely big and for not forget anybody, THANKS TO ALL!</small>"""
822 elif kind == 'description':
823 self.action_btn.hide()
825 info = """<b><big>StockThis 0.3</big></b>
827 <i>StockThis is a stocks application for Maemo</i>
830 stockthis.garage.maemo.org"""
832 elif kind == 'donate':
833 self.action_btn.show()
835 self.action_btn.set_title('I want donate')
837 self.action_btn.disconnect(self.id)
838 self.id = self.action_btn.connect("clicked", self.do_action, "donate")
839 info = """<small><b>StockThis</b> is a free (and gratis) software application.
840 Developing good software takes time and hard work.
842 <b>StockThis's author</b> develops the program in him spare time.
843 If you like the program and it's helpful, consider donating a small amount of money.
844 Donations are a great incentive and help to feel that the hard work is appreciated.</small>
847 elif kind == 'report':
848 self.action_btn.show()
850 self.action_btn.set_title('Report bug')
852 self.action_btn.disconnect(self.id)
853 self.id = self.action_btn.connect("clicked", self.do_action, "report")
854 info = """<small>StockThis is being improved thanks to bug reports. The author appreciates very much all these reports.
855 If the application is raising an error when you're using it, you have two choices to report this error:
856 1) Send the log from the application menu (if there's an error in the log).
857 2) Write a bug report in the bugtracker of StockThis with as much information as possible (especially the log from the menu).</small>"""
860 self.action_btn.show()
862 self.image.set_from_file(imgdir + "maemoorg.png")
863 self.action_btn.set_title('Vote for StockThis')
865 self.action_btn.disconnect(self.id)
866 self.id = self.action_btn.connect("clicked", self.do_action, "vote")
867 info = """<small>The downloads section in maemo.org has a nice system where you can rate applications.
868 If you consider StockThis a good application (or a bad one too), you could rate it in maemo.org site.</small>"""
870 self.info_lb.set_markup(info)
875 def __init__(self, widget, logfile):
877 dialog = gtk.Dialog(title='Log', parent=None)
879 dialog.set_size_request(600, 350)
881 parea = hildon.PannableArea()
882 parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
884 textview = hildon.TextView()
885 textview.set_property("editable", False)
886 textview.set_property("wrap-mode", gtk.WRAP_WORD)
888 log = open(logfile, 'r')
892 textview.get_buffer().set_text(logtext)
895 dialog.vbox.pack_start(parea, True, True, 0)
899 save_btn = hildon.Button(fhsize, horbtn)
900 save_btn.set_title("Save")
901 save_btn.connect('clicked', self.save, logfile, dialog)
903 clear_btn = hildon.Button(fhsize, horbtn)
904 clear_btn.set_title("Clear")
905 clear_btn.connect('clicked', self.clear, textview, logfile)
907 send_btn = hildon.Button(fhsize, horbtn)
908 send_btn.set_title('Send')
909 send_btn.connect('clicked', self.send, dialog, logfile)
911 hbox.pack_start(save_btn, True, True, 0)
912 hbox.pack_start(clear_btn, True, True, 0)
913 hbox.pack_start(send_btn, True, True, 0)
915 dialog.vbox.pack_start(hbox, False, False, 0)
921 def show_filechooser(self, window, title, name, EXT):
922 action = gtk.FILE_CHOOSER_ACTION_SAVE
924 m = hildon.FileSystemModel()
925 file_dialog = hildon.FileChooserDialog(window, action, m)
926 file_dialog.set_title(title)
928 file_dialog.set_current_name(name)
929 HOME = os.path.expanduser("~")
931 if os.path.exists(HOME + '/MyDocs/.documents'):
932 file_dialog.set_current_folder(HOME + '/MyDocs/.documents')
934 file_dialog.set_current_folder(HOME)
936 file_dialog.set_default_response(gtk.RESPONSE_CANCEL)
938 result = file_dialog.run()
939 if result == gtk.RESPONSE_OK:
940 namefile = file_dialog.get_filename()
941 namefile, extension = os.path.splitext(namefile)
942 namefile = namefile + "." + EXT
945 file_dialog.destroy()
950 def clear(self, widget, textview, logfile):
951 textview.get_buffer().set_text('')
952 f = open(logfile, 'w')
955 def save(self, widget, logfile, dlg):
957 filename = self.show_filechooser(dlg, "Save log file",
958 "stockthis-log", "txt")
964 shutil.copyfile(logfile, filename)
965 stockspy.show_info_banner(widget, 'Log file saved')
967 logger.exception("Saving log file")
968 stockspy.show_info_banner(widget, 'Error saving the log file')
970 def send(self, widget, dlg, logfile):
972 hildon.hildon_gtk_window_set_progress_indicator(dlg, 1)
973 thread.start_new_thread(self._do_send, (dlg, logfile))
975 def _do_send(self, dlg, logfile):
976 import pycurl, shutil, random, commands
979 for i in random.sample('abcdefghijkl123456789', 18):
982 rnamepath = HOME + "/.stockthis/" + rname
983 shutil.copyfile(logfile, rnamepath)
985 gtkversion = "%s.%s.%s" % gtk.ver
986 if os.path.exists("/etc/maemo_version"):
987 mfile = open("/etc/maemo_version", 'r')
988 maemoversion = mfile.read()
993 opsystem = ' '.join(os.uname())
994 pyversion = os.sys.version
996 comm = ("awk '/Private_Dirty/{sum+=$2}END{print sum \"kB\"}'"
997 " /proc/%s/smaps") % pid
998 status, dirtymem = commands.getstatusoutput(comm)
1000 lfile = open(rnamepath, 'r')
1005 log = ("%s\nPython version: %s\nGtk version: %s\n"
1006 "Maemo version: %sOperating system: %s\n"
1007 "Dirty Memory: %s\nLog:\n%s") % (_version, pyversion, gtkversion,
1008 maemoversion, opsystem, dirtymem, log)
1010 lfile = open(rnamepath, 'w')
1014 url = "http://yerga.net/logs/uploader.php"
1015 data = [('uploadedfile', (pycurl.FORM_FILE, rnamepath)),]
1016 mycurl = pycurl.Curl()
1017 mycurl.setopt(pycurl.URL, url)
1018 mycurl.setopt(pycurl.HTTPPOST, data)
1022 os.remove(rnamepath)
1024 gtk.gdk.threads_enter()
1025 stockspy.show_info_banner(dlg, 'Log sent')
1026 gtk.gdk.threads_leave()
1027 hildon.hildon_gtk_window_set_progress_indicator(dlg, 0)
1029 logger.exception("Sending log file")
1030 gtk.gdk.threads_enter()
1031 stockspy.show_info_banner(dlg, 'Error sending the log file')
1032 gtk.gdk.threads_leave()
1033 hildon.hildon_gtk_window_set_progress_indicator(dlg, 0)
1036 if __name__ == "__main__":
1037 stockspy = StocksPy()
1038 gtk.gdk.threads_enter()
1040 gtk.gdk.threads_leave()