From 71bb703bc9780ceb6c3392df07d30ad8b885bdee Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Fri, 14 May 2021 19:25:18 +0200 Subject: [PATCH 01/29] [WebInterface] add strategy optimizer info --- .../advanced_templates/advanced_strategy_optimizer.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Services/Interfaces/web_interface/advanced_templates/advanced_strategy_optimizer.html b/Services/Interfaces/web_interface/advanced_templates/advanced_strategy_optimizer.html index 69f260c72..55350d05e 100644 --- a/Services/Interfaces/web_interface/advanced_templates/advanced_strategy_optimizer.html +++ b/Services/Interfaces/web_interface/advanced_templates/advanced_strategy_optimizer.html @@ -11,6 +11,13 @@

Strategy optimizer

+
+ {% if not profile.read_only %} + {% endif %}
From 869d74647a2424d5a26095c841f031f266659e10 Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Mon, 24 May 2021 21:10:18 +0200 Subject: [PATCH 08/29] [WebInterface] fix "read-only" badge click --- .../static/js/components/profile_management.js | 13 ++++++++++--- .../Interfaces/web_interface/templates/profile.html | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Services/Interfaces/web_interface/static/js/components/profile_management.js b/Services/Interfaces/web_interface/static/js/components/profile_management.js index 882c6d78b..afcbd1f31 100644 --- a/Services/Interfaces/web_interface/static/js/components/profile_management.js +++ b/Services/Interfaces/web_interface/static/js/components/profile_management.js @@ -92,12 +92,19 @@ function handleProfileExporter(){ trigger_file_downloader_on_click($(".export-profile-button")); } +function selectCurrentProfile(profileNameDisplay){ + $("#profilesSubmenu").collapse("show"); + const profileId = profileNameDisplay.attr("data-profile-id"); + activate_tab($(`#profile-${profileId}-tab`)); +} + function handleProfileSelector(){ const profileNameDisplay = $("a[data-role=current-profile-selector]"); profileNameDisplay.click(function (){ - $("#profilesSubmenu").collapse("show"); - const profileId = profileNameDisplay.attr("data-profile-id"); - activate_tab($(`#profile-${profileId}-tab`)); + selectCurrentProfile(profileNameDisplay); + }); + $("[data-role=current-profile-selector]").click(function (){ + selectCurrentProfile(profileNameDisplay); }); } diff --git a/Services/Interfaces/web_interface/templates/profile.html b/Services/Interfaces/web_interface/templates/profile.html index 4ed135fff..3a5900546 100644 --- a/Services/Interfaces/web_interface/templates/profile.html +++ b/Services/Interfaces/web_interface/templates/profile.html @@ -170,7 +170,7 @@

This trading mode doesn't need any strategy.

Profile strategy configuration - read only + read only From fedf6a1cb77f53ea4dfbdb2ae4c14fde2809d6a1 Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Mon, 24 May 2021 20:36:01 +0200 Subject: [PATCH 09/29] [Profile] add grid trading --- profiles/grid_trading/profile.json | 43 +++++++++++++++ .../specific_config/GridTradingMode.json | 53 +++++++++++++++++++ profiles/grid_trading/tentacles_config.json | 7 +++ 3 files changed, 103 insertions(+) create mode 100644 profiles/grid_trading/profile.json create mode 100644 profiles/grid_trading/specific_config/GridTradingMode.json create mode 100644 profiles/grid_trading/tentacles_config.json diff --git a/profiles/grid_trading/profile.json b/profiles/grid_trading/profile.json new file mode 100644 index 000000000..a66bc939c --- /dev/null +++ b/profiles/grid_trading/profile.json @@ -0,0 +1,43 @@ +{ + "config": { + "crypto-currencies": { + "Bitcoin": { + "enabled": true, + "pairs": [ + "BTC/USDT" + ] + } + }, + "exchanges": { + "binance": { + "enabled": true + } + }, + "trader": { + "enabled": false, + "load-trade-history": false + }, + "trader-simulator": { + "enabled": true, + "fees": { + "maker": 0.1, + "taker": 0.1 + }, + "starting-portfolio": { + "BTC": 10, + "USDT": 1000 + } + }, + "trading": { + "reference-market": "USDT", + "risk": 0.5 + } + }, + "profile": { + "avatar": "default_profile.png", + "description": "GridTrading is a profile configured to create a pre-defined amount of buy and sell orders at fixed intervals to profit from any market move. When an order is filled, a mirror order is instantly created and generates profit when completed. GridTrading is a simpler version of the StaggeredOrdersTradingMode.", + "id": "grid_trading", + "name": "Grid Trading", + "read_only": true + } +} \ No newline at end of file diff --git a/profiles/grid_trading/specific_config/GridTradingMode.json b/profiles/grid_trading/specific_config/GridTradingMode.json new file mode 100644 index 000000000..9c7aabaeb --- /dev/null +++ b/profiles/grid_trading/specific_config/GridTradingMode.json @@ -0,0 +1,53 @@ +{ + "required_strategies": [], + "pair_settings": [ + { + "pair": "BTC/USDT", + "flat_spread": 2000, + "flat_increment": 1000, + "buy_orders_count": 25, + "sell_orders_count": 25, + "sell_funds": 0, + "buy_funds": 0, + "starting_price": 0, + "buy_volume_per_order": 0, + "sell_volume_per_order": 0, + "reinvest_profits": false, + "use_fixed_volume_for_mirror_orders": false, + "mirror_order_delay": 0, + "use_existing_orders_only": false + }, + { + "pair": "ADA/ETH", + "flat_spread": 0.00002, + "flat_increment": 0.00001, + "buy_orders_count": 25, + "sell_orders_count": 25, + "sell_funds": 0, + "buy_funds": 0, + "starting_price": 0, + "buy_volume_per_order": 0, + "sell_volume_per_order": 0, + "reinvest_profits": false, + "use_fixed_volume_for_mirror_orders": false, + "mirror_order_delay": 0, + "use_existing_orders_only": false + }, + { + "pair": "ETH/USDT", + "flat_spread": 10, + "flat_increment": 5, + "buy_orders_count": 25, + "sell_orders_count": 25, + "sell_funds": 0, + "buy_funds": 0, + "starting_price": 0, + "buy_volume_per_order": 0, + "sell_volume_per_order": 0, + "reinvest_profits": false, + "use_fixed_volume_for_mirror_orders": false, + "mirror_order_delay": 0, + "use_existing_orders_only": false + } + ] +} \ No newline at end of file diff --git a/profiles/grid_trading/tentacles_config.json b/profiles/grid_trading/tentacles_config.json new file mode 100644 index 000000000..cb8f8cef6 --- /dev/null +++ b/profiles/grid_trading/tentacles_config.json @@ -0,0 +1,7 @@ +{ + "tentacle_activation": { + "Trading": { + "GridTradingMode": true + } + } +} \ No newline at end of file From c4370ab12f0d41011e9787517811fbc56ca1a6e0 Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Mon, 24 May 2021 20:37:31 +0200 Subject: [PATCH 10/29] [Profile] add staggered orders trading --- .../staggered_orders_trading/profile.json | 43 ++++++++++++++++++ .../StaggeredOrdersTradingMode.json | 44 +++++++++++++++++++ .../tentacles_config.json | 7 +++ 3 files changed, 94 insertions(+) create mode 100644 profiles/staggered_orders_trading/profile.json create mode 100644 profiles/staggered_orders_trading/specific_config/StaggeredOrdersTradingMode.json create mode 100644 profiles/staggered_orders_trading/tentacles_config.json diff --git a/profiles/staggered_orders_trading/profile.json b/profiles/staggered_orders_trading/profile.json new file mode 100644 index 000000000..241e958e5 --- /dev/null +++ b/profiles/staggered_orders_trading/profile.json @@ -0,0 +1,43 @@ +{ + "config": { + "crypto-currencies": { + "Bitcoin": { + "enabled": true, + "pairs": [ + "BTC/USDT" + ] + } + }, + "exchanges": { + "binance": { + "enabled": true + } + }, + "trader": { + "enabled": false, + "load-trade-history": false + }, + "trader-simulator": { + "enabled": true, + "fees": { + "maker": 0.1, + "taker": 0.1 + }, + "starting-portfolio": { + "BTC": 10, + "USDT": 1000 + } + }, + "trading": { + "reference-market": "USDT", + "risk": 0.5 + } + }, + "profile": { + "avatar": "default_profile.png", + "description": "StaggeredOrdersTrading is a profile configured to create a buy and sell orders at fixed intervals on a specific price range to profit from any market move. When an order is filled, a mirror order is instantly created and generates profit when completed.", + "id": "staggered_orders_trading", + "name": "Staggered Orders Trading", + "read_only": true + } +} \ No newline at end of file diff --git a/profiles/staggered_orders_trading/specific_config/StaggeredOrdersTradingMode.json b/profiles/staggered_orders_trading/specific_config/StaggeredOrdersTradingMode.json new file mode 100644 index 000000000..6764c6733 --- /dev/null +++ b/profiles/staggered_orders_trading/specific_config/StaggeredOrdersTradingMode.json @@ -0,0 +1,44 @@ +{ + "required_strategies": [], + "pair_settings": [ + { + "pair": "BTC/USDT", + "mode": "mountain", + "spread_percent": 6, + "increment_percent": 3, + "lower_bound": 30000, + "upper_bound": 60000, + "allow_instant_fill": true, + "operational_depth": 100, + "mirror_order_delay": 0, + "reinvest_profits": false, + "use_existing_orders_only": false + }, + { + "pair": "ADA/ETH", + "mode": "mountain", + "spread_percent": 6, + "increment_percent": 3, + "lower_bound": 0.0003, + "upper_bound": 0.0007, + "allow_instant_fill": true, + "operational_depth": 50, + "mirror_order_delay": 0, + "reinvest_profits": false, + "use_existing_orders_only": false + }, + { + "pair": "ETH/USDT", + "mode": "mountain", + "spread_percent": 0.7, + "increment_percent": 0.3, + "lower_bound": 400, + "upper_bound": 500, + "allow_instant_fill": true, + "operational_depth": 50, + "mirror_order_delay": 0, + "reinvest_profits": false, + "use_existing_orders_only": false + } + ] +} \ No newline at end of file diff --git a/profiles/staggered_orders_trading/tentacles_config.json b/profiles/staggered_orders_trading/tentacles_config.json new file mode 100644 index 000000000..4b08736a3 --- /dev/null +++ b/profiles/staggered_orders_trading/tentacles_config.json @@ -0,0 +1,7 @@ +{ + "tentacle_activation": { + "Trading": { + "StaggeredOrdersTradingMode": true + } + } +} \ No newline at end of file From bb31d44bec79fa004ba99340a7630e219c522de0 Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Tue, 4 May 2021 00:00:53 +0200 Subject: [PATCH 11/29] [WebInterface] basis of community tentacles --- .../web_interface/controllers/__init__.py | 10 +- .../web_interface/controllers/community.py | 8 +- .../controllers/community_authentication.py | 1 + .../controllers/community_tentacles.py | 47 +++++++++ .../web_interface/models/__init__.py | 2 + .../web_interface/models/community.py | 5 + .../web_interface/static/css/style.css | 7 ++ .../js/components/community_tentacles.js | 3 + .../web_interface/templates/about.html | 2 +- .../web_interface/templates/community.html | 95 +++++++------------ .../templates/community_login.html | 2 +- .../templates/community_metrics.html | 74 +++++++++++++++ .../templates/community_tentacles.html | 38 ++++++++ .../tentacles_package_card.html | 24 +++++ 14 files changed, 254 insertions(+), 64 deletions(-) create mode 100644 Services/Interfaces/web_interface/controllers/community_tentacles.py create mode 100644 Services/Interfaces/web_interface/static/js/components/community_tentacles.js create mode 100644 Services/Interfaces/web_interface/templates/community_metrics.html create mode 100644 Services/Interfaces/web_interface/templates/community_tentacles.html create mode 100644 Services/Interfaces/web_interface/templates/components/tentacles_packages/tentacles_package_card.html diff --git a/Services/Interfaces/web_interface/controllers/__init__.py b/Services/Interfaces/web_interface/controllers/__init__.py index 6cffe004b..a19491bfb 100644 --- a/Services/Interfaces/web_interface/controllers/__init__.py +++ b/Services/Interfaces/web_interface/controllers/__init__.py @@ -16,6 +16,7 @@ from tentacles.Services.Interfaces.web_interface.controllers import octobot_authentication from tentacles.Services.Interfaces.web_interface.controllers import community_authentication +from tentacles.Services.Interfaces.web_interface.controllers import community_tentacles from tentacles.Services.Interfaces.web_interface.controllers import backtesting from tentacles.Services.Interfaces.web_interface.controllers import commands from tentacles.Services.Interfaces.web_interface.controllers import community @@ -41,6 +42,12 @@ community_login, community_logout, ) +from tentacles.Services.Interfaces.web_interface.controllers.community import ( + community_metrics, +) +from tentacles.Services.Interfaces.web_interface.controllers.community_tentacles import ( + community_tentacles, +) from tentacles.Services.Interfaces.web_interface.controllers.backtesting import ( data_collector, ) @@ -86,7 +93,6 @@ ) - __all__ = [ "CommunityLoginForm", "community_login", @@ -99,6 +105,8 @@ "commands", "about", "community", + "community_tentacles", + "community_metrics", "profile", "profiles_management", "config", diff --git a/Services/Interfaces/web_interface/controllers/community.py b/Services/Interfaces/web_interface/controllers/community.py index 89a0839dc..07dfdb73d 100644 --- a/Services/Interfaces/web_interface/controllers/community.py +++ b/Services/Interfaces/web_interface/controllers/community.py @@ -24,9 +24,15 @@ @web_interface.server_instance.route("/community") @login.login_required_when_activated def community(): + return flask.render_template('community.html') + + +@web_interface.server_instance.route("/community_metrics") +@login.login_required_when_activated +def community_metrics(): can_get_metrics = models.can_get_community_metrics() community_metrics = models.get_community_metrics_to_display() if can_get_metrics else None - return flask.render_template('community.html', + return flask.render_template('community_metrics.html', can_get_metrics=can_get_metrics, community_metrics=community_metrics ) diff --git a/Services/Interfaces/web_interface/controllers/community_authentication.py b/Services/Interfaces/web_interface/controllers/community_authentication.py index b5398618b..eacff8961 100644 --- a/Services/Interfaces/web_interface/controllers/community_authentication.py +++ b/Services/Interfaces/web_interface/controllers/community_authentication.py @@ -42,6 +42,7 @@ def community_login(): authenticator.login(form.email.data, form.password.data) logged_in_email = form.email.data flask.flash(f"Authenticated as {form.email.data}", "success") + return flask.redirect('community_tentacles') except community.FailedAuthentication: flask.flash(f"Invalid email or password", "error") except Exception as e: diff --git a/Services/Interfaces/web_interface/controllers/community_tentacles.py b/Services/Interfaces/web_interface/controllers/community_tentacles.py new file mode 100644 index 000000000..4e5c1b2c2 --- /dev/null +++ b/Services/Interfaces/web_interface/controllers/community_tentacles.py @@ -0,0 +1,47 @@ +# Drakkar-Software OctoBot-Interfaces +# Copyright (c) Drakkar-Software, All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3.0 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. +import flask +import flask_wtf +import wtforms.fields.html5 + +import octobot.constants as constants +import octobot.community as community +import octobot_services.interfaces.util as interfaces_util +import tentacles.Services.Interfaces.web_interface as web_interface +import tentacles.Services.Interfaces.web_interface.login as login +import tentacles.Services.Interfaces.web_interface.models as models + + +@web_interface.server_instance.route('/community_tentacles') +@login.login_required_when_activated +def community_tentacles(): + authenticator = interfaces_util.get_bot_api().get_community_auth() + logged_in_email = None + try: + logged_in_email = authenticator.get_logged_in_email() + except community.AuthenticationRequired: + pass + except Exception as e: + flask.flash(f"Error when contacting the community server: {e}", "error") + if logged_in_email is None: + return flask.redirect('community_login') + # TODO + default_image = f"{constants.OCTOBOT_COMMUNITY_URL}/assets/meganav/promo_banner_left-first-category-1e19fc784f709ed0ebbd047064cf872195c2d33da29996b7c7cf469f858a8a71.jpg" + return flask.render_template('community_tentacles.html', + current_logged_in_email=logged_in_email, + tentacles_packages=models.get_account_tentacles_packages(authenticator), + community_url=constants.OCTOBOT_COMMUNITY_URL, + default_tentacles_package_image=default_image) diff --git a/Services/Interfaces/web_interface/models/__init__.py b/Services/Interfaces/web_interface/models/__init__.py index f8c7f0710..91781184a 100644 --- a/Services/Interfaces/web_interface/models/__init__.py +++ b/Services/Interfaces/web_interface/models/__init__.py @@ -47,6 +47,7 @@ from tentacles.Services.Interfaces.web_interface.models.community import ( get_community_metrics_to_display, can_get_community_metrics, + get_account_tentacles_packages, ) from tentacles.Services.Interfaces.web_interface.models.configuration import ( get_evaluators_tentacles_startup_activation, @@ -160,6 +161,7 @@ "update_bot", "get_community_metrics_to_display", "can_get_community_metrics", + "get_account_tentacles_packages", "get_evaluators_tentacles_startup_activation", "get_trading_tentacles_startup_activation", "get_tentacle_documentation", diff --git a/Services/Interfaces/web_interface/models/community.py b/Services/Interfaces/web_interface/models/community.py index 0c5b6f80a..f494ada8c 100644 --- a/Services/Interfaces/web_interface/models/community.py +++ b/Services/Interfaces/web_interface/models/community.py @@ -24,3 +24,8 @@ def get_community_metrics_to_display(): def can_get_community_metrics(): return octobot_community.can_read_metrics(interfaces_util.get_edited_config(dict_only=False)) + + +def get_account_tentacles_packages(authenticator): + packages = authenticator.get_packages() + return [octobot_community.CommunityTentaclesPackage.from_community_dict(data) for data in packages] diff --git a/Services/Interfaces/web_interface/static/css/style.css b/Services/Interfaces/web_interface/static/css/style.css index 5df0ca400..5ee0b4d39 100644 --- a/Services/Interfaces/web_interface/static/css/style.css +++ b/Services/Interfaces/web_interface/static/css/style.css @@ -211,6 +211,13 @@ a.profile-overview-values { flex-basis: 45% !important; } +.community-card { + //TODO + align-items: stretch; + align-self: stretch; + +} + .medium-size { max-width: 18rem; min-width: 12rem; diff --git a/Services/Interfaces/web_interface/static/js/components/community_tentacles.js b/Services/Interfaces/web_interface/static/js/components/community_tentacles.js new file mode 100644 index 000000000..a8013d6df --- /dev/null +++ b/Services/Interfaces/web_interface/static/js/components/community_tentacles.js @@ -0,0 +1,3 @@ +$(document).ready(function() { + log(communityURL); +}); diff --git a/Services/Interfaces/web_interface/templates/about.html b/Services/Interfaces/web_interface/templates/about.html index 102695ef8..8eadd8a57 100644 --- a/Services/Interfaces/web_interface/templates/about.html +++ b/Services/Interfaces/web_interface/templates/about.html @@ -39,7 +39,7 @@

Help us to improve OctoBot

anonymous data to help the OctoBot Community -

This will grant you access to the OctoBot Community and greatly help the OctoBot team to figure out the best ways to improve OctoBot. +

This will grant you access to the OctoBot Community metrics and greatly help the OctoBot team to figure out the best ways to improve OctoBot. -

{{ table_name }}

-
- - - - - - - - - - {% for item in items %} - {{ m_tables.top_tr(item) }} - {% endfor %} - -
#{{ item_name }}OctoBots
-
-
-{%- endmacro %} - -{% macro col_elem_with_badge(title, badge, badge_color="unique-color-dark") -%} -
- {{ title }} -
-
- {{ badge }} -
-{%- endmacro %} +{% from "macros/forms.html" import render_field %} {% block body %}
-{% if can_get_metrics %} -
-
-

- OctoBot Community - Community chat -

+
+ -
-
-

Active OctoBots

-

-
- {{ col_elem_with_badge("Total:", community_metrics['total_count']) }} - {{ col_elem_with_badge("This month:", community_metrics['this_month'], badge_color="unique-color") }} - {{ col_elem_with_badge("Last 6 months:", community_metrics['last_six_month']) }} -
-

-
-
-
- {{ top_table_card(community_metrics['top_pairs'], "Pair", "Top community traded pairs") }} - {{ top_table_card(community_metrics['top_strategies'], "Tentacle", "Top community tentacles") }} - {{ top_table_card(community_metrics['top_exchanges'], "Exchange", "Top community exchanges") }} -
-{% else %} - +
{% endblock %} {% block additional_scripts %} - {% endblock additional_scripts %} \ No newline at end of file diff --git a/Services/Interfaces/web_interface/templates/community_login.html b/Services/Interfaces/web_interface/templates/community_login.html index 72d9f2b1c..c57849c76 100644 --- a/Services/Interfaces/web_interface/templates/community_login.html +++ b/Services/Interfaces/web_interface/templates/community_login.html @@ -1,5 +1,5 @@ {% extends "layout.html" %} -{% set active_page = "home" %} +{% set active_page = "community" %} {% from "macros/forms.html" import render_field %} diff --git a/Services/Interfaces/web_interface/templates/community_metrics.html b/Services/Interfaces/web_interface/templates/community_metrics.html new file mode 100644 index 000000000..554c9a846 --- /dev/null +++ b/Services/Interfaces/web_interface/templates/community_metrics.html @@ -0,0 +1,74 @@ +{% extends "layout.html" %} +{% set active_page = "community" %} +{% import 'macros/tables.html' as m_tables %} + +{% macro top_table_card(items, item_name, table_name) -%} +
+

{{ table_name }}

+
+ + + + + + + + + + {% for item in items %} + {{ m_tables.top_tr(item) }} + {% endfor %} + +
#{{ item_name }}OctoBots
+
+
+{%- endmacro %} + +{% macro col_elem_with_badge(title, badge, badge_color="unique-color-dark") -%} +
+ {{ title }} +
+
+ {{ badge }} +
+{%- endmacro %} + +{% block body %} +
+{% if can_get_metrics %} +
+
+

+ OctoBot Community metrics + Community chat +

+
+
+
+
+

Active OctoBots

+

+
+ {{ col_elem_with_badge("Total:", community_metrics['total_count']) }} + {{ col_elem_with_badge("This month:", community_metrics['this_month'], badge_color="unique-color") }} + {{ col_elem_with_badge("Last 6 months:", community_metrics['last_six_month']) }} +
+

+
+
+
+ {{ top_table_card(community_metrics['top_pairs'], "Pair", "Top community traded pairs") }} + {{ top_table_card(community_metrics['top_strategies'], "Tentacle", "Top community tentacles") }} + {{ top_table_card(community_metrics['top_exchanges'], "Exchange", "Top community exchanges") }} +
+{% else %} +
+

To be part of the OctoBot community, please enable OctoBot community data.

+
+{% endif %} +
+{% endblock %} + +{% block additional_scripts %} + +{% endblock additional_scripts %} \ No newline at end of file diff --git a/Services/Interfaces/web_interface/templates/community_tentacles.html b/Services/Interfaces/web_interface/templates/community_tentacles.html new file mode 100644 index 000000000..a3ba1bb25 --- /dev/null +++ b/Services/Interfaces/web_interface/templates/community_tentacles.html @@ -0,0 +1,38 @@ +{% extends "layout.html" %} +{% set active_page = "community" %} + +{% import "components/tentacles_packages/tentacles_package_card.html" as tentacles_package_card %} + +{% block body %} +
+
+
+

+ OctoBot community Tentacles +

+
+
+

+ Logged as {{current_logged_in_email}} + Logout +

+
+ {% for tentacles_package in tentacles_packages * 5 %} +
+ {{ tentacles_package_card.tentacles_package_card(tentacles_package, default_tentacles_package_image) }} +
+ {% endfor %} +
+
+
+ +
+{% endblock %} + +{% block additional_scripts %} + + +{% endblock additional_scripts %} \ No newline at end of file diff --git a/Services/Interfaces/web_interface/templates/components/tentacles_packages/tentacles_package_card.html b/Services/Interfaces/web_interface/templates/components/tentacles_packages/tentacles_package_card.html new file mode 100644 index 000000000..7efe3b8d2 --- /dev/null +++ b/Services/Interfaces/web_interface/templates/components/tentacles_packages/tentacles_package_card.html @@ -0,0 +1,24 @@ +{% macro tentacles_package_card(tentacles_package, default_image) -%} +
+ +
+

+ {{tentacles_package.name | replace("_", " ") | capitalize}} + {% if tentacles_package.activated %} + + Activated + + {% endif %} +

+
+ +
+{%- endmacro %} From e81935104d32a69242e7ef92510d8cbe46d86f9e Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Mon, 10 May 2021 21:44:09 +0200 Subject: [PATCH 12/29] [WebInterface] display community tentacles --- .../advanced_controllers/tentacles.py | 9 +- .../web_interface/controllers/__init__.py | 5 - .../web_interface/controllers/community.py | 22 +++- .../controllers/community_authentication.py | 4 +- .../controllers/community_tentacles.py | 47 -------- .../web_interface/models/__init__.py | 2 + .../web_interface/models/community.py | 5 +- .../web_interface/static/css/style.css | 27 +++++ .../static/js/components/community.js | 51 ++++++++- .../static/js/components/community_metrics.js | 24 ++++ .../js/components/community_tentacles.js | 3 - .../web_interface/templates/community.html | 106 +++++++++++++----- .../templates/community_login.html | 3 + .../templates/community_metrics.html | 4 +- .../templates/community_tentacles.html | 38 ------- .../components/community/bots_stats.html | 20 ++++ .../community/tentacles_package_row.html | 57 ++++++++++ 17 files changed, 298 insertions(+), 129 deletions(-) delete mode 100644 Services/Interfaces/web_interface/controllers/community_tentacles.py create mode 100644 Services/Interfaces/web_interface/static/js/components/community_metrics.js delete mode 100644 Services/Interfaces/web_interface/static/js/components/community_tentacles.js delete mode 100644 Services/Interfaces/web_interface/templates/community_tentacles.html create mode 100644 Services/Interfaces/web_interface/templates/components/community/bots_stats.html create mode 100644 Services/Interfaces/web_interface/templates/components/community/tentacles_package_row.html diff --git a/Services/Interfaces/web_interface/advanced_controllers/tentacles.py b/Services/Interfaces/web_interface/advanced_controllers/tentacles.py index e6d58b9f9..25bbf0060 100644 --- a/Services/Interfaces/web_interface/advanced_controllers/tentacles.py +++ b/Services/Interfaces/web_interface/advanced_controllers/tentacles.py @@ -34,8 +34,13 @@ def _handle_package_operation(update_type): request_data = flask.request.get_json() success = False if request_data: - path_or_url, action = next(iter(request_data.items())) - path_or_url = path_or_url.strip() + url_key = "url" + if url_key in request_data: + path_or_url = request_data[url_key] + action = "register_and_install" + else: + path_or_url, action = next(iter(request_data.items())) + path_or_url = request_data.strip() if action == "register_and_install": installation_result = models.install_packages(path_or_url) if installation_result: diff --git a/Services/Interfaces/web_interface/controllers/__init__.py b/Services/Interfaces/web_interface/controllers/__init__.py index a19491bfb..f5ed44b75 100644 --- a/Services/Interfaces/web_interface/controllers/__init__.py +++ b/Services/Interfaces/web_interface/controllers/__init__.py @@ -16,7 +16,6 @@ from tentacles.Services.Interfaces.web_interface.controllers import octobot_authentication from tentacles.Services.Interfaces.web_interface.controllers import community_authentication -from tentacles.Services.Interfaces.web_interface.controllers import community_tentacles from tentacles.Services.Interfaces.web_interface.controllers import backtesting from tentacles.Services.Interfaces.web_interface.controllers import commands from tentacles.Services.Interfaces.web_interface.controllers import community @@ -45,9 +44,6 @@ from tentacles.Services.Interfaces.web_interface.controllers.community import ( community_metrics, ) -from tentacles.Services.Interfaces.web_interface.controllers.community_tentacles import ( - community_tentacles, -) from tentacles.Services.Interfaces.web_interface.controllers.backtesting import ( data_collector, ) @@ -105,7 +101,6 @@ "commands", "about", "community", - "community_tentacles", "community_metrics", "profile", "profiles_management", diff --git a/Services/Interfaces/web_interface/controllers/community.py b/Services/Interfaces/web_interface/controllers/community.py index 07dfdb73d..f6d140ccd 100644 --- a/Services/Interfaces/web_interface/controllers/community.py +++ b/Services/Interfaces/web_interface/controllers/community.py @@ -16,6 +16,9 @@ import flask +import octobot.community +import octobot.constants as constants +import octobot_services.interfaces.util as interfaces_util import tentacles.Services.Interfaces.web_interface as web_interface import tentacles.Services.Interfaces.web_interface.login as login import tentacles.Services.Interfaces.web_interface.models as models @@ -24,7 +27,24 @@ @web_interface.server_instance.route("/community") @login.login_required_when_activated def community(): - return flask.render_template('community.html') + authenticator = interfaces_util.get_bot_api().get_community_auth() + logged_in_email = None + try: + logged_in_email = authenticator.get_logged_in_email() + except octobot.community.AuthenticationRequired: + pass + except Exception as e: + flask.flash(f"Error when contacting the community server: {e}", "error") + if logged_in_email is None: + return flask.redirect('community_login') + # TODO + default_image = f"{constants.OCTOBOT_COMMUNITY_URL}/assets/meganav/promo_banner_left-first-category-1e19fc784f709ed0ebbd047064cf872195c2d33da29996b7c7cf469f858a8a71.jpg" + return flask.render_template('community.html', + current_logged_in_email=logged_in_email, + tentacles_packages=models.get_account_tentacles_packages(authenticator), + current_bots_stats=models.get_current_octobots_stats(), + community_url=constants.OCTOBOT_COMMUNITY_URL, + default_tentacles_package_image=default_image) @web_interface.server_instance.route("/community_metrics") diff --git a/Services/Interfaces/web_interface/controllers/community_authentication.py b/Services/Interfaces/web_interface/controllers/community_authentication.py index eacff8961..f79daf59a 100644 --- a/Services/Interfaces/web_interface/controllers/community_authentication.py +++ b/Services/Interfaces/web_interface/controllers/community_authentication.py @@ -22,6 +22,7 @@ import octobot_services.interfaces.util as interfaces_util import tentacles.Services.Interfaces.web_interface as web_interface import tentacles.Services.Interfaces.web_interface.login as login +import tentacles.Services.Interfaces.web_interface.models as models @web_interface.server_instance.route('/community_login', methods=['GET', 'POST']) @@ -42,7 +43,7 @@ def community_login(): authenticator.login(form.email.data, form.password.data) logged_in_email = form.email.data flask.flash(f"Authenticated as {form.email.data}", "success") - return flask.redirect('community_tentacles') + return flask.redirect('community') except community.FailedAuthentication: flask.flash(f"Invalid email or password", "error") except Exception as e: @@ -50,6 +51,7 @@ def community_login(): return flask.render_template('community_login.html', form=form, current_logged_in_email=logged_in_email, + current_bots_stats=models.get_current_octobots_stats(), community_url=constants.OCTOBOT_COMMUNITY_URL) diff --git a/Services/Interfaces/web_interface/controllers/community_tentacles.py b/Services/Interfaces/web_interface/controllers/community_tentacles.py deleted file mode 100644 index 4e5c1b2c2..000000000 --- a/Services/Interfaces/web_interface/controllers/community_tentacles.py +++ /dev/null @@ -1,47 +0,0 @@ -# Drakkar-Software OctoBot-Interfaces -# Copyright (c) Drakkar-Software, All rights reserved. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 3.0 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. -import flask -import flask_wtf -import wtforms.fields.html5 - -import octobot.constants as constants -import octobot.community as community -import octobot_services.interfaces.util as interfaces_util -import tentacles.Services.Interfaces.web_interface as web_interface -import tentacles.Services.Interfaces.web_interface.login as login -import tentacles.Services.Interfaces.web_interface.models as models - - -@web_interface.server_instance.route('/community_tentacles') -@login.login_required_when_activated -def community_tentacles(): - authenticator = interfaces_util.get_bot_api().get_community_auth() - logged_in_email = None - try: - logged_in_email = authenticator.get_logged_in_email() - except community.AuthenticationRequired: - pass - except Exception as e: - flask.flash(f"Error when contacting the community server: {e}", "error") - if logged_in_email is None: - return flask.redirect('community_login') - # TODO - default_image = f"{constants.OCTOBOT_COMMUNITY_URL}/assets/meganav/promo_banner_left-first-category-1e19fc784f709ed0ebbd047064cf872195c2d33da29996b7c7cf469f858a8a71.jpg" - return flask.render_template('community_tentacles.html', - current_logged_in_email=logged_in_email, - tentacles_packages=models.get_account_tentacles_packages(authenticator), - community_url=constants.OCTOBOT_COMMUNITY_URL, - default_tentacles_package_image=default_image) diff --git a/Services/Interfaces/web_interface/models/__init__.py b/Services/Interfaces/web_interface/models/__init__.py index 91781184a..903588f96 100644 --- a/Services/Interfaces/web_interface/models/__init__.py +++ b/Services/Interfaces/web_interface/models/__init__.py @@ -48,6 +48,7 @@ get_community_metrics_to_display, can_get_community_metrics, get_account_tentacles_packages, + get_current_octobots_stats, ) from tentacles.Services.Interfaces.web_interface.models.configuration import ( get_evaluators_tentacles_startup_activation, @@ -162,6 +163,7 @@ "get_community_metrics_to_display", "can_get_community_metrics", "get_account_tentacles_packages", + "get_current_octobots_stats", "get_evaluators_tentacles_startup_activation", "get_trading_tentacles_startup_activation", "get_tentacle_documentation", diff --git a/Services/Interfaces/web_interface/models/community.py b/Services/Interfaces/web_interface/models/community.py index f494ada8c..36431ac38 100644 --- a/Services/Interfaces/web_interface/models/community.py +++ b/Services/Interfaces/web_interface/models/community.py @@ -13,7 +13,6 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. - import octobot_services.interfaces.util as interfaces_util import octobot.community as octobot_community @@ -29,3 +28,7 @@ def can_get_community_metrics(): def get_account_tentacles_packages(authenticator): packages = authenticator.get_packages() return [octobot_community.CommunityTentaclesPackage.from_community_dict(data) for data in packages] + + +def get_current_octobots_stats(): + return interfaces_util.run_in_bot_async_executor(octobot_community.get_current_octobots_stats()) diff --git a/Services/Interfaces/web_interface/static/css/style.css b/Services/Interfaces/web_interface/static/css/style.css index 5ee0b4d39..51e22a329 100644 --- a/Services/Interfaces/web_interface/static/css/style.css +++ b/Services/Interfaces/web_interface/static/css/style.css @@ -206,6 +206,29 @@ a.profile-overview-values { flex-basis: inherit !important; } +.community-bot-stats-label { + font-weight: bold; + font-size: large; +} + +.community-bot-stats { + font-size: large; + background-color: #C62828; +} + +.package_row_image { + max-height: 4rem; +} + +.table td.centered { + text-align: center; + vertical-align: middle; +} + +.tentacle_package_action { + font-size: 1.5rem; +} + .card, .table-card { flex-basis: 45% !important; @@ -218,6 +241,10 @@ a.profile-overview-values { } +.bg-darker { + background-color: #121212; +} + .medium-size { max-width: 18rem; min-width: 12rem; diff --git a/Services/Interfaces/web_interface/static/js/components/community.js b/Services/Interfaces/web_interface/static/js/components/community.js index 281fe82c2..92134e96c 100644 --- a/Services/Interfaces/web_interface/static/js/components/community.js +++ b/Services/Interfaces/web_interface/static/js/components/community.js @@ -16,9 +16,58 @@ * License along with this library. */ +function disablePackagesOperations(should_lock=true){ + const disabled_attr = 'disabled'; + $("#synchronize-tentacles").prop(disabled_attr, should_lock); + $(".install-package-button").prop(disabled_attr, should_lock); +} -$(document).ready(function() { +function syncPackages(source){ + const update_url = source.attr(update_url_attr); + disablePackagesOperations(); + send_and_interpret_bot_update({}, update_url, source, packagesOperationSuccessCallback, packagesOperationErrorCallback) +} + +function reloadTable(){ $('.table').each(function () { $(this).DataTable(); }); + registerPackagesEvents(); +} + +function registerPackagesEvents(){ + $(".install-package-button").click(function (){ + const element = $(this); + const update_url = element.attr(update_url_attr); + const data = { + "url": element.data("package-url") + } + disablePackagesOperations(); + send_and_interpret_bot_update(data, update_url, element, packagesOperationSuccessCallback, packagesOperationErrorCallback) + }) +} + +function reloadOwnedPackages(){ + $("#owned-tentacles").load(location.href + " #owned-tentacles", function(){ + reloadTable(); + }); +} + +function packagesOperationSuccessCallback(updated_data, update_url, dom_root_element, msg, status){ + disablePackagesOperations(false); + reloadOwnedPackages(); + create_alert("success", "Packages operation success", msg); +} + +function packagesOperationErrorCallback(updated_data, update_url, dom_root_element, result, status, error){ + disablePackagesOperations(false); + reloadOwnedPackages(); + create_alert("error", "Error when managing packages: "+result.responseText, ""); +} + +$(document).ready(function() { + reloadTable(); + $("#synchronize-tentacles").click(function(){ + syncPackages($(this)); + }); }); diff --git a/Services/Interfaces/web_interface/static/js/components/community_metrics.js b/Services/Interfaces/web_interface/static/js/components/community_metrics.js new file mode 100644 index 000000000..281fe82c2 --- /dev/null +++ b/Services/Interfaces/web_interface/static/js/components/community_metrics.js @@ -0,0 +1,24 @@ +/* + * Drakkar-Software OctoBot + * Copyright (c) Drakkar-Software, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ + + +$(document).ready(function() { + $('.table').each(function () { + $(this).DataTable(); + }); +}); diff --git a/Services/Interfaces/web_interface/static/js/components/community_tentacles.js b/Services/Interfaces/web_interface/static/js/components/community_tentacles.js deleted file mode 100644 index a8013d6df..000000000 --- a/Services/Interfaces/web_interface/static/js/components/community_tentacles.js +++ /dev/null @@ -1,3 +0,0 @@ -$(document).ready(function() { - log(communityURL); -}); diff --git a/Services/Interfaces/web_interface/templates/community.html b/Services/Interfaces/web_interface/templates/community.html index b34eb1f65..74c35953f 100644 --- a/Services/Interfaces/web_interface/templates/community.html +++ b/Services/Interfaces/web_interface/templates/community.html @@ -1,43 +1,92 @@ {% extends "layout.html" %} {% set active_page = "community" %} -{% from "macros/forms.html" import render_field %} +{% import "components/community/tentacles_package_row.html" as tentacles_package_row %} +{% import "components/community/bots_stats.html" as bots_stats %} {% block body %}
-
-
-
- -
-

- OctoBot community Tentacles -

-
-
- Community Tentacles -

- Gateway to the octobot community tentacles -

-
-
+{{ bots_stats.bots_stats_card(current_bots_stats) }} + +
+
+
+

Your community tentacles packages +

+
+
+ +
+
-
-
- +
+ +
+ {% if tentacles_packages %} + + + + + + + + + + + + {% for tentacles_package in tentacles_packages * 5 %} + {{ tentacles_package_row.tentacles_package_row(tentacles_package, default_tentacles_package_image) }} + {% endfor %} + +
+ Add more tentacles packages by activating them on the + OctoBot community website. +
PackageDescriptionStateMore
+ {% else %} +
-

- OctoBot community Metrics -

+

+ No activated tentacles package found on your OctoBot community account. +

- Community Tentacles -

- Details usage data provided by the OctoBot community -

+

+ Find community tentacles packages on the + OctoBot community website. +

+

+ Each activated tentacles package will be displayed here for you to add on your OctoBot. +

- +
+ {% endif %}
@@ -46,4 +95,5 @@

{% endblock %} {% block additional_scripts %} + {% endblock additional_scripts %} \ No newline at end of file diff --git a/Services/Interfaces/web_interface/templates/community_login.html b/Services/Interfaces/web_interface/templates/community_login.html index c57849c76..a53b7230f 100644 --- a/Services/Interfaces/web_interface/templates/community_login.html +++ b/Services/Interfaces/web_interface/templates/community_login.html @@ -1,10 +1,13 @@ {% extends "layout.html" %} {% set active_page = "community" %} +{% import "components/community/bots_stats.html" as bots_stats %} {% from "macros/forms.html" import render_field %} {% block body %}
+{{ bots_stats.bots_stats_card(current_bots_stats) }} +