X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=stockthis.py;h=7ca72b4eed8a49b1ec88a3b00367cc6199f0edb1;hb=HEAD;hp=3e86a93824f226ee19f3eb487e791021e13f940d;hpb=1b5defec2986ac3566d5e44b91a911c142f51b65;p=stockthis diff --git a/stockthis.py b/stockthis.py index 3e86a93..7ca72b4 100644 --- a/stockthis.py +++ b/stockthis.py @@ -16,41 +16,66 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# StocksPy: Application to get stocks data from Yahoo Finance. -# Version 0.1 +# StocksThis: Application to get stocks data from Yahoo Finance. # +_version = "StockThis 0.4 rev1" +VERSION = "0.4" + import urllib2 import gtk, gobject - +import os import hildon +import marketdata +import settings +import logging +import sys -#import osso -#osso_c = osso.Context("net.yerga.stockthis", "0.2", False) +from portrait import FremantleRotation -from marketdata import markets, idmarket, localmarkets, localids +import osso +osso_c = osso.Context("net.yerga.stockthis", "0.3", False) #detect if is ran locally or not -import sys runningpath = sys.path[0] -if '/usr/share' in runningpath: - running_locally = False +if '/opt/' in runningpath: + runninglocally = False else: - running_locally = True + runninglocally = True -if running_locally: - imgdir = 'pixmaps/' -else: - imgdir = '/usr/share/stockthis/pixmaps/' +HOME = os.path.expanduser("~") + +settingsdb, imgdir, configdir, logfile = \ + settings.define_paths(runninglocally, HOME) -loading_img = imgdir + 'loading.gif' +logger = logging.getLogger('st') +logging.basicConfig(filename=logfile,level=logging.ERROR, filemode='w') + +DEBUG = True + +if DEBUG: + #set the main logger to DEBUG + logger.setLevel(logging.DEBUG) + + #Create a handler for console debug + console = logging.StreamHandler() + console.setLevel(logging.DEBUG) + # set a format which is simpler for console use + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + # tell the handler to use this format + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) fhsize = gtk.HILDON_SIZE_FINGER_HEIGHT horbtn = hildon.BUTTON_ARRANGEMENT_HORIZONTAL ui_normal = gtk.HILDON_UI_MODE_NORMAL +ui_edit = gtk.HILDON_UI_MODE_EDIT winprogind = hildon.hildon_gtk_window_set_progress_indicator +allnames = [] +allsymbols = [] + gtk.gdk.threads_init() class StocksPy: @@ -64,20 +89,19 @@ class StocksPy: self.program.add_window(self.window) self.window.connect("destroy", gtk.main_quit) - menu = hildon.AppMenu() - self.window.set_app_menu(menu) - button = gtk.Button("About") - button.connect("clicked", self.on_about) - menu.append(button) - menu.show_all() + FremantleRotation('StockThis', None, "0.3", 0) + + self.create_menu(self.window) vbox = gtk.VBox() - toolbar = self.main_toolbar(False) + toolbar = self.main_toolbar(False, False, None, '', '', True) parea = hildon.PannableArea() tv = hildon.GtkTreeView(ui_normal) - inmodel = self.__create_model(markets, idmarket) - tv.connect("row-activated", self.show_instrument_view, inmodel) + inmodel = self.__create_model(marketdata.main, marketdata.idmain) + tv.connect("row-activated", self.show_instrument_view, inmodel, + marketdata.localmarkets, marketdata.localids, + marketdata.idmain) tv.set_model(inmodel) self._tv_columns(tv) parea.add(tv) @@ -89,21 +113,41 @@ class StocksPy: self.window.add(vbox) self.window.show_all() - def show_instrument_view(self, widget, path, column, inmodel): - market = inmodel[path][0] + self.show_info_banner(self.window, + ("StockThis uses your network connection to get data.\n" + "Be aware of the high costs that your network provider may apply.")) - names = localmarkets[idmarket.index(market)] - ids = localids[idmarket.index(market)] + def create_menu(self, window): + menu = hildon.AppMenu() + window.set_app_menu(menu) + button = gtk.Button("About") + button.connect("clicked", About) + menu.append(button) + menu.show_all() + + def show_instrument_view(self, widget, path, column, inmodel, names, + ids, mindex): + market = inmodel[path][0] + names = names[mindex.index(market)] + ids = ids[mindex.index(market)] window = hildon.StackableWindow() + self.create_menu(window) + window.set_title(inmodel[path][1]) vbox = gtk.VBox() - toolbar = self.main_toolbar(False) + toolbar = self.main_toolbar(False, False, None, '', '', False) parea = hildon.PannableArea() + parea.connect("horizontal-movement", self.horizontal_mov) tv = hildon.GtkTreeView(ui_normal) model = self.__create_model(names, ids) - tv.connect("row-activated", self.show_quotes_view, model) + + if market == 'metals': + tv.connect("row-activated", self.show_metal_graphs, model) + else: + tv.connect("row-activated", self.show_quotes_view, model, False) + tv.set_model(model) self._tv_columns(tv) parea.add(tv) @@ -115,124 +159,287 @@ class StocksPy: window.add(vbox) window.show_all() + def horizontal_mov(self, parea, direction, initial_x, initial_y): + #direction = 2 right-to-left + #direction = 3 lefto-to-right + + vadj = parea.get_vadjustment() + val = vadj.get_value() - def show_quotes_view(self, widget, path, column, model): + if direction == 2: + if int(val)-2500 < 0: + parea.scroll_to(-1, 0) + else: + parea.scroll_to(-1, int(val)-2500) + elif direction == 3: + parea.scroll_to(-1, int(val)+3500) + + #print val + + def show_quotes_view(self, widget, path, column, model, portfolio): quote = model[path][0], model[path][1] + #print "quote:", quote[0] + #('EURUSD=X', 'EUR/USD') + + #Currencies and ETFs should show the list now -> view = True + #Other items show a new list with options + view = False + for i in marketdata.localids[(len(marketdata.localids)-2):]: + for j in i: + if quote[0] == j: + #print j + view = True + + if not view: + if quote[0] in marketdata.idindexes: + self.show_instrument_view(widget, path, column, model, + marketdata.wnamesindexes, + marketdata.widsindexes, + marketdata.idindexes) + return + if quote[0] in marketdata.idotmarkets: + self.show_instrument_view(widget, path, column, model, + marketdata.omnames, + marketdata.omsymbols, + marketdata.idotmarkets) + return + if quote[0] in marketdata.ideumarkets: + self.show_instrument_view(widget, path, column, model, + marketdata.eunames, + marketdata.eusymbols, + marketdata.ideumarkets) + return + if quote[0] in marketdata.idusmarkets: + self.show_instrument_view(widget, path, column, model, + marketdata.usnames, + marketdata.ussymbols, + marketdata.idusmarkets) + return - self.stocks_id = quote[0] - self.stocks_name = quote[1] - print quote - self.quotes_win = hildon.StackableWindow() + win = hildon.StackableWindow() + self.create_menu(win) + win.set_title(quote[1]) vbox = gtk.VBox() - toolbar = self.main_toolbar(True) - - self.titlelbl = gtk.Label('') - self.titlelbl.set_markup('' + quote[1].replace('&', '') + + ltitle = gtk.Label('') + ltitle.set_markup('' + quote[1].replace('&', '') + '') color = gtk.gdk.color_parse("#03A5FF") - self.titlelbl.modify_fg(gtk.STATE_NORMAL, color) + ltitle.modify_fg(gtk.STATE_NORMAL, color) parea = hildon.PannableArea() + parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH) vbox1 = gtk.VBox() hbox = gtk.HBox() label = gtk.Label('') - label.set_markup('Price:') - self.lprice = gtk.Label('') - hbox.pack_start(label, False, False, 50) - hbox.pack_start(self.lprice, False, False, 185) + label.set_markup('%39s:' % 'Price') + lprice = gtk.Label('') + hbox.pack_start(label, False, False, 0) + #if self.is_portrait(): + hbox.pack_start(lprice, False, False, 25) + #else: + # hbox.pack_start(lprice, False, False, 245) + vbox1.pack_start(hbox, True, True, 0) hbox = gtk.HBox() label = gtk.Label('') - label.set_markup('Change:') - self.lchange = gtk.Label('') - self.lpercent = gtk.Label('') - hbox.pack_start(label, False, False, 50) - hbox.pack_start(self.lchange, False, False, 145) - hbox.pack_start(self.lpercent, False, False, 0) + label.set_markup('%35s:' % 'Change') + lchange = gtk.Label('') + lpercent = gtk.Label('') + hbox.pack_start(label, False, False, 0) + #if self.is_portrait(): + hbox.pack_start(lchange, False, False, 25) + hbox.pack_start(lpercent, False, False, 0) + #else: + # hbox.pack_start(lchange, False, False, 205) + # hbox.pack_start(lpercent, False, False, 0) + vbox1.pack_start(hbox, True, True, 0) hbox = gtk.HBox() label = gtk.Label('') - label.set_markup('Volume:') - self.lvolume = gtk.Label('') - hbox.pack_start(label, False, False, 50) - hbox.pack_start(self.lvolume, False, False, 145) + label.set_markup('%35s:' % 'Volume') + lvolume = gtk.Label('') + hbox.pack_start(label, False, False, 0) + #if self.is_portrait(): + hbox.pack_start(lvolume, False, False, 25) + #else: + # hbox.pack_start(lvolume, False, False, 207) + vbox1.pack_start(hbox, True, True, 0) hbox = gtk.HBox() label = gtk.Label('') - label.set_markup('52 week high:') - self.l52whigh = gtk.Label('') - hbox.pack_start(label, False, False, 50) - hbox.pack_start(self.l52whigh, False, False, 55) + label.set_markup('%30s:' % '52 week high') + l52whigh = gtk.Label('') + hbox.pack_start(label, False, False, 0) + #if self.is_portrait(): + hbox.pack_start(l52whigh, False, False, 25) + #else: + # hbox.pack_start(l52whigh, False, False, 125) + vbox1.pack_start(hbox, True, True, 0) hbox = gtk.HBox() label = gtk.Label('') - label.set_markup('52 week low:') - self.l52wlow = gtk.Label('') - hbox.pack_start(label, False, False, 50) - hbox.pack_start(self.l52wlow, False, False, 70) + label.set_markup('%30s:' % '52 week low') + l52wlow = gtk.Label('') + hbox.pack_start(label, False, False, 0) + #if self.is_portrait(): + hbox.pack_start(l52wlow, False, False, 26) + #else: + # hbox.pack_start(l52wlow, False, False, 140) vbox1.pack_start(hbox, True, True, 0) + #if self.is_portrait(): + # hbox = gtk.VBox() + #else: hbox = gtk.HBox() - - button = hildon.PickerButton(fhsize, horbtn) - #FIXME: touchentry selector + button1 = hildon.PickerButton(fhsize, horbtn) data = ["50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "1000"] - selector = self.create_selector(data) - button.set_selector(selector) - button.set_title("Shares") - #FIXME: check portfolio and see if there's quotes - button.set_value(data[0]) - hbox.pack_start(button, True, True, 0) + selector = self.create_selector(data, True) + button1.set_selector(selector) + button1.set_title("Your shares") + shares = self.get_shares_from_symbol(quote[0]) + button1.set_value(shares) + hbox.pack_start(button1, True, True, 0) button = hildon.Button(fhsize, horbtn) button.set_title("Add to Portfolio") - #button.connect("clicked", self.publish_opt_screen) + button.connect("clicked", self.add_to_portfolio, button1, quote[0], quote[1]) hbox.pack_start(button, True, True, 0) - vbox1.pack_start(hbox, False, False, 0) + + hbox1 = gtk.HBox() + label = gtk.Label('') + label.set_markup('%37s:' % 'Shares') + lshares = gtk.Label(shares) + hbox1.pack_start(label, False, False, 0) + #if self.is_portrait(): + hbox1.pack_start(lshares, False, False, 25) + #else: + # hbox1.pack_start(lshares, False, False, 220) + + hbox2 = gtk.HBox() + label = gtk.Label('') + label.set_markup('%29s:' % 'Holdings Value') + holdingsvalue = gtk.Label("") + hbox2.pack_start(label, False, False, 0) + #if self.is_portrait(): + hbox2.pack_start(holdingsvalue, False, False, 25) + #else: + # hbox2.pack_start(holdingsvalue, False, False, 105) + + hbox3 = gtk.HBox() + label = gtk.Label('') + label.set_markup("%25s:" % "Day's Value Change") + dayvaluechange = gtk.Label("") + hbox3.pack_start(label, False, False, 0) + #if self.is_portrait(): + hbox3.pack_start(dayvaluechange, False, False, 25) + #else: + # hbox3.pack_start(dayvaluechange, False, False, 45) + + if not portfolio: + vbox1.pack_start(hbox, False, False, 0) + else: + vbox1.pack_start(hbox1, True, True, 0) + vbox1.pack_start(hbox2, True, True, 0) + vbox1.pack_start(hbox3, True, True, 0) parea.add_with_viewport(vbox1) - vbox.pack_start(self.titlelbl, False, False, 0) + widgets = [win, ltitle, lprice, lchange, lpercent, lvolume, l52whigh, + l52wlow, lshares, holdingsvalue, dayvaluechange] + + toolbar = self.main_toolbar(True, portfolio, widgets, quote[0], quote[1], False) + + vbox.pack_start(ltitle, False, False, 0) vbox.pack_start(gtk.HSeparator(), False, False, 0) vbox.pack_start(parea, True, True, 0) vbox.pack_start(gtk.HSeparator(), False, False, 5) vbox.pack_start(toolbar, False, False, 0) - self.quotes_win.add(vbox) - self.quotes_win.show_all() - self.show_data(quote[0], self.quotes_win) - def create_selector(self, data): - selector = hildon.hildon_touch_selector_new_text() + win.add(vbox) + win.show_all() + self.show_data(quote[0], widgets, shares) + + def is_portrait(self): + width = gtk.gdk.screen_width() + height = gtk.gdk.screen_height() + if width > height: + return False + else: + return True + + def get_shares_from_symbol(self, symbol): + shares = "0" + try: + portfolio_data = settings.load_portfolio(settingsdb) + for item in portfolio_data : + if symbol in item: + shares = item[2] + return shares + except: + logger.exception("Getting shares from symbol: %s" % symbol) + return shares + + def add_to_portfolio(self, widget, button, symbol, name): + shares = button.get_value() + + try: + portfolio = settings.load_portfolio(settingsdb) + index = "None" + for item in portfolio: + if symbol in item: + index = portfolio.index(item) + + item = [symbol, name, shares, '-'] + + if index is "None": + settings.insert_new_item_to_portfolio(settingsdb, item) + else: + settings.delete_item_from_portfolio(settingsdb, symbol) + settings.insert_new_item_to_portfolio(settingsdb, item) + + self.show_info_banner(widget, "Added to portfolio") + except: + logger.exception("Adding to portfolio: %s, %s" % (symbol, name)) + self.show_info_banner(widget, "Error adding to portfolio") + + + def create_selector(self, data, entry): + if entry: + selector = hildon.TouchSelectorEntry(text=True) + else: + selector = hildon.TouchSelector(text=True) for i in range(len(data)): selector.append_text(data[i]) return selector - def show_data(self, quote, win): + def show_data(self, symbol, widgets, shares): import thread - winprogind(win, 1) - thread.start_new_thread(self.get_data, (quote, win)) + winprogind(widgets[0], 1) + thread.start_new_thread(self.get_data, (symbol, widgets, shares)) - def get_data(self, quote, win): + def get_data(self, symbol, widgets, shares): from ystockquote import ystockquote as yt + win, ltitle, lprice, lchange, lpercent, lvolume, l52whigh, l52wlow, lshares, holdingsvalue, dayvaluechange = widgets + try: - data = yt.get_all(quote) + data = yt.get_all(symbol) except: - print 'Failed to get internet data' + logger.exception("Getting data from Yahoo: %s" % symbol) data = {'price': 'N/A', 'change': 'N/A', 'volume':'N/A', '52_week_high': 'N/A', '52_week_low': 'N/A'} - self.titlelbl.set_markup('Failed to get data') + ltitle.set_markup('Failed to get data') try: ch_percent = \ @@ -241,84 +448,108 @@ class StocksPy: except ValueError: ch_percent = 0.0 - self.lprice.set_label(data['price']) - self.lchange.set_label(data['change']) - self.lpercent.set_label('%6.2f %%' % ch_percent) + lprice.set_label(data['price']) + lchange.set_label(data['change']) + lpercent.set_label('%6.2f %%' % ch_percent) if '-' in data['change']: color = gtk.gdk.color_parse("#FF0000") else: color = gtk.gdk.color_parse("#16EB78") - self.lpercent.modify_fg(gtk.STATE_NORMAL, color) - self.lchange.modify_fg(gtk.STATE_NORMAL, color) + lpercent.modify_fg(gtk.STATE_NORMAL, color) + lchange.modify_fg(gtk.STATE_NORMAL, color) + + lvolume.set_label(data['volume']) + l52whigh.set_label(data['52_week_high']) + l52wlow.set_label(data['52_week_low']) + + try: + daychange = float(shares)*float(data['change']) + except ValueError: + daychange = 'N/A' + try: + holdvalue = float(shares)*float(data['price']) + except ValueError: + holdvalue = 'N/A' - self.lvolume.set_label(data['volume']) - self.l52whigh.set_label(data['52_week_high']) - self.l52wlow.set_label(data['52_week_low']) + dayvaluechange.set_label(str(daychange)) + holdingsvalue.set_label(str(holdvalue)) winprogind(win, 0) - def refresh_stock_data(self, widget): - self.show_data(self.stocks_id, self.quotes_win) + def refresh_stock_data(self, widget, portfolio, widgets, symbol): + if portfolio: + shares = self.get_shares_from_symbol(symbol) + else: + shares = "0" + self.show_data(symbol, widgets, shares) - def show_graph_view(self, widget): + def show_graph_view(self, widget, symbol, name): win = hildon.StackableWindow() + self.create_menu(win) + win.set_title(name) vbox = gtk.VBox() - toolbar = self.main_toolbar(False) + toolbar = self.main_toolbar(False, True, None, '', '', False) - self.graphs_title = gtk.Label(self.stocks_name) + self.graphs_title = gtk.Label(name) color = gtk.gdk.color_parse("#03A5FF") self.graphs_title.modify_fg(gtk.STATE_NORMAL, color) parea = hildon.PannableArea() + parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH) + + vbox1 = gtk.VBox() hbox = gtk.HBox() hbox.set_homogeneous(True) button = hildon.Button(fhsize, horbtn) button.set_label('1d') - button.connect("clicked", self.show_graph, '1d', win) + button.connect("clicked", self.show_graph, '1d', win, symbol) hbox.pack_start(button) button = hildon.Button(fhsize, horbtn) button.set_label('5d') - button.connect("clicked", self.show_graph, '5d', win) + button.connect("clicked", self.show_graph, '5d', win, symbol) hbox.pack_start(button) button = hildon.Button(fhsize, horbtn) button.set_label('3m') - button.connect("clicked", self.show_graph, '3m', win) + button.connect("clicked", self.show_graph, '3m', win, symbol) hbox.pack_start(button) button = hildon.Button(fhsize, horbtn) button.set_label('6m') - button.connect("clicked", self.show_graph, '6m', win) + button.connect("clicked", self.show_graph, '6m', win, symbol) hbox.pack_start(button) + vbox1.pack_start(hbox, False, False, 0) + hbox = gtk.HBox() + hbox.set_homogeneous(True) + button = hildon.Button(fhsize, horbtn) button.set_label('1y') - button.connect("clicked", self.show_graph, '1y', win) + button.connect("clicked", self.show_graph, '1y', win, symbol) hbox.pack_start(button) button = hildon.Button(fhsize, horbtn) button.set_label('2y') - button.connect("clicked", self.show_graph, '2y', win) + button.connect("clicked", self.show_graph, '2y', win, symbol) hbox.pack_start(button) button = hildon.Button(fhsize, horbtn) button.set_label('5y') - button.connect("clicked", self.show_graph, '5y', win) + button.connect("clicked", self.show_graph, '5y', win, symbol) hbox.pack_start(button) button = hildon.Button(fhsize, horbtn) button.set_label('Max') - button.connect("clicked", self.show_graph, 'max', win) + button.connect("clicked", self.show_graph, 'max', win, symbol) hbox.pack_start(button) - vbox1 = gtk.VBox() vbox1.pack_start(hbox, False, False, 0) self.graph = gtk.Image() @@ -335,31 +566,30 @@ class StocksPy: win.add(vbox) win.show_all() - self.show_graph(None, '1d', win) + self.show_graph(None, '1d', win, symbol) - def show_graph(self, widget, option, win): + def show_graph(self, widget, option, win, symbol): import thread winprogind(win, 1) - thread.start_new_thread(self.get_graph_data, (option, win)) + thread.start_new_thread(self.get_graph_data, (option, win, symbol)) - def get_graph_data(self, option, win): - kind = self.stocks_id + def get_graph_data(self, option, win, symbol): if option == '1d': - url = 'http://uk.ichart.yahoo.com/b?s=%s' % kind + url = 'http://uk.ichart.yahoo.com/b?s=%s' % symbol elif option == '5d': - url = 'http://uk.ichart.yahoo.com/w?s=%s' % kind + url = 'http://uk.ichart.yahoo.com/w?s=%s' % symbol elif option == '3m': - url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % kind.lower() + url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % symbol.lower() elif option == '6m': - url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % kind.lower() + url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % symbol.lower() elif option == '1y': - url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % kind.lower() + url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % symbol.lower() elif option == '2y': - url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % kind.lower() + url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % symbol.lower() elif option == '5y': - url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % kind.lower() + url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % symbol.lower() elif option == 'max': - url = 'http://chart.finance.yahoo.com/c/my/s/%s' % kind.lower() + url = 'http://chart.finance.yahoo.com/c/my/s/%s' % symbol.lower() try: myimg = urllib2.urlopen(url) @@ -369,16 +599,198 @@ class StocksPy: pbl.write(imgdata) pbuf = pbl.get_pixbuf() + pbuf = pbuf.scale_simple(475, 235, gtk.gdk.INTERP_TILES) pbl.close() self.graph.set_from_pixbuf(pbuf) winprogind(win, 0) except: + logger.exception("Getting graph data: %s" % url) winprogind(win, 0) self.graphs_title.set_label('Failed to get data') self.graph.destroy() + def show_metal_graphs(self, widget, path, column, model): + metal = model[path][1] + metal_urls = self.get_metal_graphs_urls(metal) + options = [] + for i in metal_urls: + options.append(i[0]) + + win = hildon.StackableWindow() + #self.create_menu(win) + win.set_title(metal) + + vbox = gtk.VBox() + #toolbar = self.main_toolbar(False, True, None, '', '', False) + + self.metalgraph_title = gtk.Label(metal) + color = gtk.gdk.color_parse("#03A5FF") + self.metalgraph_title.modify_fg(gtk.STATE_NORMAL, color) + + parea = hildon.PannableArea() + parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH) + + vbox1 = gtk.VBox() + + hbox = gtk.HBox() + hbox.set_homogeneous(True) + + button = hildon.Button(fhsize, horbtn) + button.set_label('live') + button.connect("clicked", self.show_metalgraph, 'live', win, metal_urls) + if 'live' in options: + hbox.pack_start(button) + + button = hildon.Button(fhsize, horbtn) + button.set_label('30d') + button.connect("clicked", self.show_metalgraph, '30d', win, metal_urls) + if '30d' in options: + hbox.pack_start(button) + + button = hildon.Button(fhsize, horbtn) + button.set_label('60d') + button.connect("clicked", self.show_metalgraph, '60d', win, metal_urls) + if '60d' in options: + hbox.pack_start(button) + + button = hildon.Button(fhsize, horbtn) + button.set_label('6m') + button.connect("clicked", self.show_metalgraph, '6m', win, metal_urls) + if '6m' in options: + hbox.pack_start(button) + + vbox1.pack_start(hbox, False, False, 0) + hbox = gtk.HBox() + hbox.set_homogeneous(True) + + button = hildon.Button(fhsize, horbtn) + button.set_label('1y') + button.connect("clicked", self.show_metalgraph, '1y', win, metal_urls) + if '1y' in options: + hbox.pack_start(button) + + button = hildon.Button(fhsize, horbtn) + button.set_label('5y') + button.connect("clicked", self.show_metalgraph, '5y', win, metal_urls) + if '5y' in options: + hbox.pack_start(button) + + button = hildon.Button(fhsize, horbtn) + button.set_label('10y') + button.connect("clicked", self.show_metalgraph, '10y', win, metal_urls) + if '10y' in options: + hbox.pack_start(button) + + vbox1.pack_start(hbox, False, False, 0) + + self.metalgraph = gtk.Image() + vbox1.pack_start(self.metalgraph, True, True, 0) + + parea.add_with_viewport(vbox1) + + vbox.pack_start(self.metalgraph_title, False, False, 0) + vbox.pack_start(gtk.HSeparator(), False, False, 0) + vbox.pack_start(parea, True, True, 0) + vbox.pack_start(gtk.HSeparator(), False, False, 5) + #vbox.pack_start(toolbar, False, False, 0) + + win.add(vbox) + win.show_all() + + self.show_metalgraph(None, options[0], win, metal_urls) + + def show_metalgraph(self, widget, option, win, metal_urls): + import thread + winprogind(win, 1) + thread.start_new_thread(self.get_metalgraph_data, (option, win, metal_urls)) + + def get_metalgraph_data(self, option, win, metal_urls): + for i in metal_urls: + if i[0] == option: + print i[1] + url = i[1] + + try: + myimg = urllib2.urlopen(url) + imgdata = myimg.read() + + pbl = gtk.gdk.PixbufLoader() + pbl.write(imgdata) + + + pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, 475, 235) + pixbuf.fill(0xffffffff) + pbuf = pbl.get_pixbuf() + pbuf = pbuf.scale_simple(475, 235, gtk.gdk.INTERP_TILES) + + pixbuf.composite(pbuf, 0, 0, pixbuf.props.width, pixbuf.props.height, 0, 0, 1.0, 1.0, gtk.gdk.INTERP_HYPER, 55) + + + pbl.close() + self.metalgraph.set_from_pixbuf(pbuf) + winprogind(win, 0) + except: + logger.exception("Getting graph data: %s" % url) + winprogind(win, 0) + self.metalgraph_title.set_label('Failed to get data') + self.metalgraph.destroy() + + def get_metal_graphs_urls(self, metal): + metal_urls = [] + liveurl = 'http://www.kitco.com/images/live/%s' + graphurl = 'http://www.kitco.com/LFgif/%s' + t24url = 'http://www.kitconet.com/charts/metals/base/%s' + if metal == 'Gold': + metal_urls.append(['live', liveurl % 'gold.gif']) + metal_urls.append(['30d', graphurl % 'au0030lnb.gif']) + metal_urls.append(['60d', graphurl % 'au0060lnb.gif']) + metal_urls.append(['6m', graphurl % 'au0182nyb.gif']) + metal_urls.append(['1y', graphurl % 'au0365nyb.gif']) + metal_urls.append(['5y', graphurl % 'au1825nyb.gif']) + metal_urls.append(['10y', graphurl % 'au3650nyb.gif']) + elif metal == 'Silver': + metal_urls.append(['live', liveurl % 'silver.gif']) + metal_urls.append(['30d', graphurl % 'ag0030lnb.gif']) + metal_urls.append(['60d', graphurl % 'ag0060lnb.gif']) + metal_urls.append(['6m', graphurl % 'ag0182nyb.gif']) + metal_urls.append(['1y', graphurl % 'ag0365nyb.gif']) + metal_urls.append(['5y', graphurl % 'ag1825nyb.gif']) + metal_urls.append(['10y', graphurl % 'ag3650nyb.gif']) + elif metal == 'Platinum': + metal_urls.append(['live', liveurl % 'plati.gif']) + metal_urls.append(['30d', graphurl % 'pt0030lnb.gif']) + metal_urls.append(['60d', graphurl % 'pt0060lnb.gif']) + metal_urls.append(['6m', graphurl % 'pt0182nyb.gif']) + metal_urls.append(['1y', graphurl % 'pt0365nyb.gif']) + metal_urls.append(['5y', graphurl % 'pt1825nyb.gif']) + elif metal == 'Palladium': + metal_urls.append(['live', liveurl % 'plad.gif']) + metal_urls.append(['30d', graphurl % 'pd0030lnb.gif']) + metal_urls.append(['60d', graphurl % 'pd0060lnb.gif']) + metal_urls.append(['6m', graphurl % 'pd0182nyb.gif']) + metal_urls.append(['1y', graphurl % 'pd0365nyb.gif']) + metal_urls.append(['5y', graphurl % 'pd1825nyb.gif']) + elif metal == 'Rhodium': + metal_urls.append(['30d', graphurl % 'rh0030lnb.gif']) + metal_urls.append(['60d', graphurl % 'rh0060lnb.gif']) + metal_urls.append(['6m', graphurl % 'rh0182lnb.gif']) + metal_urls.append(['1y', graphurl % 'rh0365lnb.gif']) + metal_urls.append(['5y', graphurl % 'rh1825lnb.gif']) + elif metal == 'Copper': + metal_urls.append(['live', t24url % 't24_cp450x275.gif']) + elif metal == 'Nickel': + metal_urls.append(['live', t24url % 't24_nk450x275.gif']) + elif metal == 'Aluminium': + metal_urls.append(['live', t24url % 't24_al450x275.gif']) + elif metal == 'Zinc': + metal_urls.append(['live', t24url % 't24_zc450x275.gif']) + elif metal == 'Lead': + metal_urls.append(['live', t24url % 't24_ld450x275.gif']) + + return metal_urls + def _tv_columns(self, treeview): column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0) column.set_visible(False) @@ -394,9 +806,9 @@ class StocksPy: lstore.set(iter, 0, ids[item], 1, names[item]) return lstore - def main_toolbar(self, quotesview): + def main_toolbar(self, quotesview, portfolio, widgets, symbol, name, initial): toolbar = gtk.HBox() - toolbar.set_homogeneous(True) + #toolbar.set_homogeneous(True) portfolio_btn = hildon.Button(fhsize, horbtn) portfolio_btn.set_title("Portfolio") @@ -404,73 +816,406 @@ class StocksPy: graph_btn = hildon.Button(fhsize, horbtn) graph_btn.set_title("Graph") - graph_btn.connect("clicked", self.show_graph_view) + graph_btn.connect("clicked", self.show_graph_view, symbol, name) refresh_btn = hildon.Button(fhsize, horbtn) refresh_btn.set_title("Refresh") - refresh_btn.connect("clicked", self.refresh_stock_data) + refresh_btn.connect("clicked", self.refresh_stock_data, portfolio, + widgets, symbol) + + + info_btn = hildon.Button(fhsize, horbtn) + img = gtk.image_new_from_icon_name("general_information", gtk.ICON_SIZE_SMALL_TOOLBAR) + info_btn.set_image(img) + info_btn.connect("clicked", self.show_app_information) + + search_btn = hildon.Button(fhsize, horbtn) + img = gtk.image_new_from_icon_name("general_search", gtk.ICON_SIZE_SMALL_TOOLBAR) + search_btn.set_image(img) + search_btn.connect("clicked", self.show_search_dialog) - toolbar.pack_start(portfolio_btn) + if not portfolio: + toolbar.pack_start(portfolio_btn) + if not quotesview: + toolbar.pack_start(info_btn, False, False, 0) if quotesview: toolbar.pack_start(graph_btn) toolbar.pack_start(refresh_btn) + if initial: + toolbar.pack_start(search_btn, False, False, 0) + toolbar.show_all() return toolbar + def show_search_dialog(self, widget): + dlg = gtk.Dialog(title='Search company', parent=None, flags=0) + dlg.set_has_separator(False) + + hbox = gtk.HBox() + + entry = hildon.Entry(fhsize) + entry.connect("activate", self.do_search, entry, dlg) + hbox.pack_start(entry, True, True, 0) + + button = hildon.Button(fhsize, horbtn) + img = gtk.image_new_from_icon_name("general_search", gtk.ICON_SIZE_SMALL_TOOLBAR) + button.set_image(img) + button.connect("clicked", self.do_search, entry, dlg) + hbox.pack_start(button, False, False, 0) + + dlg.vbox.pack_start(hbox, False, False, 0) + + dlg.show_all() + dlg.run() + dlg.destroy() + + + def do_search(self, widget, entry, dlg): + import thread + text = entry.get_text() + dlg.destroy() + + winprogind(self.window, 1) + thread.start_new_thread(self._really_do_search, (text,)) + + def _really_do_search(self, text): + + if allnames == []: + for market in marketdata.eunames: + for company in market: + allnames.append(company) + + for market in marketdata.omnames: + for company in market: + allnames.append(company) + + for market in marketdata.usnames: + for company in market: + allnames.append(company) + + if allsymbols == []: + for market in marketdata.eusymbols: + for company in market: + allsymbols.append(company) + + for market in marketdata.omsymbols: + for company in market: + allsymbols.append(company) + + for market in marketdata.ussymbols: + for company in market: + allsymbols.append(company) + + new_model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + for i in range(len(allnames)): + if text.lower() in allnames[i].lower(): + niter = new_model.append() + #print allsymbols[i], allnames[i] + #FIXME: repeated companies in the results list + #print if allnames[i] + #for j in new_model: + # if j[1] == allnames[i]: + new_model.set(niter, 0, allsymbols[i], 1, allnames[i]) + + if len(new_model) == 0: + winprogind(self.window, 0) + gtk.gdk.threads_enter() + self.show_info_banner(self.window, "No items found for this search") + gtk.gdk.threads_leave() + return + + gtk.gdk.threads_enter() + self.show_search_screen(new_model, text) + gtk.gdk.threads_leave() + winprogind(self.window, 0) + + + def show_search_screen(self, model, text): + window = hildon.StackableWindow() + self.create_menu(window) + window.set_title("Search for " + text) + + vbox = gtk.VBox() + toolbar = self.main_toolbar(False, False, None, '', '', False) + + parea = hildon.PannableArea() + parea.connect("horizontal-movement", self.horizontal_mov) + tv = hildon.GtkTreeView(ui_normal) + tv.connect("row-activated", self.show_quotes_view, model, False) + tv.set_model(model) + self._tv_columns(tv) + parea.add(tv) + + vbox.pack_start(parea, True, True, 0) + vbox.pack_start(gtk.HSeparator(), False, False, 5) + vbox.pack_start(toolbar, False, False, 0) + + window.add(vbox) + window.show_all() + + def show_app_information(self, widget): + self.show_information_note(self.window, ( + "The data is got from Yahoo! Finance.\n" + "It could be delayed or even wrong.\n" + "The author doesn't validate in any way this data and therefore he is not responsible for any damage that may occur.\n\n" + "You can scroll large list with gestures:\n" + "Left-to-right gesture: scroll down.\n" + "Right-to-left gesture: scroll up.")) + def show_portfolio_view(self, widget): - #FIXME: get data from sqlite settings - data = [["SAN.MC", "BANCO SANTANDER R", "200", "-"], - ["BBVA.MC", "BBVA R", "300", "-"]] + data = settings.load_portfolio(settingsdb) + for item in data: + item.append('-') + item.append('-') + win = hildon.StackableWindow() + self.create_menu(win) + win.set_title("Portfolio") vbox = gtk.VBox() parea = hildon.PannableArea() + parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH) tv = hildon.GtkTreeView(ui_normal) tv.set_headers_visible(True) - inmodel = self._create_portfolio_model(data) - #tv.connect("row-activated", self.show_instrument_view, inmodel) - tv.set_model(inmodel) + self.portfolio_model = self._create_portfolio_model(data) + self.quotes_id = tv.connect("row-activated", self.show_quotes_view, + self.portfolio_model, True) + tv.set_model(self.portfolio_model) self._tv_portfolio_columns(tv) parea.add(tv) + hbox = gtk.HBox() button = hildon.Button(fhsize, horbtn) button.set_title("Refresh All") - button.connect("clicked", self.refresh_portfolio, tv, win, data) + button.connect("clicked", self.refresh_portfolio, tv, win) + hbox.pack_start(button, True, True, 0) + + button = hildon.Button(fhsize, horbtn) + button.set_title("Add manually") + button.connect("clicked", self.add_item_dlg, None, None) + hbox.pack_start(button, True, True, 0) + + button = hildon.Button(fhsize, horbtn) + button.set_title("Remove") + button.connect("clicked", self.remove_item) + hbox.pack_start(button, True, True, 0) + + button = hildon.CheckButton(fhsize) + button.set_label("Edit") + + button.connect("toggled", self.edit_toggled, tv) + hbox.pack_start(button, True, True, 0) + + + vbox.pack_start(parea, True, True, 0) + vbox.pack_start(hbox, False, False, 0) + win.add(vbox) + win.show_all() + + def edit_toggled(self, widget, tv): + if widget.get_active(): + tv.disconnect(self.quotes_id) + self.edit_id = tv.connect("row-activated", + self.edit_portfolio_item, self.portfolio_model) + else: + tv.disconnect(self.edit_id) + self.quotes_id = tv.connect("row-activated", + self.show_quotes_view, self.portfolio_model, True) + + def edit_portfolio_item(self, widget, path, column, model): + seliter = model.get_iter(path) + + symbol = model[path][0] + name = model[path][1] + shares = model[path][2] + + data = [shares, symbol, name] + self.add_item_dlg(widget, data, seliter) + + def add_item_dlg(self, widget, data, seliter): + if data: + shares = data[0] + symbol = data[1] + name = data[2] + title = "Edit item from portfolio" + btntext = "Edit" + edit = True + else: + shares = "0" + symbol = "" + name = "" + title = "Add to portfolio" + btntext = "Add" + edit = False + + dlg = gtk.Dialog(title=title, parent=None, flags=0) + dlg.set_has_separator(False) + + button1 = hildon.PickerButton(fhsize, horbtn) + data = ["50", "100", "200", "300", "400", "500", "600", "700", "800", + "900", "1000"] + selector = self.create_selector(data, True) + button1.set_selector(selector) + button1.set_title("Your shares") + button1.set_value(shares) + dlg.vbox.pack_start(button1, False, False, 0) + + entry1 = hildon.Entry(fhsize) + entry1.set_text(name) + entry2 = hildon.Entry(fhsize) + entry2.set_text(symbol) + + entry1.set_placeholder("Name") + entry1.connect("activate", self.add_item, dlg, button1, entry1, + entry2, edit, seliter) + dlg.vbox.pack_start(entry1, False, False, 0) + + entry2.connect("activate", self.add_item, dlg, button1, entry1, + entry2, edit, seliter) + entry2.set_placeholder("Yahoo Finance symbol") + dlg.vbox.pack_start(entry2, False, False, 0) + + button = hildon.Button(fhsize, horbtn) + button.set_label(btntext) + button.connect("clicked", self.add_item, dlg, button1, entry1, + entry2, edit, seliter) + dlg.vbox.pack_start(button, False, False, 0) + + dlg.show_all() + dlg.run() + dlg.destroy() + + def add_item(self, widget, dlg, button, entry1, entry2, edit, seliter): + symbol = entry2.get_text() + name = entry1.get_text() + shares = button.get_value() + + if name == '' or symbol == '': + self.show_info_banner(widget, "Must add the name and symbol") + return + + self.add_to_portfolio(widget, button, symbol, name) + dlg.destroy() + + if edit: + self.portfolio_model.set(seliter, 0, symbol, 1, name, 2, shares, + 3, "-", 4, "-", 5, "-", 6, "green") + else: + niter = self.portfolio_model.append() + self.portfolio_model.set(niter, 0, symbol, 1, name, 2, shares, + 3, "-", 4, "-", 5, "-", 6, "green") + + def remove_item(self, widget): + win = hildon.StackableWindow() + win.fullscreen() + toolbar = hildon.EditToolbar("Choose items to delete", "Delete") + win.set_edit_toolbar(toolbar) + + vbox = gtk.VBox() + parea = hildon.PannableArea() + tv = hildon.GtkTreeView(ui_edit) + selection = tv.get_selection() + selection.set_mode(gtk.SELECTION_MULTIPLE) + tv.set_model(self.portfolio_model) + self._tv_remove_portfolio_columns(tv) + parea.add(tv) + + toolbar.connect("button-clicked", self.delete_from_portfolio, win, tv, + selection) + toolbar.connect_object("arrow-clicked", gtk.Window.destroy, win) vbox.pack_start(parea, True, True, 0) - vbox.pack_start(button, False, False, 0) win.add(vbox) win.show_all() - def refresh_portfolio(self, widget, tv, win, data): + def delete_from_portfolio(self, widget, win, tv, selection): + if not self.is_treeview_selected(tv): + return + + conf = self.show_confirmation(win, "Delete items?") + + if conf: + try: + selmodel, selected = selection.get_selected_rows() + iters = [selmodel.get_iter(path) for path in selected] + for i in iters: + symbol = selmodel.get_value(i, 0) + settings.delete_item_from_portfolio(settingsdb, symbol) + selmodel.remove(i) + except: + logger.exception("Deleting item from portfolio") + self.info_banner(widget, "Error deleting item") + + def _tv_remove_portfolio_columns(self, treeview): + column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0) + column.set_visible(False) + treeview.append_column(column) + + column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1) + column.set_property("expand", True) + treeview.append_column(column) + + column = gtk.TreeViewColumn('Shares', gtk.CellRendererText(), text=2) + column.set_visible(False) + treeview.append_column(column) + + column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3) + column.set_visible(False) + treeview.append_column(column) + + def refresh_portfolio(self, widget, tv, win): + data = settings.load_portfolio(settingsdb) + for item in data: + item.append('-') + item.append('-') import thread winprogind(win, 1) thread.start_new_thread(self._do_refresh_portfolio, (data, tv, win)) - def _do_refresh_portfolio(self, data, tv, win): + print data for item in data: - item[3] = self.get_price(item[0]) + item[3], item[4] = self.get_portfolio_data(item[0]) + try: + ch_percent = \ + 100.0 * float(item[4])/(float(item[3]) - \ + float(item[4])) + except ValueError: + ch_percent = 0.0 - model = self._create_portfolio_model(data) - tv.set_model(model) + item[5] = '%6.2f %%' % ch_percent + + + print data + self.portfolio_model = self._create_portfolio_model(data) + tv.set_model(self.portfolio_model) winprogind(win, 0) - def get_price(self, symbol): + def get_portfolio_data(self, symbol): from ystockquote import ystockquote as yt - price = yt.get_price(symbol) - - return price + try: + data = yt.get_all(symbol) + return data['price'], data['change'] + except: + logger.exception("Getting price from Yahoo: %s" % symbol) + return "-", "-" def _create_portfolio_model(self, data): lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, - gobject.TYPE_STRING, gobject.TYPE_STRING) + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING) for item in data: iter = lstore.append() - lstore.set(iter, 0, item[0], 1, item[1], 2, item[2], 3, item[3]) + if '+' in item[4]: + color = 'green' + else: + color = 'red' + lstore.set(iter, 0, item[0], 1, item[1], 2, item[2], 3, item[3], + 4, item[4], 5, item[5], 6, color) return lstore def _tv_portfolio_columns(self, treeview): @@ -488,20 +1233,412 @@ class StocksPy: column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3) treeview.append_column(column) - def on_about(self, widget): - dialog = gtk.AboutDialog() - dialog.set_name("StockThis") - dialog.set_version("0.2") - dialog.set_copyright("Copyright © 2009") - dialog.set_website("http://stockthis.garage.maemo.org") - dialog.set_authors(["Daniel Martin Yerga "]) - logo = gtk.gdk.pixbuf_new_from_file(imgdir + "stockthis.png") - dialog.set_logo(logo) - dialog.set_license("This program is released under the GNU\nGeneral Public License. Please visit \nhttp://www.gnu.org/copyleft/gpl.html\nfor details.") - dialog.set_artists(["Logo by Daniel Martin Yerga"]) + column = gtk.TreeViewColumn('Change', gtk.CellRendererText(), text=4) + treeview.append_column(column) + + + renderer = gtk.CellRendererText() + renderer.set_property("foreground-set", True) + column = gtk.TreeViewColumn('%', renderer, text=5, foreground=6) + treeview.append_column(column) + + def show_confirmation(self, window, msg): + dialog = hildon.hildon_note_new_confirmation(window, msg) + dialog.show_all() + result = dialog.run() + if result == gtk.RESPONSE_OK: + dialog.destroy() + return True + + dialog.destroy() + return False + + def show_information_note(self, window, msg): + dialog = hildon.hildon_note_new_information(window, msg) + dialog.show_all() + result = dialog.run() + dialog.destroy() + + def show_info_banner(self, widget, msg): + hildon.hildon_banner_show_information(widget, 'qgn_note_infoprint', msg) + + def is_treeview_selected(self, treeview): + selection = treeview.get_selection() + if selection.count_selected_rows() == 0: + self.show_info_banner(treeview, 'No selected item') + return False + else: + return True + +class About: + + def __init__(self, widget): + self.abdialog = gtk.Dialog() + self.abdialog.set_title("About StockThis") + + notebook = gtk.Notebook() + notebook.set_show_tabs(False) + notebook.set_scrollable(False) + notebook.set_show_border(False) + + # Description page # + vbox = gtk.VBox() + + label = gtk.Label() + label.set_markup("StockThis %s" % VERSION) + vbox.pack_start(label, True, True, 0) + + label = gtk.Label("Stocks application with big database") + vbox.pack_start(label, True, True, 0) + + label = gtk.Label("GNU General Public License") + vbox.pack_start(label, True, True, 0) + + url = "http://stockthis.garage.maemo.org" + webbtn = gtk.LinkButton(url, "Web") + vbox.pack_start(webbtn, True, True, 0) + gtk.link_button_set_uri_hook(self.launch_browser) + + notebook.append_page(vbox, gtk.Label()) + + # Credits page # + vbox = gtk.VBox() + textview = hildon.TextView() + textview.set_cursor_visible(False) + textview.set_wrap_mode(gtk.WRAP_WORD) + text = "Written by Daniel Martin Yerga (dyerga@gmail.com)" + textview.get_buffer().set_text(text) + + parea = hildon.PannableArea() + parea.add(textview) + + vbox.pack_start(parea, True, True, 0) + notebook.append_page(vbox, gtk.Label()) + + + # Donate page # + vbox = gtk.VBox() + + textview = hildon.TextView() + textview.set_cursor_visible(False) + textview.set_wrap_mode(gtk.WRAP_WORD) + text = """StockThis is a free software application. +Developing good software takes time and hard work. +StockThis's author develops the program in his spare time. +If you like the program and it's helpful, consider donating a small amount of money. +Donations are a great incentive and help the developer feel that the hard work is appreciated. +""" + textview.get_buffer().set_text(text) + + parea = hildon.PannableArea() + parea.add(textview) + + button = hildon.Button(fhsize, horbtn) + button.set_title("Make donation") + url = "http://stockthis.garage.maemo.org/donate.html" + button.connect("clicked", self.launch_browser, url) + vbox.pack_start(button, False, False, 0) + vbox.pack_start(parea, True, True, 0) + + notebook.append_page(vbox, gtk.Label()) + + # Report page # + vbox = gtk.VBox() + + textview = hildon.TextView() + textview.set_cursor_visible(False) + textview.set_wrap_mode(gtk.WRAP_WORD) + text = """StockThis is being improved thanks to bug reports that users have submitted. The author appreciates these reports. +If the application is having an error when you're using it, you have two choices to report this error: +1) Send the log from the button above (if there's an error in the log). +2) Press the button and read how to report a bug.""" + textview.get_buffer().set_text(text) + + parea = hildon.PannableArea() + parea.add(textview) + + hbox = gtk.HBox() + hbox.set_homogeneous(True) + + button = hildon.Button(fhsize, horbtn) + button.set_title("Report error") + url = "http://stockthis.garage.maemo.org/reporting.html" + button.connect("clicked", self.launch_browser, url) + hbox.pack_start(button, True, True, 0) + + button = hildon.Button(fhsize, horbtn) + button.set_title("Log") + button.connect("clicked", self.on_show_log) + hbox.pack_start(button, True, True, 0) + + vbox.pack_start(hbox, False, False, 0) + vbox.pack_start(parea, True, True, 0) + + notebook.append_page(vbox, gtk.Label()) + + # Rate page # + vbox = gtk.VBox() + + textview = hildon.TextView() + textview.set_cursor_visible(False) + textview.set_wrap_mode(gtk.WRAP_WORD) + text = """The downloads section in maemo.org has a nice system where you can rate applications. +If you consider StockThis a good application (or a bad one too), you could rate it in maemo.org site.""" + textview.get_buffer().set_text(text) + + button = hildon.Button(fhsize, horbtn) + button.set_title("Rate StockThis") + url = "http://maemo.org/downloads/product/Maemo5/stockthis" + button.connect("clicked", self.launch_browser, url) + image = gtk.Image() + image.set_from_file(imgdir + "maemoorg.png") + vbox.pack_start(button, False, False, 0) + vbox.pack_start(image, False, False, 5) + vbox.pack_start(textview, True, True, 0) + + notebook.append_page(vbox, gtk.Label()) + + # Buttons # + self.abdialog.vbox.pack_start(notebook, True, True, 0) + + hbox = gtk.HBox() + + descbutton = hildon.GtkRadioButton(fhsize) + descbutton.set_mode(False) + descbutton.set_active(True) + descbutton.set_label('Description') + descbutton.connect("toggled", self.change_tab, notebook, 0) + hbox.pack_start(descbutton, True, True, 0) + + button = hildon.GtkRadioButton(fhsize) + button.set_mode(False) + button.set_active(True) + button.set_label('Credits') + button.set_group(descbutton) + button.connect("toggled", self.change_tab, notebook, 1) + hbox.pack_start(button, True, True, 0) + + button = hildon.GtkRadioButton(fhsize) + button.set_mode(False) + button.set_label('Donate') + button.set_group(descbutton) + button.connect("clicked", self.change_tab, notebook, 2) + hbox.pack_start(button, True, True, 0) + + button = hildon.GtkRadioButton(fhsize) + button.set_mode(False) + button.set_label('Report') + button.set_group(descbutton) + button.connect("clicked", self.change_tab, notebook, 3) + hbox.pack_start(button, True, True, 0) + + button = hildon.GtkRadioButton(fhsize) + button.set_mode(False) + button.set_label('Rate') + button.set_group(descbutton) + button.connect("clicked", self.change_tab, notebook, 4) + hbox.pack_start(button, True, True, 0) + + self.abdialog.vbox.pack_start(hbox, False, False, 0) + + self.abdialog.show_all() + self.abdialog.run() + self.abdialog.destroy() + + def change_tab(self, widget, notebook, number): + notebook.set_current_page(number) + + def launch_browser(self, widget, url): + import dbus + bus = dbus.SystemBus() + proxy = bus.get_object("com.nokia.osso_browser", "/com/nokia/osso_browser/request") + iface = dbus.Interface(proxy, 'com.nokia.osso_browser') + + self.abdialog.destroy() + + iface.open_new_window(url) + + def on_show_log(self, widget): + Log(widget, logfile) + + +class Log: + + def __init__(self, widget, logfile): + #Log dialog UI + dialog = gtk.Dialog(title='Log', parent=None) + + dialog.set_size_request(600, 350) + + parea = hildon.PannableArea() + parea.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH) + + textview = hildon.TextView() + textview.set_property("editable", False) + textview.set_property("wrap-mode", gtk.WRAP_WORD) + + log = open(logfile, 'r') + logtext = log.read() + log.close() + + textview.get_buffer().set_text(logtext) + parea.add(textview) + + dialog.vbox.pack_start(parea, True, True, 0) + + hbox = gtk.HBox() + + save_btn = hildon.Button(fhsize, horbtn) + save_btn.set_title("Save") + save_btn.connect('clicked', self.save, logfile, dialog) + + clear_btn = hildon.Button(fhsize, horbtn) + clear_btn.set_title("Clear") + clear_btn.connect('clicked', self.clear, textview, logfile) + + send_btn = hildon.Button(fhsize, horbtn) + send_btn.set_title('Send') + send_btn.connect('clicked', self.send, dialog, logfile) + + hbox.pack_start(save_btn, True, True, 0) + hbox.pack_start(clear_btn, True, True, 0) + hbox.pack_start(send_btn, True, True, 0) + + dialog.vbox.pack_start(hbox, False, False, 0) + + dialog.show_all() dialog.run() dialog.destroy() + def show_filechooser(self, window, title, name, EXT): + action = gtk.FILE_CHOOSER_ACTION_SAVE + + m = hildon.FileSystemModel() + file_dialog = hildon.FileChooserDialog(window, action, m) + file_dialog.set_title(title) + + file_dialog.set_current_name(name) + HOME = os.path.expanduser("~") + + if os.path.exists(HOME + '/MyDocs/.documents'): + file_dialog.set_current_folder(HOME + '/MyDocs/.documents') + else: + file_dialog.set_current_folder(HOME) + + file_dialog.set_default_response(gtk.RESPONSE_CANCEL) + + result = file_dialog.run() + if result == gtk.RESPONSE_OK: + namefile = file_dialog.get_filename() + namefile, extension = os.path.splitext(namefile) + namefile = namefile + "." + EXT + else: + namefile = None + file_dialog.destroy() + + return namefile + + + def clear(self, widget, textview, logfile): + textview.get_buffer().set_text('') + f = open(logfile, 'w') + f.close() + + def save(self, widget, logfile, dlg): + import shutil + filename = self.show_filechooser(dlg, "Save log file", + "stockthis-log", "txt") + + if not filename: + return + + try: + shutil.copyfile(logfile, filename) + stockspy.show_info_banner(widget, 'Log file saved') + except: + logger.exception("Saving log file") + stockspy.show_info_banner(widget, 'Error saving the log file') + + def send(self, widget, dlg, logfile): + sendtxt = ("You are going to send the log to the developers.\n" + "This helps the developers to track problems with the application.\n" + "It doesn't send any personal information (like passwords or similar).") + + dialog = hildon.hildon_note_new_confirmation(dlg, sendtxt) + dialog.set_button_texts("Send", "Cancel") + dialog.show_all() + response = dialog.run() + if response == gtk.RESPONSE_OK: + self.do_pre_send(dlg, logfile) + + dialog.destroy() + + def do_pre_send(self, dlg, logfile): + import thread + hildon.hildon_gtk_window_set_progress_indicator(dlg, 1) + thread.start_new_thread(self._do_send, (dlg, logfile)) + + def _do_send(self, dlg, logfile): + import pycurl, shutil, random, commands + try: + rname = '' + for i in random.sample('abcdefghijkl123456789', 18): + rname += i + + rnamepath = HOME + "/.stockthis/" + rname + shutil.copyfile(logfile, rnamepath) + + gtkversion = "%s.%s.%s" % gtk.ver + if os.path.exists("/etc/maemo_version"): + mfile = open("/etc/maemo_version", 'r') + maemoversion = mfile.read() + mfile.close() + else: + maemoversion = '' + + opsystem = ' '.join(os.uname()) + pyversion = os.sys.version + pid = os.getpid() + comm = ("awk '/Private_Dirty/{sum+=$2}END{print sum \"kB\"}'" + " /proc/%s/smaps") % pid + status, dirtymem = commands.getstatusoutput(comm) + + lfile = open(rnamepath, 'r') + log = lfile.read() + lfile.close() + + + log = ("%s\nPython version: %s\nGtk version: %s\n" + "Maemo version: %sOperating system: %s\n" + "Dirty Memory: %s\nLog:\n%s") % (_version, pyversion, gtkversion, + maemoversion, opsystem, dirtymem, log) + + lfile = open(rnamepath, 'w') + lfile.write(log) + lfile.close() + + url = "http://yerga.net/logs/uploader.php" + data = [('uploadedfile', (pycurl.FORM_FILE, rnamepath)),] + mycurl = pycurl.Curl() + mycurl.setopt(pycurl.URL, url) + mycurl.setopt(pycurl.HTTPPOST, data) + + mycurl.perform() + mycurl.close() + os.remove(rnamepath) + + gtk.gdk.threads_enter() + stockspy.show_info_banner(dlg, 'Log sent') + gtk.gdk.threads_leave() + hildon.hildon_gtk_window_set_progress_indicator(dlg, 0) + except: + logger.exception("Sending log file") + gtk.gdk.threads_enter() + stockspy.show_info_banner(dlg, 'Error sending the log file') + gtk.gdk.threads_leave() + hildon.hildon_gtk_window_set_progress_indicator(dlg, 0) + + if __name__ == "__main__": stockspy = StocksPy() gtk.gdk.threads_enter()