From a42743ca2dc1376331d231740224e3f011836bbb Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 28 Apr 2015 03:28:43 +0200 Subject: [PATCH 01/13] always request exchange rates over https --- plugins/exchange_rate.py | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py index 8ab3fd81b..27a3d2f19 100644 --- a/plugins/exchange_rate.py +++ b/plugins/exchange_rate.py @@ -67,27 +67,6 @@ class Exchanger(threading.Thread): raise return json_resp - def get_json_insecure(self, site, get_string): - """ get_json_insecure shouldn't be used in production releases - It doesn't use SSL, and so prices could be manipulated by a middle man - This should be used ONLY when developing plugins when you don't have a - SSL certificate that validates against HTTPSConnection - """ - try: - connection = httplib.HTTPConnection(site) - connection.request("GET", get_string, headers={"User-Agent":"Electrum"}) - except Exception: - raise - resp = connection.getresponse() - if resp.reason == httplib.responses[httplib.NOT_FOUND]: - raise - try: - json_resp = json.loads(resp.read()) - except Exception: - raise - return json_resp - - def exchange(self, btc_amount, quote_currency): with self.lock: if self.quote_currencies is None: @@ -342,8 +321,7 @@ class Exchanger(threading.Thread): def update_bv(self): try: - jsonresp = self.get_json_insecure('api.bitcoinvenezuela.com', "/") - print("**WARNING**: update_bv is using an insecure connection, shouldn't be used on production") + jsonresp = self.get_json('api.bitcoinvenezuela.com', "/") except SSLError: print("SSL Error when accesing bitcoinvenezuela") return @@ -364,15 +342,13 @@ class Exchanger(threading.Thread): def update_bpl(self): try: - jsonresp = self.get_json_insecure('btcparalelo.com', "/api/price") - print("**WARNING**: update_bpl is using an insecure connection, shouldn't be used on production") + jsonresp = self.get_json('btcparalelo.com', "/api/price") except SSLError: print("SSL Error when accesing btcparalelo") return except Exception: return - quote_currencies = {} try: quote_currencies = {"VEF": Decimal(jsonresp["price"])} From 0b0fb45f620689fc4bdd82f34ce28abff829b46c Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 28 Apr 2015 03:55:55 +0200 Subject: [PATCH 02/13] exchange rates: factorize getter functions --- plugins/exchange_rate.py | 275 +++++++++------------------------------ 1 file changed, 59 insertions(+), 216 deletions(-) diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py index 27a3d2f19..ea33a2869 100644 --- a/plugins/exchange_rate.py +++ b/plugins/exchange_rate.py @@ -98,9 +98,13 @@ class Exchanger(threading.Thread): "Winkdex": self.update_wd, } try: - update_rates[self.use_exchange]() - except KeyError: - return + rates = update_rates[self.use_exchange]() + except Exception as e: + self.parent.print_error(e) + rates = {} + with self.lock: + self.quote_currencies = rates + self.parent.set_currencies(rates) def run(self): self.is_running = True @@ -111,28 +115,15 @@ class Exchanger(threading.Thread): def update_cd(self): - try: - resp_currencies = self.get_json('api.coindesk.com', "/v1/bpi/supported-currencies.json") - except SSLError: - print("SSL Error when accesing coindesk") - return - except Exception: - return - + resp_currencies = self.get_json('api.coindesk.com', "/v1/bpi/supported-currencies.json") quote_currencies = {} for cur in resp_currencies: quote_currencies[str(cur["currency"])] = 0.0 - current_cur = self.parent.config.get("currency", "EUR") if current_cur in quote_currencies: - try: - resp_rate = self.get_json('api.coindesk.com', "/v1/bpi/currentprice/" + str(current_cur) + ".json") - quote_currencies[str(current_cur)] = decimal.Decimal(str(resp_rate["bpi"][str(current_cur)]["rate_float"])) - except Exception: - return - with self.lock: - self.quote_currencies = quote_currencies - self.parent.set_currencies(quote_currencies) + resp_rate = self.get_json('api.coindesk.com', "/v1/bpi/currentprice/" + str(current_cur) + ".json") + quote_currencies[str(current_cur)] = decimal.Decimal(str(resp_rate["bpi"][str(current_cur)]["rate_float"])) + return quote_currencies def update_ib(self): available_currencies = ["USD", "EUR", "SGD"] @@ -141,242 +132,94 @@ class Exchanger(threading.Thread): quote_currencies[cur] = 0.0 current_cur = self.parent.config.get("currency", "EUR") if current_cur in available_currencies: - try: - resp_rate = self.get_json('api.itbit.com', "/v1/markets/XBT" + str(current_cur) + "/ticker") - quote_currencies[str(current_cur)] = decimal.Decimal(str(resp_rate["lastPrice"])) - except SSLError: - print("SSL Error when accesing itbit") - return - except Exception: - return - with self.lock: - self.quote_currencies = quote_currencies - self.parent.set_currencies(quote_currencies) + resp_rate = self.get_json('api.itbit.com', "/v1/markets/XBT" + str(current_cur) + "/ticker") + quote_currencies[str(current_cur)] = decimal.Decimal(str(resp_rate["lastPrice"])) + return quote_currencies def update_wd(self): - try: - winkresp = self.get_json('winkdex.com', "/api/v0/price") - except SSLError: - print("SSL Error when accesing winkdex") - return - except Exception: - return + winkresp = self.get_json('winkdex.com', "/api/v0/price") quote_currencies = {"USD": 0.0} usdprice = decimal.Decimal(str(winkresp["price"]))/decimal.Decimal("100.0") - try: - quote_currencies["USD"] = usdprice - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) + quote_currencies["USD"] = usdprice + return quote_currencies def update_cv(self): - try: - jsonresp = self.get_json('www.cavirtex.com', "/api/CAD/ticker.json") - except SSLError: - print("SSL Error when accesing cavirtex") - return - except Exception: - return + jsonresp = self.get_json('www.cavirtex.com', "/api/CAD/ticker.json") quote_currencies = {"CAD": 0.0} cadprice = jsonresp["last"] - try: - quote_currencies["CAD"] = decimal.Decimal(str(cadprice)) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) + quote_currencies["CAD"] = decimal.Decimal(str(cadprice)) + return quote_currencies def update_bm(self): - try: - jsonresp = self.get_json('www.bitmarket.pl', "/json/BTCPLN/ticker.json") - except SSLError: - print("SSL Error when accesing bitmarket") - return - except Exception: - return + jsonresp = self.get_json('www.bitmarket.pl', "/json/BTCPLN/ticker.json") quote_currencies = {"PLN": 0.0} pln_price = jsonresp["last"] - try: - quote_currencies["PLN"] = decimal.Decimal(str(pln_price)) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) + quote_currencies["PLN"] = decimal.Decimal(str(pln_price)) + return quote_currencies def update_bx(self): - try: - jsonresp = self.get_json('pln.bitcurex.com', "/data/ticker.json") - except SSLError: - print("SSL Error when accesing bitcurex") - return - except Exception: - return + jsonresp = self.get_json('pln.bitcurex.com', "/data/ticker.json") quote_currencies = {"PLN": 0.0} pln_price = jsonresp["last"] - try: - quote_currencies["PLN"] = decimal.Decimal(str(pln_price)) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) + quote_currencies["PLN"] = decimal.Decimal(str(pln_price)) + return quote_currencies def update_CNY(self): - try: - jsonresp = self.get_json('data.btcchina.com', "/data/ticker") - except SSLError: - print("SSL Error when accesing btcchina") - return - except Exception: - return + jsonresp = self.get_json('data.btcchina.com', "/data/ticker") quote_currencies = {"CNY": 0.0} cnyprice = jsonresp["ticker"]["last"] - try: - quote_currencies["CNY"] = decimal.Decimal(str(cnyprice)) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) + quote_currencies["CNY"] = decimal.Decimal(str(cnyprice)) + return quote_currencies def update_bp(self): - try: - jsonresp = self.get_json('bitpay.com', "/api/rates") - except SSLError: - print("SSL Error when accesing bitpay") - return - except Exception: - return + jsonresp = self.get_json('bitpay.com', "/api/rates") quote_currencies = {} - try: - for r in jsonresp: - quote_currencies[str(r["code"])] = decimal.Decimal(r["rate"]) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) + for r in jsonresp: + quote_currencies[str(r["code"])] = decimal.Decimal(r["rate"]) + return quote_currencies def update_cb(self): - try: - jsonresp = self.get_json('coinbase.com', "/api/v1/currencies/exchange_rates") - except SSLError: - print("SSL Error when accesing coinbase") - return - except Exception: - return - + jsonresp = self.get_json('coinbase.com', "/api/v1/currencies/exchange_rates") quote_currencies = {} - try: - for r in jsonresp: - if r[:7] == "btc_to_": - quote_currencies[r[7:].upper()] = self._lookup_rate_cb(jsonresp, r) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) - + for r in jsonresp: + if r[:7] == "btc_to_": + quote_currencies[r[7:].upper()] = self._lookup_rate_cb(jsonresp, r) + return quote_currencies def update_bc(self): - try: - jsonresp = self.get_json('blockchain.info', "/ticker") - except SSLError: - print("SSL Error when accesing blockchain") - return - except Exception: - return + jsonresp = self.get_json('blockchain.info', "/ticker") quote_currencies = {} - try: - for r in jsonresp: - quote_currencies[r] = self._lookup_rate(jsonresp, r) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) + for r in jsonresp: + quote_currencies[r] = self._lookup_rate(jsonresp, r) + return quote_currencies def update_lb(self): - try: - jsonresp = self.get_json('localbitcoins.com', "/bitcoinaverage/ticker-all-currencies/") - except SSLError: - print("SSL Error when accesing localbitcoins") - return - except Exception: - return + jsonresp = self.get_json('localbitcoins.com', "/bitcoinaverage/ticker-all-currencies/") quote_currencies = {} - try: - for r in jsonresp: - quote_currencies[r] = self._lookup_rate_lb(jsonresp, r) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) - + for r in jsonresp: + quote_currencies[r] = self._lookup_rate_lb(jsonresp, r) + return quote_currencies def update_bv(self): - try: - jsonresp = self.get_json('api.bitcoinvenezuela.com', "/") - except SSLError: - print("SSL Error when accesing bitcoinvenezuela") - return - except Exception: - return - + jsonresp = self.get_json('api.bitcoinvenezuela.com', "/") quote_currencies = {} - try: - for r in jsonresp["BTC"]: - quote_currencies[r] = Decimal(jsonresp["BTC"][r]) - - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - print ("KeyError") - self.parent.set_currencies(quote_currencies) + for r in jsonresp["BTC"]: + quote_currencies[r] = Decimal(jsonresp["BTC"][r]) + return quote_currencies - def update_bpl(self): - try: - jsonresp = self.get_json('btcparalelo.com', "/api/price") - except SSLError: - print("SSL Error when accesing btcparalelo") - return - except Exception: - return - + jsonresp = self.get_json('btcparalelo.com', "/api/price") quote_currencies = {} - try: - quote_currencies = {"VEF": Decimal(jsonresp["price"])} - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - print ("KeyError") - self.parent.set_currencies(quote_currencies) - + quote_currencies = {"VEF": Decimal(jsonresp["price"])} + return quote_currencies + def update_ba(self): - try: - jsonresp = self.get_json('api.bitcoinaverage.com', "/ticker/global/all") - except SSLError: - print("SSL Error when accesing bitcoinaverage") - return - except Exception: - return + jsonresp = self.get_json('api.bitcoinaverage.com', "/ticker/global/all") quote_currencies = {} - try: - for r in jsonresp: - if not r == "timestamp": - quote_currencies[r] = self._lookup_rate_ba(jsonresp, r) - with self.lock: - self.quote_currencies = quote_currencies - except KeyError: - pass - self.parent.set_currencies(quote_currencies) - + for r in jsonresp: + if not r == "timestamp": + quote_currencies[r] = self._lookup_rate_ba(jsonresp, r) + return quote_currencies def _lookup_rate(self, response, quote_id): return decimal.Decimal(str(response[str(quote_id)]["15m"])) From 4f3dd51885eb48a36257fd2e12f632b4d820f6cb Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 28 Apr 2015 04:24:10 +0200 Subject: [PATCH 03/13] factorize more --- plugins/exchange_rate.py | 57 +++++++++------------------------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py index ea33a2869..c6ea61d0c 100644 --- a/plugins/exchange_rate.py +++ b/plugins/exchange_rate.py @@ -138,88 +138,55 @@ class Exchanger(threading.Thread): def update_wd(self): winkresp = self.get_json('winkdex.com', "/api/v0/price") - quote_currencies = {"USD": 0.0} - usdprice = decimal.Decimal(str(winkresp["price"]))/decimal.Decimal("100.0") - quote_currencies["USD"] = usdprice - return quote_currencies + return {"USD": decimal.Decimal(str(winkresp["price"]))/decimal.Decimal("100.0")} def update_cv(self): jsonresp = self.get_json('www.cavirtex.com', "/api/CAD/ticker.json") - quote_currencies = {"CAD": 0.0} cadprice = jsonresp["last"] - quote_currencies["CAD"] = decimal.Decimal(str(cadprice)) - return quote_currencies + return {"CAD": decimal.Decimal(str(cadprice))} def update_bm(self): jsonresp = self.get_json('www.bitmarket.pl', "/json/BTCPLN/ticker.json") - quote_currencies = {"PLN": 0.0} pln_price = jsonresp["last"] - quote_currencies["PLN"] = decimal.Decimal(str(pln_price)) - return quote_currencies + return {"PLN": decimal.Decimal(str(pln_price))} def update_bx(self): jsonresp = self.get_json('pln.bitcurex.com', "/data/ticker.json") - quote_currencies = {"PLN": 0.0} pln_price = jsonresp["last"] - quote_currencies["PLN"] = decimal.Decimal(str(pln_price)) - return quote_currencies + return {"PLN": decimal.Decimal(str(pln_price))} def update_CNY(self): jsonresp = self.get_json('data.btcchina.com', "/data/ticker") - quote_currencies = {"CNY": 0.0} cnyprice = jsonresp["ticker"]["last"] - quote_currencies["CNY"] = decimal.Decimal(str(cnyprice)) - return quote_currencies + return {"CNY": decimal.Decimal(str(cnyprice))} def update_bp(self): jsonresp = self.get_json('bitpay.com', "/api/rates") - quote_currencies = {} - for r in jsonresp: - quote_currencies[str(r["code"])] = decimal.Decimal(r["rate"]) - return quote_currencies + return dict([(str(r["code"]), decimal.Decimal(r["rate"])) for r in jsonresp]) def update_cb(self): jsonresp = self.get_json('coinbase.com', "/api/v1/currencies/exchange_rates") - quote_currencies = {} - for r in jsonresp: - if r[:7] == "btc_to_": - quote_currencies[r[7:].upper()] = self._lookup_rate_cb(jsonresp, r) - return quote_currencies + return dict([(r[7:].upper(), self._lookup_rate_cb(jsonresp, r)) for r in jsonresp if r.startswith("btc_to_")]) def update_bc(self): jsonresp = self.get_json('blockchain.info', "/ticker") - quote_currencies = {} - for r in jsonresp: - quote_currencies[r] = self._lookup_rate(jsonresp, r) - return quote_currencies + return dict([(r, self._lookup_rate(jsonresp, r)) for r in jsonresp]) def update_lb(self): jsonresp = self.get_json('localbitcoins.com', "/bitcoinaverage/ticker-all-currencies/") - quote_currencies = {} - for r in jsonresp: - quote_currencies[r] = self._lookup_rate_lb(jsonresp, r) - return quote_currencies + return dict([(r, self._lookup_rate_lb(jsonresp, r)) for r in jsonresp]) def update_bv(self): jsonresp = self.get_json('api.bitcoinvenezuela.com', "/") - quote_currencies = {} - for r in jsonresp["BTC"]: - quote_currencies[r] = Decimal(jsonresp["BTC"][r]) - return quote_currencies + return dict([(r, Decimal(jsonresp["BTC"][r])) for r in jsonresp["BTC"]]) def update_bpl(self): jsonresp = self.get_json('btcparalelo.com', "/api/price") - quote_currencies = {} - quote_currencies = {"VEF": Decimal(jsonresp["price"])} - return quote_currencies + return {"VEF": Decimal(jsonresp["price"])} def update_ba(self): jsonresp = self.get_json('api.bitcoinaverage.com', "/ticker/global/all") - quote_currencies = {} - for r in jsonresp: - if not r == "timestamp": - quote_currencies[r] = self._lookup_rate_ba(jsonresp, r) - return quote_currencies + return dict([(r, self._lookup_rate_ba(jsonresp, r)) for r in jsonresp if not r == "timestamp"]) def _lookup_rate(self, response, quote_id): return decimal.Decimal(str(response[str(quote_id)]["15m"])) From 576e9be02b4081a5aaabea23872fc1185328e052 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 28 Apr 2015 04:42:25 +0200 Subject: [PATCH 04/13] exchange_rates: more simplifications --- plugins/exchange_rate.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py index c6ea61d0c..3f10b983a 100644 --- a/plugins/exchange_rate.py +++ b/plugins/exchange_rate.py @@ -166,15 +166,15 @@ class Exchanger(threading.Thread): def update_cb(self): jsonresp = self.get_json('coinbase.com', "/api/v1/currencies/exchange_rates") - return dict([(r[7:].upper(), self._lookup_rate_cb(jsonresp, r)) for r in jsonresp if r.startswith("btc_to_")]) + return dict([(r[7:].upper(), Decimal(str(jsonresp[r]))) for r in jsonresp if r.startswith("btc_to_")]) def update_bc(self): jsonresp = self.get_json('blockchain.info', "/ticker") - return dict([(r, self._lookup_rate(jsonresp, r)) for r in jsonresp]) + return dict([(r, Decimal(str(jsonresp[r]["15m"]))) for r in jsonresp]) def update_lb(self): jsonresp = self.get_json('localbitcoins.com', "/bitcoinaverage/ticker-all-currencies/") - return dict([(r, self._lookup_rate_lb(jsonresp, r)) for r in jsonresp]) + return dict([(r, Decimal(jsonresp[r]["rates"]["last"])) for r in jsonresp]) def update_bv(self): jsonresp = self.get_json('api.bitcoinvenezuela.com', "/") @@ -186,16 +186,7 @@ class Exchanger(threading.Thread): def update_ba(self): jsonresp = self.get_json('api.bitcoinaverage.com', "/ticker/global/all") - return dict([(r, self._lookup_rate_ba(jsonresp, r)) for r in jsonresp if not r == "timestamp"]) - - def _lookup_rate(self, response, quote_id): - return decimal.Decimal(str(response[str(quote_id)]["15m"])) - def _lookup_rate_cb(self, response, quote_id): - return decimal.Decimal(str(response[str(quote_id)])) - def _lookup_rate_ba(self, response, quote_id): - return decimal.Decimal(response[str(quote_id)]["last"]) - def _lookup_rate_lb(self, response, quote_id): - return decimal.Decimal(response[str(quote_id)]["rates"]["last"]) + return dict([(r, Decimal(jsonresp[r]["last"])) for r in jsonresp if not r == "timestamp"]) class Plugin(BasePlugin): From 2bec9711ccb8cb4122390349ced9bd3e9608538b Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 28 Apr 2015 04:50:41 +0200 Subject: [PATCH 05/13] decimal.Decimal -> Decimal --- plugins/exchange_rate.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py index 3f10b983a..1ecb491eb 100644 --- a/plugins/exchange_rate.py +++ b/plugins/exchange_rate.py @@ -74,7 +74,7 @@ class Exchanger(threading.Thread): quote_currencies = self.quote_currencies.copy() if quote_currency not in quote_currencies: return None - return btc_amount * decimal.Decimal(str(quote_currencies[quote_currency])) + return btc_amount * Decimal(str(quote_currencies[quote_currency])) def stop(self): self.is_running = False @@ -122,7 +122,7 @@ class Exchanger(threading.Thread): current_cur = self.parent.config.get("currency", "EUR") if current_cur in quote_currencies: resp_rate = self.get_json('api.coindesk.com', "/v1/bpi/currentprice/" + str(current_cur) + ".json") - quote_currencies[str(current_cur)] = decimal.Decimal(str(resp_rate["bpi"][str(current_cur)]["rate_float"])) + quote_currencies[str(current_cur)] = Decimal(str(resp_rate["bpi"][str(current_cur)]["rate_float"])) return quote_currencies def update_ib(self): @@ -133,36 +133,36 @@ class Exchanger(threading.Thread): current_cur = self.parent.config.get("currency", "EUR") if current_cur in available_currencies: resp_rate = self.get_json('api.itbit.com', "/v1/markets/XBT" + str(current_cur) + "/ticker") - quote_currencies[str(current_cur)] = decimal.Decimal(str(resp_rate["lastPrice"])) + quote_currencies[str(current_cur)] = Decimal(str(resp_rate["lastPrice"])) return quote_currencies def update_wd(self): winkresp = self.get_json('winkdex.com', "/api/v0/price") - return {"USD": decimal.Decimal(str(winkresp["price"]))/decimal.Decimal("100.0")} + return {"USD": Decimal(str(winkresp["price"]))/Decimal("100.0")} def update_cv(self): jsonresp = self.get_json('www.cavirtex.com', "/api/CAD/ticker.json") cadprice = jsonresp["last"] - return {"CAD": decimal.Decimal(str(cadprice))} + return {"CAD": Decimal(str(cadprice))} def update_bm(self): jsonresp = self.get_json('www.bitmarket.pl', "/json/BTCPLN/ticker.json") pln_price = jsonresp["last"] - return {"PLN": decimal.Decimal(str(pln_price))} + return {"PLN": Decimal(str(pln_price))} def update_bx(self): jsonresp = self.get_json('pln.bitcurex.com', "/data/ticker.json") pln_price = jsonresp["last"] - return {"PLN": decimal.Decimal(str(pln_price))} + return {"PLN": Decimal(str(pln_price))} def update_CNY(self): jsonresp = self.get_json('data.btcchina.com', "/data/ticker") cnyprice = jsonresp["ticker"]["last"] - return {"CNY": decimal.Decimal(str(cnyprice))} + return {"CNY": Decimal(str(cnyprice))} def update_bp(self): jsonresp = self.get_json('bitpay.com', "/api/rates") - return dict([(str(r["code"]), decimal.Decimal(r["rate"])) for r in jsonresp]) + return dict([(str(r["code"]), Decimal(r["rate"])) for r in jsonresp]) def update_cb(self): jsonresp = self.get_json('coinbase.com', "/api/v1/currencies/exchange_rates") From 77648c284f069b743edd5df38f7bbee85be00a2d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 28 Apr 2015 06:28:20 +0200 Subject: [PATCH 06/13] exchange_rates: use requests module --- plugins/exchange_rate.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py index 1ecb491eb..ff21c10c6 100644 --- a/plugins/exchange_rate.py +++ b/plugins/exchange_rate.py @@ -3,7 +3,7 @@ from PyQt4.QtCore import * import datetime import decimal -import httplib +import requests import json import threading import time @@ -53,19 +53,8 @@ class Exchanger(threading.Thread): self.is_running = False def get_json(self, site, get_string): - try: - connection = httplib.HTTPSConnection(site) - connection.request("GET", get_string, headers={"User-Agent":"Electrum"}) - except Exception: - raise - resp = connection.getresponse() - if resp.reason == httplib.responses[httplib.NOT_FOUND]: - raise - try: - json_resp = json.loads(resp.read()) - except Exception: - raise - return json_resp + resp = requests.request('GET', 'https://' + site + get_string, headers={"User-Agent":"Electrum"}) + return resp.json() def exchange(self, btc_amount, quote_currency): with self.lock: From 7316f613d62b8a0f618f1a56b136bad5fac1fa25 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 28 Apr 2015 08:58:33 +0200 Subject: [PATCH 07/13] fix history command --- lib/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands.py b/lib/commands.py index 478cebb82..0136cf805 100644 --- a/lib/commands.py +++ b/lib/commands.py @@ -343,7 +343,7 @@ class Commands: balance = 0 out = [] for item in self.wallet.get_history(): - tx_hash, conf, is_mine, value, fee, balance, timestamp = item + tx_hash, conf, value, timestamp, balance = item try: time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3] except Exception: From 4bed294ddd8fd4e95ebc19ca56731ed536ed8826 Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 28 Apr 2015 12:54:33 +0200 Subject: [PATCH 08/13] Change labelsync to use https Self signed for now until I get a wildcard --- plugins/labels.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/labels.py b/plugins/labels.py index fcdef4c3b..0bde56447 100644 --- a/plugins/labels.py +++ b/plugins/labels.py @@ -25,7 +25,7 @@ from electrum_gui.qt.util import ThreadedButton, Buttons, CancelButton, OkButton class Plugin(BasePlugin): - target_host = 'sync.bysh.me:8080' + target_host = 'sync.bysh.me:9090' encode_password = None def fullname(self): @@ -140,8 +140,8 @@ class Plugin(BasePlugin): QMessageBox.information(None, _("Labels synchronised"), _("Your labels have been synchronised.")) def do_request(self, method, url = "/labels", is_batch=False, data=None): - url = 'http://' + self.target_host + url - kwargs = {'headers': {}} + url = 'https://' + self.target_host + url + kwargs = {'headers': {}, 'verify': False} if method == 'GET' and data: kwargs['params'] = data elif method == 'POST' and data: From 83e05b1183d3857060a015f3207bf55aff7665b2 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Wed, 29 Apr 2015 10:13:41 +0900 Subject: [PATCH 09/13] Modify format_satoshis to display amounts according to locale. In particular, thousands and decimal point separators are taken from locale. --- gui/qt/history_widget_lite.py | 2 +- lib/util.py | 37 +++++++++++++++-------------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/gui/qt/history_widget_lite.py b/gui/qt/history_widget_lite.py index a08f8d1c7..d202ff7ff 100644 --- a/gui/qt/history_widget_lite.py +++ b/gui/qt/history_widget_lite.py @@ -20,6 +20,6 @@ class HistoryWidget(QTreeWidget): if date is None: date = _("Unknown") item = QTreeWidgetItem([amount, address, date]) - if float(amount) < 0: + if amount.find('-') != -1: item.setForeground(0, QBrush(QColor("#BC1E1E"))) self.insertTopLevelItem(0, item) diff --git a/lib/util.py b/lib/util.py index 62fec50a5..514728447 100644 --- a/lib/util.py +++ b/lib/util.py @@ -108,28 +108,23 @@ def user_dir(): def format_satoshis(x, is_diff=False, num_zeros = 0, decimal_point = 8, whitespaces=False): - from decimal import Decimal - if x is None: - return 'unknown' - s = Decimal(x) - sign, digits, exp = s.as_tuple() - digits = map(str, digits) - while len(digits) < decimal_point + 1: - digits.insert(0,'0') - digits.insert(-decimal_point,'.') - s = ''.join(digits).rstrip('0') - if sign: - s = '-' + s - elif is_diff: - s = "+" + s - - p = s.find('.') - s += "0"*( 1 + num_zeros - ( len(s) - p )) + from locale import localeconv + if is_diff: + fmt = "{:+n}" + else: + fmt = "{:n}" + scale_factor = pow (10, decimal_point) + integer_part = fmt.format(int(x / float(scale_factor))) + dp = localeconv()['decimal_point'] + fract_part = ("{:0" + str(decimal_point) + "}").format(abs(x) % scale_factor) + fract_part = fract_part.rstrip('0') + if len(fract_part) < num_zeros: + fract_part += "0" * (num_zeros - len(fract_part)) + result = integer_part + dp + fract_part if whitespaces: - s += " "*( 1 + decimal_point - ( len(s) - p )) - s = " "*( 13 - decimal_point - ( p )) + s - return s - + result += " " * (decimal_point - len(fract_part)) + result = " " * (17 - len(result)) + result + return result def format_time(timestamp): import datetime From 0d35dd66d232cf0c0703572b91645c367a151582 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Wed, 29 Apr 2015 14:48:55 +0900 Subject: [PATCH 10/13] The lite GUI passes a decimal type --- lib/util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/util.py b/lib/util.py index 514728447..3744d8fd4 100644 --- a/lib/util.py +++ b/lib/util.py @@ -109,6 +109,7 @@ def user_dir(): def format_satoshis(x, is_diff=False, num_zeros = 0, decimal_point = 8, whitespaces=False): from locale import localeconv + x = int(x) # Some callers pass Decimal if is_diff: fmt = "{:+n}" else: From 4f45e0a083fef225afccf23ededa19775f315352 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Wed, 29 Apr 2015 15:09:55 +0900 Subject: [PATCH 11/13] Prior lost minus sign for fractional negative numbers --- lib/util.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/util.py b/lib/util.py index 3744d8fd4..3de3e0cf9 100644 --- a/lib/util.py +++ b/lib/util.py @@ -110,12 +110,12 @@ def user_dir(): def format_satoshis(x, is_diff=False, num_zeros = 0, decimal_point = 8, whitespaces=False): from locale import localeconv x = int(x) # Some callers pass Decimal - if is_diff: - fmt = "{:+n}" - else: - fmt = "{:n}" scale_factor = pow (10, decimal_point) - integer_part = fmt.format(int(x / float(scale_factor))) + integer_part = "{:n}".format(int(abs(x) / float(scale_factor))) + if x < 0: + integer_part = '-' + integer_part + elif is_diff: + integer_part = '+' + integer_part dp = localeconv()['decimal_point'] fract_part = ("{:0" + str(decimal_point) + "}").format(abs(x) % scale_factor) fract_part = fract_part.rstrip('0') From 953674aac7c949676f10bf9e83dcb34905cb1d05 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 29 Apr 2015 09:26:22 +0200 Subject: [PATCH 12/13] resize columns to content by default --- gui/qt/history_widget.py | 2 +- gui/qt/main_window.py | 19 +++++++++---------- gui/qt/util.py | 12 ++++-------- lib/util.py | 2 +- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/gui/qt/history_widget.py b/gui/qt/history_widget.py index 6449cc65b..94acd43ea 100644 --- a/gui/qt/history_widget.py +++ b/gui/qt/history_widget.py @@ -28,7 +28,7 @@ from electrum.plugins import run_hook class HistoryWidget(MyTreeWidget): def __init__(self, parent=None): - MyTreeWidget.__init__(self, parent, self.create_menu, [ '', _('Date'), _('Description') , _('Amount'), _('Balance')], [40, 140, None, 140, 140]) + MyTreeWidget.__init__(self, parent, self.create_menu, [ '', _('Date'), _('Description') , _('Amount'), _('Balance')], 2) self.config = self.parent.config self.setSortingEnabled(False) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index 4ad87cedc..c65d19b88 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -629,7 +629,7 @@ class ElectrumWindow(QMainWindow): buttons.addWidget(self.new_request_button) self.receive_requests_label = QLabel(_('My Requests')) - self.receive_list = MyTreeWidget(self, self.receive_list_menu, [_('Date'), _('Account'), _('Address'), _('Description'), _('Amount'), _('Status')], []) + self.receive_list = MyTreeWidget(self, self.receive_list_menu, [_('Date'), _('Account'), _('Address'), _('Description'), _('Amount'), _('Status')], 3) self.receive_list.currentItemChanged.connect(self.receive_item_changed) self.receive_list.itemClicked.connect(self.receive_item_changed) self.receive_list.setSortingEnabled(True) @@ -901,7 +901,7 @@ class ElectrumWindow(QMainWindow): self.from_label = QLabel(_('From')) grid.addWidget(self.from_label, 3, 0) - self.from_list = MyTreeWidget(self, self.from_list_menu, ['',''], [350, 50]) + self.from_list = MyTreeWidget(self, self.from_list_menu, ['','']) self.from_list.setHeaderHidden(True) self.from_list.setMaximumHeight(80) grid.addWidget(self.from_list, 3, 1, 1, 3) @@ -968,12 +968,11 @@ class ElectrumWindow(QMainWindow): self.fee_e.textChanged.connect(entry_changed) self.invoices_label = QLabel(_('Invoices')) - self.invoices_list = MyTreeWidget( - self, - self.create_invoice_menu, - [_('Date'), _('Requestor'), _('Description'), _('Amount'), _('Status')], - [150, 150, None, 150, 100] - ) + self.invoices_list = MyTreeWidget(self, self.create_invoice_menu, + [_('Date'), _('Requestor'), _('Description'), _('Amount'), _('Status')], 2) + self.invoices_list.header().setResizeMode(1, QHeaderView.Interactive) + self.invoices_list.setColumnWidth(1, 200) + vbox0 = QVBoxLayout() vbox0.addLayout(grid) vbox0.addLayout(buttons) @@ -1309,14 +1308,14 @@ class ElectrumWindow(QMainWindow): return w def create_addresses_tab(self): - l = MyTreeWidget(self, self.create_receive_menu, [ _('Address'), _('Label'), _('Balance'), _('Tx')], [370, None, 130]) + l = MyTreeWidget(self, self.create_receive_menu, [ _('Address'), _('Label'), _('Balance'), _('Tx')], 1) l.setSelectionMode(QAbstractItemView.ExtendedSelection) l.setSortingEnabled(False) self.address_list = l return self.create_list_tab(l) def create_contacts_tab(self): - l = MyTreeWidget(self, self.create_contact_menu, [_('Key'), _('Value'), _('Type')], [250, None, 130]) + l = MyTreeWidget(self, self.create_contact_menu, [_('Key'), _('Value'), _('Type')], 1) self.contacts_list = l return self.create_list_tab(l) diff --git a/gui/qt/util.py b/gui/qt/util.py index 2a3ff28e6..0babb0e2d 100644 --- a/gui/qt/util.py +++ b/gui/qt/util.py @@ -260,7 +260,7 @@ def filename_field(parent, config, defaultname, select_msg): class MyTreeWidget(QTreeWidget): - def __init__(self, parent, create_menu, headers, column_width): + def __init__(self, parent, create_menu, headers, stretch_column=0): QTreeWidget.__init__(self, parent) self.parent = parent self.setColumnCount(len(headers)) @@ -277,13 +277,9 @@ class MyTreeWidget(QTreeWidget): self.edit_column = None self.itemDoubleClicked.connect(self.edit_label) self.itemChanged.connect(self.label_changed) - # set column width - for i, width in enumerate(column_width): - if width is None: - self.header().setResizeMode(i, QHeaderView.Stretch) - self.edit_column = i - else: - self.setColumnWidth(i, width) + # stretch + for i in range(len(headers)): + self.header().setResizeMode(i, QHeaderView.Stretch if i == stretch_column else QHeaderView.ResizeToContents) self.setSortingEnabled(True) def on_activated(self, item): diff --git a/lib/util.py b/lib/util.py index 3de3e0cf9..72685fff2 100644 --- a/lib/util.py +++ b/lib/util.py @@ -124,7 +124,7 @@ def format_satoshis(x, is_diff=False, num_zeros = 0, decimal_point = 8, whitespa result = integer_part + dp + fract_part if whitespaces: result += " " * (decimal_point - len(fract_part)) - result = " " * (17 - len(result)) + result + result = " " * (15 - len(result)) + result return result def format_time(timestamp): From ec953c7f30e2e62d45a4dca71c9e9ae8617d43e6 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Wed, 29 Apr 2015 21:41:27 +0900 Subject: [PATCH 13/13] Expand default BTC / fiat amount width. Seems to suffice for expected usage. --- gui/qt/amountedit.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/qt/amountedit.py b/gui/qt/amountedit.py index f9e939b02..dcb14aad7 100644 --- a/gui/qt/amountedit.py +++ b/gui/qt/amountedit.py @@ -18,6 +18,8 @@ class AmountEdit(MyLineEdit): def __init__(self, base_unit, is_int = False, parent=None): QLineEdit.__init__(self, parent) + # This seems sufficient for hundred-BTC amounts with 8 decimals + self.setFixedWidth(140) self.base_unit = base_unit self.textChanged.connect(self.numbify) self.is_int = is_int