diff --git a/.gitignore b/.gitignore index 6439d473c..ad2c2fb9e 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,5 @@ config\.json *.csv *.ods + +temp_config\.json diff --git a/config/cst.py b/config/cst.py index 3f750ad35..84c9ae333 100644 --- a/config/cst.py +++ b/config/cst.py @@ -68,6 +68,7 @@ CONFIG_PORTFOLIO_TOTAL = "total" # Notification +CONFIG_NOTIFICATION_TYPE = "notification_type" CONFIG_NOTIFICATION_INSTANCE = "notifier" CONFIG_CATEGORY_NOTIFICATION = "notification" CONFIG_NOTIFICATION_GLOBAL_INFO = "global_info" @@ -224,6 +225,11 @@ STRATEGIES_REQUIRED_EVALUATORS = "required_evaluators" TRADING_MODE_REQUIRED_STRATEGIES = "required_strategies" +# Web interface +UPDATED_CONFIG_SEPARATOR = "_" +GLOBAL_CONFIG_KEY = "global_config" +EVALUATOR_CONFIG_KEY = "evaluator_config" + class TentacleManagerActions(Enum): INSTALL = 1 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1d25e2330..a78d515b3 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,7 @@ Changelog for 0.1.5_2-beta **Warning** : - You have to encrypt your exchange token : **please run python tools/temp_encrypt_tool.py** +- Notification type key changed from "type" to "notification_type" # Concerned issues : #269 [Tool] Implement ConfigManager diff --git a/interfaces/web/controllers/configuration.py b/interfaces/web/controllers/configuration.py index 7c7e198d7..c4261d862 100644 --- a/interfaces/web/controllers/configuration.py +++ b/interfaces/web/controllers/configuration.py @@ -2,12 +2,12 @@ from flask import render_template, request, jsonify from config.cst import CONFIG_EXCHANGES, CONFIG_CATEGORY_SERVICES, CONFIG_CATEGORY_NOTIFICATION, \ - CONFIG_TRADER, CONFIG_SIMULATOR, CONFIG_CRYPTO_CURRENCIES + CONFIG_TRADER, CONFIG_SIMULATOR, CONFIG_CRYPTO_CURRENCIES, GLOBAL_CONFIG_KEY, EVALUATOR_CONFIG_KEY from interfaces import get_bot from interfaces.web import server_instance from interfaces.web.models.configuration import get_evaluator_config, update_evaluator_config, \ - get_evaluator_startup_config, get_services_list, get_symbol_list + get_evaluator_startup_config, get_services_list, get_symbol_list, update_global_config, get_global_config from interfaces.web.util.flask_util import get_rest_reply @@ -15,18 +15,26 @@ @server_instance.route('/config', methods=['GET', 'POST']) def config(): if request.method == 'POST': - update_type = request.args["update_type"] - if update_type == "evaluator_config": - request_data = request.get_json() - success = False - - if request_data: - success = update_evaluator_config(request_data) - - if success: - return get_rest_reply(jsonify(get_evaluator_config())) - else: - return get_rest_reply('{"update": "ko"}', 500) + request_data = request.get_json() + success = False + + if request_data: + # update global config if required + if GLOBAL_CONFIG_KEY in request_data and request_data[GLOBAL_CONFIG_KEY]: + success = update_global_config(request_data[GLOBAL_CONFIG_KEY]) + + # update evaluator config if required + if EVALUATOR_CONFIG_KEY in request_data and request_data[EVALUATOR_CONFIG_KEY]: + success = update_evaluator_config(request_data[EVALUATOR_CONFIG_KEY]) + + if success: + # return get_rest_reply(jsonify({ + # GLOBAL_CONFIG_KEY: get_global_config(), + # EVALUATOR_CONFIG_KEY: get_evaluator_config() + # })) + return get_rest_reply("") + else: + return get_rest_reply('{"update": "ko"}', 500) else: g_config = get_bot().get_config() user_exchanges = [e for e in g_config[CONFIG_EXCHANGES]] diff --git a/interfaces/web/models/configuration.py b/interfaces/web/models/configuration.py index 53ce4d517..0103b8a00 100644 --- a/interfaces/web/models/configuration.py +++ b/interfaces/web/models/configuration.py @@ -6,6 +6,14 @@ from tools.config_manager import ConfigManager +def get_global_config(): + return get_bot().get_config() + + +def get_global_startup_config(): + return get_bot().get_startup_config() + + def get_evaluator_config(): return get_bot().get_config()[CONFIG_EVALUATOR] @@ -15,7 +23,7 @@ def get_evaluator_startup_config(): def update_evaluator_config(new_config): - current_config = get_bot().get_config()[CONFIG_EVALUATOR] + current_config = get_evaluator_config() try: ConfigManager.update_evaluator_config(new_config, current_config) return True @@ -23,6 +31,15 @@ def update_evaluator_config(new_config): return False +def update_global_config(new_config): + current_config = get_global_config() + # try: + ConfigManager.update_global_config(new_config, current_config) + return True + # except Exception: + # return False + + def get_services_list(): return [ service().get_type() diff --git a/interfaces/web/static/js/common/cst.js b/interfaces/web/static/js/common/cst.js index f74ac5153..541738e1e 100644 --- a/interfaces/web/static/js/common/cst.js +++ b/interfaces/web/static/js/common/cst.js @@ -4,7 +4,7 @@ var config_value_attr = "config-value"; var current_value_attr = "current-value"; var startup_value_attr = "startup-config-value"; var update_url_attr = "update-url"; -var config_type_attr = "config_type"; +var config_type_attr = "config-type"; var config_root_class = "config-root"; var config_container_class = "config-container"; var config_element_class = "config-element"; @@ -27,8 +27,4 @@ var deactivated = "Deactivated"; // utility functions function log(text){ window.console&&console.log(text); -} - -function replace_eol_by_html(str){ - return str.replace(/(?:\r\n|\r|\n)/g, '
'); } \ No newline at end of file diff --git a/interfaces/web/static/js/common/util.js b/interfaces/web/static/js/common/util.js index cce9d51da..011807e14 100644 --- a/interfaces/web/static/js/common/util.js +++ b/interfaces/web/static/js/common/util.js @@ -6,4 +6,12 @@ function handle_editable(){ $(".editable").each(function(){ $(this).editable(); }); +} + +function replace_break_line(str, replacement=""){ + return str.replace(/(?:\r\n|\r|\n)/g, replacement); +} + +function replace_spaces(str, replacement=""){ + return str.replace(/ /g, replacement); } \ No newline at end of file diff --git a/interfaces/web/static/js/components/configuration.js b/interfaces/web/static/js/components/configuration.js index 2480f4a68..cf0262daf 100644 --- a/interfaces/web/static/js/components/configuration.js +++ b/interfaces/web/static/js/components/configuration.js @@ -15,9 +15,21 @@ function handle_save_buttons(){ var update_url = full_config.attr(update_url_attr); full_config.find("."+config_element_class).each(function(){ - var new_value = $(this).attr(current_value_attr) + var new_value = ""; + var config_type = $(this).attr(config_type_attr); + + if(!(config_type in updated_config)){ + updated_config[config_type] = {}; + } + + if($(this)[0].hasAttribute(current_value_attr)){ + new_value = $(this).attr(current_value_attr); + }else{ + new_value = replace_spaces(replace_break_line($(this).text())); + } + if(new_value.toLowerCase() != $(this).attr(config_value_attr).toLowerCase() ){ - updated_config[$(this).attr(config_key_attr)]=new_value; + updated_config[config_type][$(this).attr(config_key_attr)] = new_value; } }) @@ -62,6 +74,9 @@ function handle_evaluator_configuration_editor(){ }); } +function handle_global_configuration_editor(){ +} + function reset_configuration_element(element){ var full_config = get_active_tab_config(); full_config.find("."+ config_element_class).each(function(){ diff --git a/interfaces/web/templates/components/config/currency_card.html b/interfaces/web/templates/components/config/currency_card.html index 3777c13ce..e3fac5fbc 100644 --- a/interfaces/web/templates/components/config/currency_card.html +++ b/interfaces/web/templates/components/config/currency_card.html @@ -11,8 +11,7 @@
-

{{crypto_currency}}

+

{{crypto_currency}}

{% if crypto_currency in config_symbols %} @@ -22,7 +21,6 @@

-

diff --git a/interfaces/web/templates/components/config/evaluator_card.html b/interfaces/web/templates/components/config/evaluator_card.html index 219b6f0dc..2d1dc1ec7 100644 --- a/interfaces/web/templates/components/config/evaluator_card.html +++ b/interfaces/web/templates/components/config/evaluator_card.html @@ -1,5 +1,5 @@ {% macro config_evaluator_card(evaluator_startup_config, evaluator_name, activated) -%} - {{ evaluator_name }} + {{ evaluator_name }} {{('Activation pending restart' if activated else 'Deactivation pending restart') if evaluator_startup_config[evaluator_name] != activated else ('Activated' if activated else 'Deactivated')}} diff --git a/interfaces/web/templates/components/config/exchange_card.html b/interfaces/web/templates/components/config/exchange_card.html index 65c70285c..06649e4d6 100644 --- a/interfaces/web/templates/components/config/exchange_card.html +++ b/interfaces/web/templates/components/config/exchange_card.html @@ -1,4 +1,4 @@ -{% macro config_exchange_card(exchange, add_class='') -%} +{% macro config_exchange_card(config, exchange, add_class='') -%}
@@ -11,26 +11,42 @@
-

{{exchange}}

+

{{exchange}}

- API Key : *********
- API Secret : ************
+ API Key : + *********
+ + API Secret : + ************

Websocket : - {% if "web_socket" in exchange and exchange["web_socket"] == True %} - - {% else %} - - {% endif %} + {% if "web_socket" in exchange and exchange["web_socket"] == True %} + + {% endif %} +

-
diff --git a/interfaces/web/templates/components/config/service_card.html b/interfaces/web/templates/components/config/service_card.html index b1ff9101b..b319679bd 100644 --- a/interfaces/web/templates/components/config/service_card.html +++ b/interfaces/web/templates/components/config/service_card.html @@ -11,8 +11,7 @@
-

{{service}}

+

{{service}}

UserName : *********

-
diff --git a/interfaces/web/templates/config.html b/interfaces/web/templates/config.html index f008f6e56..0c0432079 100644 --- a/interfaces/web/templates/config.html +++ b/interfaces/web/templates/config.html @@ -78,9 +78,9 @@

This page is currently in development

-
+
{% for exchange in config_exchanges %} - {{ m_config_exchange_card.config_exchange_card(exchange) }} + {{ m_config_exchange_card.config_exchange_card(config_exchanges, exchange) }} {% endfor %}
@@ -88,7 +88,7 @@

This page is currently in development


-
+
{% for evaluator_name, activated in get_evaluator_config().items() %} {{ m_config_evaluator_card.config_evaluator_card(get_evaluator_startup_config(), evaluator_name, activated) }} {% endfor %} @@ -99,7 +99,7 @@

This page is currently in development


-
+
{% for trader in ["Trader", "Trader Simulator"] %} {{ m_config_trader_card.config_trader_card(trader) }} {% endfor %} @@ -109,7 +109,7 @@

This page is currently in development


-
+
@@ -158,7 +158,7 @@

Notification type

-
+
{% for service in services_list %} {{ m_config_service_card.config_service_card(service) }} {% endfor %} @@ -177,7 +177,7 @@

Notification type

-
+
{% for crypto_currency in config_symbols %} {{ m_config_currency_card.config_currency_card(config_symbols, crypto_currency) }} {% endfor %} @@ -189,7 +189,7 @@

Notification type

- {{ m_config_exchange_card.config_exchange_card(exchange=config_default_value, add_class=modified_class) }} + {{ m_config_exchange_card.config_exchange_card(config=config_exchanges, exchange=config_default_value, add_class=modified_class) }}
@@ -214,7 +214,9 @@

Notification type

handle_reset_buttons(); handle_save_buttons(); + handle_evaluator_configuration_editor(); + handle_global_configuration_editor(); $('#save-config').prop('disabled', true); $('#reset-config').prop('disabled', true); @@ -239,11 +241,5 @@

Notification type

$(this).remove(); }); }); - -// Card deck editing -$(".card").on("click", ".edit-btn", function() { - $() -}); - {% endblock additional_scripts %} \ No newline at end of file diff --git a/tools/config_manager.py b/tools/config_manager.py index dab8df1c1..8904c3ed6 100644 --- a/tools/config_manager.py +++ b/tools/config_manager.py @@ -2,9 +2,12 @@ import logging import os import shutil +from copy import copy from config.config import load_config -from config.cst import CONFIG_DEBUG_OPTION, CONFIG_EVALUATOR_FILE_PATH +from config.cst import CONFIG_DEBUG_OPTION, CONFIG_EVALUATOR_FILE_PATH, UPDATED_CONFIG_SEPARATOR, CONFIG_FILE, \ + TEMP_CONFIG_FILE, CONFIG_NOTIFICATION_INSTANCE, CONFIG_EVALUATOR, CONFIG_INTERFACES, CONFIG_ADVANCED_CLASSES, \ + CONFIG_ADVANCED_INSTANCES, CONFIG_TIME_FRAME, CONFIG_SERVICE_INSTANCE, CONFIG_CATEGORY_SERVICES class ConfigManager: @@ -19,7 +22,7 @@ def save_config(config_file, config, temp_config_file, json_data=None): if json_data is not None: cg_file.write(json_data) else: - cg_file.write(json.dumps(config)) + cg_file.write(ConfigManager.jsonify_config(config)) # check if the new config file is correct ConfigManager.check_config(config_file) @@ -44,6 +47,22 @@ def restore_config(new_config_file, old_config_file, remove_old_file=True, resto except OSError: pass + @staticmethod + def jsonify_config(config): + # remove service instances + for service in config[CONFIG_CATEGORY_SERVICES][0]: + config[CONFIG_CATEGORY_SERVICES][0][service].pop(CONFIG_SERVICE_INSTANCE, None) + + # remove non config keys + config.pop(CONFIG_EVALUATOR, None) + config.pop(CONFIG_INTERFACES, None) + config.pop(CONFIG_ADVANCED_CLASSES, None) + config.pop(CONFIG_TIME_FRAME, None) + config.pop(CONFIG_NOTIFICATION_INSTANCE, None) + config.pop(CONFIG_ADVANCED_INSTANCES, None) + + return json.dumps(config) + @staticmethod def check_config(config_file): try: @@ -72,3 +91,41 @@ def update_evaluator_config(to_update_data, current_config): if something_changed: with open(CONFIG_EVALUATOR_FILE_PATH, "w+") as evaluator_config_file_w: evaluator_config_file_w.write(json.dumps(current_config, indent=4, sort_keys=True)) + + @staticmethod + def update_global_config(to_update_data, current_config): + new_current_config = copy(current_config) + updated_configs = [ + ConfigManager.parse_and_update(data_key, data_value) + for data_key, data_value in to_update_data.items() + ] + + # merge configs + for new_config in updated_configs: + new_current_config = ConfigManager.merge_dictionaries_by_appending_keys(new_current_config, new_config) + + # save config + ConfigManager.save_config(CONFIG_FILE, new_current_config, TEMP_CONFIG_FILE) + + @staticmethod + def parse_and_update(key, new_data): + parsed_data_array = key.split(UPDATED_CONFIG_SEPARATOR) + new_config = {} + current_dict = new_config + + for i in range(len(parsed_data_array)): + if i > 0: + if i == len(parsed_data_array) - 1: + current_dict[parsed_data_array[i]] = new_data + else: + current_dict[parsed_data_array[i]] = {} + else: + new_config[parsed_data_array[i]] = {} + + current_dict = current_dict[parsed_data_array[i]] + + return new_config + + @staticmethod + def merge_dictionaries_by_appending_keys(dict_src, dict_dest): + return dict((k, [v] + ([dict_dest[k]] if k in dict_dest else [])) for (k, v) in dict_src.items()) diff --git a/tools/notifications.py b/tools/notifications.py index 6a90230d1..9047c4aaa 100644 --- a/tools/notifications.py +++ b/tools/notifications.py @@ -5,7 +5,7 @@ from config.cst import CONFIG_CATEGORY_NOTIFICATION, CONFIG_CATEGORY_SERVICES, CONFIG_GMAIL, \ CONFIG_SERVICE_INSTANCE, CONFIG_TWITTER, CONFIG_TELEGRAM, CONFIG_NOTIFICATION_PRICE_ALERTS, \ - CONFIG_NOTIFICATION_TRADES, CONFIG_ENABLED_OPTION, CONFIG_WEB + CONFIG_NOTIFICATION_TRADES, CONFIG_ENABLED_OPTION, CONFIG_WEB, CONFIG_NOTIFICATION_TYPE from interfaces.web import add_notification from services import TwitterService, TelegramService, WebService from services.gmail_service import GmailService @@ -19,7 +19,7 @@ class Notification: def __init__(self, config): self.config = config self.logger = logging.getLogger(self.__class__.__name__) - self.notification_type = self.config[CONFIG_CATEGORY_NOTIFICATION]["type"] + self.notification_type = self.config[CONFIG_CATEGORY_NOTIFICATION][CONFIG_NOTIFICATION_TYPE] self._enable = self.config[CONFIG_CATEGORY_NOTIFICATION] # return True if key is enabled