def import_exchanges(logger, file): """Imports 'Exchange' table from file. Example: import_exchanges(logger, "./.../static/inputs/exchanges.txt") """ try: with open(file, "r", encoding='utf-8') as f: f_contents = f.readlines() except Exception as e: logger.error("import_exchanges: Could not read file '{}'".format(file)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Delete previous records db_exch = Exchange.query.all() if len(f_contents) > len(db_exch) * 0.8: Exchange.query.delete() else: logger.warning("import_exchanges: Much less rows in new version." " '{}' Vs '{}'.".format(len(f_contents), len(db_exch))) # Loop for each line for line in f_contents: id, name, type, img, url, af, lan = (line.replace("\n", "")).split("¬") exch = Exchange(id=id, name=name, type=type, img_fn=img, site_url=url, affiliate=af, language=lan) db.session.add(exch) db.session.commit() logger.info("import_exchanges: {} rows inserted".format(len(f_contents))) return
def update_tags_info(logger): """Gets the tag info from Coinpaprika. """ # Fetch JSON file from site try: with urlopen("https://api.coinpaprika.com/v1/tags") as response: source = response.read() tags_data = json.loads(source) except Exception as e: logger.error("update_tags_info: Could not fetch json") error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Check if JSON is a list. Throw error otherwise if not isinstance(tags_data, list): error_desc = "update_tags_info: The JSON is not a list" logger.error(error_desc) error_notifier("update_tags_info", error_desc, mail, logger) return error_desc # Create return dictionary tags_dict = {} # Read JSON file for index, tag in enumerate(tags_data): try: id = tag["id"] except KeyError as e: logger.warning("update_tags_info: No key found for {}".format(tag)) continue tags_dict[id] = tag print("New tag added: {} ({})".format(id, index + 1)) # Repleace data in JSON with merged data and save to file: file = Params.TAG_INFO_FILE with open(file, "w") as f: json.dump(tags_dict, f) logger.debug("update_tags_info: Tags updated") return "ok"
def import_pairs(logger, file): """Imports 'TradePair' table from file. Example: import_coins(logger, "./.../static/inputs/pairs.txt") """ try: with open(file, "r", encoding='utf-8') as f: f_contents = f.readlines() except Exception as e: logger.error("import_pairs: Could not read file '{}'".format(file)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Delete previous records db_pair = TradePair.query.all() if len(f_contents) > len(db_pair) * 0.8: TradePair.query.delete() else: logger.warning("import_pairs: Much less rows in new version." " '{}' Vs '{}'.".format(len(f_contents), len(db_pair))) # Loop for each line for line in f_contents: exchange, coin, base_coin, volume = line.replace("\n", "").split("¬") pair = TradePair(exchange=exchange, coin=coin, base_coin=base_coin, volume=volume) db.session.add(pair) db.session.commit() logger.info("import_pairs: {} rows inserted".format(len(f_contents))) return
def update_pairs(): try: import_pairs(logger, Params.PAIRS_PATH) return "ok" except Exception as e: error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc()
def update_prcs(): try: update_prices(logger) return "ok" except Exception as e: error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc()
def import_coins_route(): try: import_coins(logger, Params.COINS_PATH) return "ok" except Exception as e: error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc()
def get_tag_info(): global tag_info_file try: exit_code = update_tags_info(logger) tag_info_file = load_json_file(Params.TAG_INFO_FILE, logger) return str(exit_code) except Exception as e: error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc()
def get_bittrex_fees(logger): """Fetches the fees from Bittrex API and stores them in a TXT file.. """ # File where prices will be temporaly stored dest_file = generate_file_path('static/imports', 'bittrex_fees') try: url = Params.URL_BITTREX_FEES with urlopen(url) as response: source = response.read() bittrex_fees = json.loads(source) except Exception as e: logger.error("get_bittrex_fees [1]: Error fetching Bittrex fees from " "source. URL='{}' [{}]".format(url, e)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Check if response success returns True if not bittrex_fees["success"]: logger.error("get_bittrex_fees [2]: Error fetching Bittrex fees from " "source. URL='{}'".format(url)) return 2 if 'result' in bittrex_fees.keys(): fee_info = bittrex_fees['result'] with open(dest_file, "a") as f: # Write header header = "" for item in fee_info[0].keys(): header += str(item) + ";" f.write(header + "\n") # Write info for each coin for coin in fee_info: currency = coin['Currency'] currencyLong = coin['CurrencyLong'] minConfirmation = coin['MinConfirmation'] txFee = coin['TxFee'] isActive = coin['IsActive'] isRestricted = coin['IsRestricted'] coinType = coin['CoinType'] baseAddress = coin['BaseAddress'] notice = coin['Notice'] f.write("{};{};{};{};{};{};{};{};{}\n".format( currency, currencyLong, minConfirmation, txFee, isActive, isRestricted, coinType, coinType, baseAddress, notice)) logger.info("get_bittrex_fees: Process finished!")
def promotions(): try: # If 'calc_currency' exists in cookie, use it currency = request.cookies.get('calc_currency') if currency: curr = currency else: curr = Params.DEFAULT_CURRENCY curr = get_coin(curr) # Get forms feedback_form = FeedbackForm() promo_form = PromoForm() # Get Meta tags title = get_meta_tags('Promotions', 'Title') description = get_meta_tags('Promotions', 'Description') # Get promos promos = get_promos() # Actions if Feedback Form was filled if promo_form.promo_submit.data: if promo_form.validate(): manage_promo_form(promo_form, 'Promos') # Actions if Feedback Form was filled if feedback_form.feedback_submit.data: if feedback_form.validate(): manage_feedback_form(feedback_form, request.path) # Catch generic exception just in case anything went wront except Exception as e: logger.error("routes: Error procesing 'crypto-promotions'") error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return redirect(url_for('home')) # Load page return render_template('promotions.html', curr=curr, title=title, description=description, feedback_form=feedback_form, promo_form=promo_form, promos=promos)
def import_fees(logger, file): """Imports 'Exchange' table from file. Example: import_fees(logger, "./.../static/inputs/fees.txt") """ try: with open(file, "r", encoding='utf-8') as f: f_contents = f.readlines() except Exception as e: logger.error("import_fees: Could not read file '{}'".format(file)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Delete previous records db_fee = Fee.query.all() if len(f_contents) > len(db_fee) * 0.8: Fee.query.delete() else: logger.warning("import_fees: Much less rows in new version." " '{}' Vs '{}'.".format(len(f_contents), len(db_fee))) # Loop for each line for line in f_contents: exch, action, scope, amt, min_amt, fee_coin, type = line\ .replace("\n", "").split("¬") if amt == '': amt = None if min_amt == '': min_amt = None fee = Fee(exchange=exch, action=action, scope=scope, amount=amt, min_amount=min_amt, fee_coin=fee_coin, type=type) db.session.add(fee) db.session.commit() logger.info("import_fees: {} rows inserted".format(len(f_contents))) return
def import_coins(logger, file): """Imports 'Coin' table from file. Example: import_coins(logger, "./.../static/inputs/coins.txt") """ try: with open(file, "r", encoding='utf-8') as f: f_contents = f.readlines() except Exception as e: logger.error("import_coins: Could not read file '{}'".format(file)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Delete previous records db_coin = Coin.query.all() if len(f_contents) > len(db_coin) * 0.8: Coin.query.delete() else: logger.warning("import_coins: Much less rows in new version." " '{}' Vs '{}'.".format(len(f_contents), len(db_coin))) # Loop for each line for line in f_contents: id, symbol, ln, url_ln, prc_id, p_id, rank, url, loc_fn, type, stat = line\ .replace("\n", "").split("¬") coin = Coin(id=id, symbol=symbol, long_name=ln, url_name=url_ln, price_id=prc_id, paprika_id=p_id, ranking=rank, image_url=url, local_fn=loc_fn, type=type, status=stat) db.session.add(coin) db.session.commit() logger.info("import_coins: {} rows inserted".format(len(f_contents))) return
def get_coins_from_paprika(logger): """Gets list of coins from paprika and store them in file. File just to MANUALLY map coins with Coinpaprika ID. """ dest_file = generate_file_path('static/imports', 'paprika_coins') # Fetch JSON file from site try: with urlopen("https://api.coinpaprika.com/v1/coins") as response: source = response.read() coins_data = json.loads(source) except Exception as e: logger.error("get_coins_from_paprika: Could not fetch json") error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Check if JSON is a list. Throw error otherwise if not isinstance(coins_data, list): logger.error("get_coins_from_paprika: The JSON is not a list") error_notifier("get_coins_from_paprika", "The JSON is not a list", mail, logger) return "get_coins_from_paprika: The JSON is not a list" # Read JSON and generate file with open(dest_file, "a") as f: for crypto in coins_data: try: id = crypto["id"] name = crypto["name"] symbol = crypto["symbol"] rank = crypto["rank"] type = crypto["type"] f.write(("{};{};{};{};{}\n").format(id, name, symbol, rank, type)) except KeyError as e: logger.warning("get_coins_from_paprika: Bad JSON format for " "'{}'".format(crypto)) continue except Exception as e: logger.error("get_coins_from_paprika: Unexpected error for " "'{}'. Coin not stored".format(crypto)) error_notifier( type(e).__name__, traceback.format_exc(), mail, logger) continue logger.debug("get_coins_from_paprika: Coins updated") return "ok"
def update_coins(logger): """Gets all the coins available in Provider. ONLY TO BE USED IN TEST DATABASES TO LATER DOWNLOAD AND EDIT COINS. """ return None logger.info("update_coins(): Starting process") try: url = Params.URL_COINS with urlopen(url) as response: source = response.read() coins = json.loads(source) # If Provider responds successfully, process data: if coins["Response"] != 'Success': error = coins["Response"] error_desc = ("update_coins: Provider error: '{}'".format(error)) logger.error(error_desc) error_notifier("update_coins", error_desc, mail, logger) return error_desc except Exception as e: logger.error("update_coins: Error fetching coins from " "source. URL='{}' [{}]".format(url, e)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Compare size of query with coins currently in DB cryptos_in_db = Coin.query.filter_by(type="Crypto").all() coins = coins["Data"] if len(coins) < len(cryptos_in_db) * 0.8: error_desc = ("update_coins(): Did not update coins because there are " "much less coins than currently in DB: {} Vs {}." "".format(len(coins), len(cryptos_in_db))) logger.error(error_desc) error_notifier("update_coins", error_desc, mail, logger) return error_desc else: # Delete table contents Coin.query.filter_by(type="Crypto").delete() db.session.commit() for coin in coins: try: # Get symbol and long_name symbol = coins[coin]["Symbol"] long_name = coins[coin]["CoinName"] # If Symbol has blank spaces, don't store coin (bad format) if " " in symbol: logger.warning("update_coins: Coin '{}' not stored due to " "wrong symbol format (blank spaces)" "".format(symbol)) continue # Get Image URL try: image_url = coins[coin]["ImageUrl"] except KeyError as e: image_url = None if image_url == 'N/A': image_url = None # Generate local filename local_fn = None if image_url: local_fn = image_url.replace('/media/', '').replace('/', '_') # Insert coin in DB c = Coin(symbol=symbol, long_name=long_name, market_cap=None, image_url=image_url, local_fn=local_fn, type="Crypto", status="Active") db.session.add(c) db.session.commit() # Warning if unicode characters are found in the JSON except UnicodeEncodeError as e: logger.warning( "update_coins() [UnicodeEncodeError] [{}]: {}".format( symbol, e)) continue except KeyError as e: logger.warning("update_coins() [KeyError] [{}]: {}".format( symbol, e)) continue logger.info("update_coins(): 'COINS' updated") return 0
def exch_results(url_orig_coin=None, url_dest_coin=None): """There are two ways of landing in this page: - Search form was filled: performs search and returns results - Direct external link (no form was filled!): in this case, a page with no results is shown, and then from the page a proper calculation is triggered. It is done like this to let the user load the page as soon as possible. """ session_id = request.cookies.get('session') if not session_id: new_session_id = token_hex(8) sorted_paths = [] input_form = SearchForm() # Choose currency: 1) Form 2) Cookie 3) Default currency = request.cookies.get('calc_currency') if input_form.currency.data != 'Empty': curr = input_form.currency.data elif currency: curr = currency input_form.currency.data = curr else: curr = Params.DEFAULT_CURRENCY input_form.currency.data = curr auto_search = False feedback_form = FeedbackForm() exchanges = get_exchanges(['Exchange']) user_exchanges = [exch.id for exch in exchanges] path_results = None open_fbck_modal = False # Get Meta tags (in case form was not filled) title = get_meta_tags('Exchanges|Results', 'Title', [url_orig_coin.upper(), url_dest_coin.upper()]) description = get_meta_tags('Exchanges|Results', 'Description', [url_orig_coin.upper(), url_dest_coin.upper()]) # 1) ACTIONS IF *SEARCH* FORM WAS FILLED if input_form.search_submit.data: if input_form.validate(): curr = input_form.currency.data orig_loc = get_exch_by_name(input_form.orig_loc.data) orig_coin = get_coin_by_longname(input_form.orig_coin.data) # Save 'orig_amt' as Float or integer depending on value num = float(input_form.orig_amt.data) if num % 1 == 0: num = int(num) orig_amt = num dest_loc = get_exch_by_name(input_form.dest_loc.data) dest_coin = get_coin_by_longname(input_form.dest_coin.data) user_exchanges = input_form.exchanges.data # Get Meta tags (again, if form was filled) title = get_meta_tags('Exchanges|Results', 'Title', [orig_coin.symbol, dest_coin.symbol]) description = get_meta_tags('Exchanges|Results', 'Description', [orig_coin.long_name, dest_coin.long_name]) # If user selected all Exchanges or none of them, don't filter if len(user_exchanges) == len(exchanges): user_exchanges = [] fee_settings = {"CEP": input_form.cep_promos.data, "Default": input_form.default_fee.data, "Binance": input_form.binance_fee.data} # start_time = datetime.datetime.now() try: paths = calc_paths(orig_loc, orig_coin, orig_amt, dest_loc, dest_coin, curr, fee_settings, logger) path_results = len(paths) # Catch generic exception just in case anything went wront in logic except Exception as e: db.session.rollback() error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) paths = [] path_results = -1 # If no results were found, send worning email if path_results == 0: args_dic = {"orig_amt": orig_amt, "orig_coin": orig_coin.id, "orig_loc": orig_loc.id, "dest_coin": dest_coin.id, "dest_loc": dest_loc.id, "currency": curr} warning_notifier("Search with no results", args_dic, mail, logger) # Register query # finish_time = datetime.datetime.now() # results = len(paths) # exchs = "" # for exch in user_exchanges: # exchs += exch + '|' # exchs = exchs[:-1] # try: # query = QueryRegister(session_id=session_id, # orig_amt=orig_amt, # orig_coin=orig_coin.id, # orig_loc=orig_loc.id, # dest_coin=dest_coin.id, # dest_loc=dest_loc.id, # currency=curr, # connection_type=connection_type, # exchanges=exchs, # results=results, # start_time=start_time, # finish_time=finish_time) # db.session.add(query) # db.session.commit() # except Exception as e: # db.session.rollback() # error_notifier(type(e).__name__, # traceback.format_exc(), # mail, # logger) # Select all Exchanges if no partial selection was made if not user_exchanges: user_exchanges = [exch.id for exch in exchanges] # Return capped list of results sorted_paths = sorted(paths, key=lambda x: x.total_fees) sorted_paths = sorted_paths[0:Params.MAX_PATHS] # 2) ACTIONS IF *NO* FORM WAS FILLED (DIRECT LINK!) else: orig_coin = get_coin(url_orig_coin.upper()) dest_coin = get_coin(url_dest_coin.upper()) if orig_coin: input_form.orig_coin.data = orig_coin.long_name amt = fx_exchange("USD", orig_coin.id, 3000, logger) input_form.orig_amt.data = str(math.ceil(amt)) + " " if dest_coin: input_form.dest_coin.data = dest_coin.long_name auto_search = True resp = make_response(render_template('exch_results.html', form=input_form, curr=curr, exchanges=exchanges, user_exchanges=user_exchanges, paths=sorted_paths, path_results=path_results, auto_search=auto_search, feedback_form=feedback_form, title=title, description=description, open_feedback_modal=open_fbck_modal, url_orig_coin=url_orig_coin, url_dest_coin=url_dest_coin)) # Store session ID & Currency in cookie if there are not already stored if not session_id: resp.set_cookie('session', new_session_id) if currency != curr: resp.set_cookie('calc_currency', curr) return resp
def exchange_fees_by_coin(coin_url_name): """Displays the fees for the coin given as as argument. """ try: coin = get_coin_by_urlname(coin_url_name) # If coin not recognized, redirect if not coin: return redirect(url_for('exchange_fees_coin')) # Get coin fees coin_fees = get_coin_fees(coin.id) # Get coin data coin_data = get_coin_data(coin.paprika_id, coin_info_file, people_info_file, tag_info_file, logger) # If 'calc_currency' exists in cookie, use it currency = request.cookies.get('calc_currency') if currency: curr = currency else: curr = Params.DEFAULT_CURRENCY feedback_form = FeedbackForm() # Get Meta tags title = get_meta_tags('Exchanges|Fees|Coin|Coin', 'Title', [coin.long_name, coin.symbol]) description = get_meta_tags('Exchanges|Fees|Coin|Coin', 'Description', [coin.long_name, coin.symbol]) # Get search coins if (coin.type == 'Crypto'): quick_search_coins = Params.QUICK_SEARCH_COINS['Crypto'] else: quick_search_coins = Params.QUICK_SEARCH_COINS['Fiat'] search_coins = [] search_count = 0 for item in quick_search_coins: if item != coin.id: coinA = get_coin(item) search_coins.append({"coinA": coinA, "coinB": coin}) search_count += 1 # If for items have been gathered, exit loop if search_count == 4: break # Catch generic exception just in case anything went wront except Exception as e: logger.error("routes: Error procesing '{}'".format(coin_url_name)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return redirect(url_for('exchange_fees_coin')) # Load page return render_template('exchange_fees_by_coin.html', coin=coin, coin_data=coin_data, curr=curr, title=title, description=description, feedback_form=feedback_form, coin_fees=coin_fees, search_coins=search_coins)
def update_people_info(logger): """Gets the people info from Coinpaprika. """ # Read coins file coin_info_file = Params.COIN_INFO_FILE try: with open(coin_info_file) as f: coin_info = json.load(f) except Exception as e: logger.error("update_prices: Could not read '{}'" "".format(coin_info_file)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() coins = coin_info.keys() # Create return dictionary people_dict = {} # Loop for each coin for coin in coins: # Fetch JSON file from site try: team = coin_info[coin]["team"] # Check if 'team' is a list if not isinstance(team, list): logger.info("update_people_info: No team list found for " "coin '{} []'".format(coin_info[coin]["symbol"], coin_info[coin]["id"])) continue # Loop for each person in the list for person in team: person_id = person["id"] # Fetch info from people try: with urlopen("https://api.coinpaprika.com/v1/people/{}" "".format(person_id)) as response: source = response.read() person_data = json.loads(source) except Exception as e: logger.warning("update_people_info: Could not fetch json" " for person_id='{}''".format(person_id)) continue # Check if JSON is a list. Throw error otherwise if not isinstance(person_data, dict): error_desc = "update_people_info: The JSON is not a list" logger.error(error_desc) error_notifier("update_people_info", error_desc, mail, logger) return error_desc # Read JSON file try: id = person_data["id"] except KeyError as e: logger.warning("update_people_info: No ID found for {}" "[{}]-{}".format(coin_info[coin]["symbol"], coin_info[coin]["id"], person_id)) continue except Exception as e: logger.error("update_people_info: Error reading {}[{}]-{}" "".format(coin_info[coin]["symbol"], coin_info[coin]["id"], person_id)) error_notifier( type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() if person_id not in people_dict: people_dict[person_id] = person_data logger.debug("New person added: {}".format(person_id)) print("New person added: {}".format(person_id)) except KeyError as e: logger.warning("update_people_info: No key found for {}" "".format(coin_info[coin])) continue # Repleace data in JSON with merged data and save to file: file = Params.PEOPLE_INFO_FILE with open(file, "w") as f: json.dump(people_dict, f) logger.debug("update_people_info: Tags updated") return "ok"
def update_coins_info(logger): """Gets the coin info from Coinpaprika. """ # Get coins coins = get_coins(type="Crypto") print("update_coins_info: Processing '{}' coins. Starting...") # Create return dictionary coins_dict = {} # Loop for each coin for index, coin in enumerate(coins): print("update_coins_info: Processing coin '{}' --> {}" "".format(index + 1, coin)) # Fetch JSON file from site try: coin_id = coin.paprika_id if not coin_id: continue with urlopen("https://api.coinpaprika.com/v1/coins/{}" "".format(coin_id)) as response: source = response.read() coin_data = json.loads(source) except Exception as e: error_desc = ("update_coins_info: Could not fetch json for" " {} [{}]".format(coin.symbol, coin.paprika_id)) print(error_desc) logger.error(error_desc) error_notifier( type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Check if JSON is a list. Throw error otherwise if not isinstance(coin_data, dict): error_desc = ("update_coins_info: The JSON for coin '{} [{}]'" " is not a list".format(coin.symbol, coin.paprika_id)) print(error_desc) logger.error(error_desc) error_notifier("update_coins_info", error_desc, mail, logger) return error_desc # Read JSON file try: id = coin_data["id"] except KeyError as e: error_desc = ("update_coins_info: No key found for {}" "".format(coin.symbol)) print(error_desc) logger.warning(error_desc) break except Exception as e: error_desc = ("update_coins_info: Error reading '{}': {}" "".format(coin.id, coin_data)) print(error_desc) logger.error(error_desc) error_notifier( type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() coins_dict[id] = coin_data logger.debug("New coin added: {} ({})".format(coin.symbol, index + 1)) print("New coin added: {} ({})".format(coin.symbol, index + 1)) # Repleace data in JSON with merged data and save to file: file = Params.COIN_INFO_FILE with open(file, "w") as f: json.dump(coins_dict, f) logger.debug("update_coins_info: Tags updated") print("update_coins_info: Tags updated") return "ok"
def update_prices(logger): """Fetches the prices of all the cryptos in database. If no price is found for any of them, they are flagged as 'Inactive'. """ # File where prices will be temporaly stored dest_file = generate_file_path('static/imports', 'prices') # Get cryptos and Fiat to fetch prices from cryptos = get_active_coins("Crypto", True) # Get Coins to request prices against and generate string fiats = get_active_coins("Fiat", True) currencies = "BTC,ETH" for fiat in fiats: currencies += "," + fiat # Generate a string of less than 'max_len' to fetch the data max_len = Params.PRICE_FETCH_LENGTH next_coins = "" next_coins_lst = [] is_compl = False for index, coin in enumerate(cryptos): # While list is not complete (or not last item), append coins if ((len(next_coins) + len(coin) + 1 < max_len) or (index == len(cryptos) - 1)): next_coins += coin + "," next_coins_lst.append(coin) else: is_compl = True # If the loop is finished, time to fetch data as well if index == len(cryptos) - 1: is_compl = True # Fetch data if list is generated or loop is finished if is_compl: try: url = Params.URL_PRICES.format(next_coins, currencies) with urlopen(url) as response: source = response.read() prices = json.loads(source) except Exception as e: logger.error("update_prices: Error fetching prices from " "source. URL='{}' [{}]".format(url, e)) error_notifier( type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Store data in file with open(dest_file, "a") as f: for crypto in prices: try: for currency in prices[crypto]: prc = prices[crypto][currency] crypto_id = get_coin_by_price_id(crypto) if crypto_id: f.write(("{};{};{}\n").format( crypto_id.id, currency, prc)) else: logger.warning( "update_prices: No coin found " "in DB for price_id={}".format(crypto)) next_coins_lst.remove(crypto) except KeyError as e: logger.warning("update_prices: Bad JSON format for " "'{}'".format(crypto)) continue except Exception as e: logger.warning( "update_prices: Unexpected error for " "'{}'. Prices not stored".format(crypto)) continue # Flag as "Inactive" coins from which there are no prices # for item in next_coins_lst: # db_crypto = Coin.query.filter_by(symbol=item).first() # if db_crypto: # db_crypto.status = "Inactive" # logger.warning("fetch_prices: '{}' flagged inactive as " # "no prices could be fetched".format(item)) db.session.commit() # Initialize variables to start the process again is_compl = False next_coins = coin + "," next_coins_lst = [coin] # Read file to store contents in DB try: with open(dest_file, "r", encoding='utf-8') as f: f_contents = f.readlines() except Exception as e: logger.error("update_prices: Could not read '{}'".format(dest_file)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return traceback.format_exc() # Compare sizes db_prices = Price.query.all() if len(f_contents) < len(db_prices) * 0.8: error_desc = ("update_prices: Fetched much less prices that previous " "time ('{}' Vs '{}'). Not stored in DB.".format( len(f_contents), len(db_prices))) logger.error(error_desc) error_notifier("update_prices", error_desc, mail, logger) return error_desc # Delete prices from DB Price.query.delete() # Store prices in DB for line in f_contents: coin, currency, price = (line.replace("\n", "")).split(";") prc = Price(coin=coin, base_coin=currency, price=price) db.session.add(prc) db.session.commit() logger.info("update_prices: Prices updated [{} rows inserted]".format( len(f_contents))) # Finally, update coins in JSON file update_coins_file("crypto_exchange_path/static/data/coins.json") return "ok"
def exch_results(url_orig_coin=None, url_dest_coin=None): """There are two ways of landing in this page: - Search form was filled: performs search and returns results - Direct external link (no form was filled!): in this case, a page with no results is shown, and then from the page a proper calculation is triggered. It is done like this to let the user load the page as soon as possible. """ try: session_id = request.cookies.get('session') if not session_id: new_session_id = token_hex(8) sorted_paths = [] input_form = SearchForm() # Choose currency: 1) Form 2) Cookie 3) Default currency = request.cookies.get('calc_currency') if input_form.currency.data != 'Empty': curr = input_form.currency.data elif currency: curr = currency input_form.currency.data = curr else: curr = Params.DEFAULT_CURRENCY input_form.currency.data = curr curr = get_coin(curr) if not curr: curr = get_coin('usd-us-dollars') auto_search = False feedback_form = FeedbackForm() exchanges = get_exchanges(['Exchange'], status='Active') user_exchanges = [exch.id for exch in exchanges] path_results = None amt_warning = None # Get Meta tags (in case form was not filled) title = get_meta_tags('Exchanges|Results', 'Title', [url_orig_coin.upper(), url_dest_coin.upper()]) description = get_meta_tags('Exchanges|Results', 'Description', [url_orig_coin.upper(), url_dest_coin.upper()]) # 1) ACTIONS IF *SEARCH* FORM WAS FILLED if input_form.search_submit.data: if input_form.validate(): curr = input_form.currency.data curr = get_coin(curr) orig_loc = get_exch_by_name(input_form.orig_loc.data) orig_coin = get_coin_by_longname(input_form.orig_coin.data) # Save 'orig_amt' as Float or integer depending on value num = float(input_form.orig_amt.data) if num % 1 == 0: num = int(num) orig_amt = num dest_loc = get_exch_by_name(input_form.dest_loc.data) dest_coin = get_coin_by_longname(input_form.dest_coin.data) user_exchanges = input_form.exchanges.data # Get Meta tags (again, if form was filled) title = get_meta_tags('Exchanges|Results', 'Title', [orig_coin.symbol, dest_coin.symbol]) description = get_meta_tags('Exchanges|Results', 'Description', [orig_coin.long_name, dest_coin.long_name]) # If user selected all Exchanges or none of them, don't filter if len(user_exchanges) == len(exchanges): user_exchanges = [] fee_settings = {"CEP": input_form.cep_promos.data, "Default": input_form.default_fee.data, "Binance": input_form.binance_fee.data} # start_time = datetime.datetime.now() try: paths = calc_paths(orig_loc, orig_coin, orig_amt, dest_loc, dest_coin, curr, fee_settings, logger) path_results = len(paths) # Catch generic exception if anything went wrong in logic except Exception as e: db.session.rollback() error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) paths = [] path_results = -1 # If no results were found, check "orign_amt" to try again if path_results == 0: amount_usd = fx_exchange(orig_coin.id, 'usd-us-dollars', orig_amt, logger) if amount_usd and amount_usd < Params.RECALC_AMOUNT: orig_amt = round_number(orig_amt * Params.RECALC_AMOUNT / amount_usd) orig_amt = round_big_number(orig_amt) input_form.orig_amt.data = orig_amt try: paths = calc_paths(orig_loc, orig_coin, orig_amt, dest_loc, dest_coin, curr, fee_settings, logger) path_results = len(paths) amt_warning = True # Catch generic exception if anything went wrong except Exception as e: db.session.rollback() error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) paths = [] path_results = -1 # If no results were found, send worning email if path_results == 0: args_dic = {"orig_amt": orig_amt, "orig_coin": orig_coin.id, "orig_loc": orig_loc.id, "dest_coin": dest_coin.id, "dest_loc": dest_loc.id, "currency": curr.id} warning_notifier("Search with no results", args_dic, mail, logger) # Select all Exchanges if no partial selection was made if not user_exchanges: user_exchanges = [exch.id for exch in exchanges] # Return capped list of results sorted_paths = sorted(paths, key=lambda x: x.total_fees) sorted_paths = sorted_paths[0:Params.MAX_PATHS] # 2) ACTIONS IF *NO* FORM WAS FILLED (DIRECT LINK!) else: orig_coin = get_coin_by_symbol(url_orig_coin.upper()) dest_coin = get_coin_by_symbol(url_dest_coin.upper()) # If 'orig_coin' or 'dest_coin' not found, try in mappings table if not orig_coin: new_symbol = get_mapping('Coin', 'symbol', url_orig_coin.upper()) if new_symbol: orig_coin = get_coin_by_symbol(new_symbol) if not dest_coin: new_symbol = get_mapping('Coin', 'symbol', url_dest_coin.upper()) if new_symbol: dest_coin = get_coin_by_symbol(new_symbol) # Procced with function if orig_coin: input_form.orig_coin.data = orig_coin.long_name amt = fx_exchange('usd-us-dollars', orig_coin.id, 3000, logger) if amt: amt = round_big_number(amt) input_form.orig_amt.data = str(math.ceil(amt)) + " " if dest_coin: input_form.dest_coin.data = dest_coin.long_name auto_search = True # Actions if Feedback Form was filled if feedback_form.feedback_submit.data: if feedback_form.validate(): manage_feedback_form(feedback_form, request.path) # Catch generic exception just in case anything went wront in logic except Exception as e: db.session.rollback() logger.error("Routes: Non-handled exception at '{}'" .format(request.url)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return redirect(url_for('exchanges')) resp = make_response(render_template('exch_results.html', form=input_form, curr=curr, exchanges=exchanges, user_exchanges=user_exchanges, paths=sorted_paths, amt_warning=amt_warning, path_results=path_results, auto_search=auto_search, feedback_form=feedback_form, title=title, description=description, url_orig_coin=url_orig_coin, url_dest_coin=url_dest_coin)) # Store session ID & Currency in cookie if there are not already stored if not session_id: resp.set_cookie('session', new_session_id) if currency != curr.id: resp.set_cookie('calc_currency', curr.id) return resp