From a0a7ae9be17f7769f15ed3a9569d7c2dd516b0b4 Mon Sep 17 00:00:00 2001 From: Black Coffee Date: Mon, 14 Nov 2022 12:48:15 +0000 Subject: [PATCH] Added single page stats back to Gerty --- lnbits/extensions/gerty/helpers.py | 39 + .../gerty/templates/gerty/index.html | 1398 +++++++++-------- lnbits/extensions/gerty/views_api.py | 80 +- 3 files changed, 887 insertions(+), 630 deletions(-) diff --git a/lnbits/extensions/gerty/helpers.py b/lnbits/extensions/gerty/helpers.py index ccb1bb55..4852fb58 100644 --- a/lnbits/extensions/gerty/helpers.py +++ b/lnbits/extensions/gerty/helpers.py @@ -239,3 +239,42 @@ def get_time_remaining(seconds, granularity=2): name = name.rstrip("s") result.append("{} {}".format(round(value), name)) return ", ".join(result[:granularity]) + + +async def get_mining_stat(stat_slug: str, gerty): + text = [] + if stat_slug == "mining_current_hash_rate": + stat = await api_get_mining_stat(stat_slug, gerty) + logger.debug(stat) + current = "{0}hash".format(si_format(stat['current'], 6, True, " ")) + text.append(get_text_item_dict("Current Mining Hashrate", 20)) + text.append(get_text_item_dict(current, 40)) + # compare vs previous time period + difference = get_percent_difference(current=stat['current'], previous=stat['1w']) + text.append(get_text_item_dict("{0} in last 7 days".format(difference), 12)) + elif stat_slug == "mining_current_difficulty": + stat = await api_get_mining_stat(stat_slug, gerty) + text.append(get_text_item_dict("Current Mining Difficulty", 20)) + text.append(get_text_item_dict(format_number(stat['current']), 40)) + difference = get_percent_difference(current=stat['current'], previous=stat['previous']) + text.append(get_text_item_dict("{0} since last adjustment".format(difference), 12)) + # text.append(get_text_item_dict("Required threshold for mining proof-of-work", 12)) + return text + +async def api_get_mining_stat(stat_slug: str, gerty): + stat = "" + if stat_slug == "mining_current_hash_rate": + async with httpx.AsyncClient() as client: + r = await client.get(gerty.mempool_endpoint + "/api/v1/mining/hashrate/1m") + data = r.json() + stat = {} + stat['current'] = data['currentHashrate'] + stat['1w'] = data['hashrates'][len(data['hashrates']) - 7]['avgHashrate'] + elif stat_slug == "mining_current_difficulty": + async with httpx.AsyncClient() as client: + r = await client.get(gerty.mempool_endpoint + "/api/v1/mining/hashrate/1m") + data = r.json() + stat = {} + stat['current'] = data['currentDifficulty'] + stat['previous'] = data['difficulty'][len(data['difficulty']) - 2]['difficulty'] + return stat \ No newline at end of file diff --git a/lnbits/extensions/gerty/templates/gerty/index.html b/lnbits/extensions/gerty/templates/gerty/index.html index 5d67f46e..55e67a2d 100644 --- a/lnbits/extensions/gerty/templates/gerty/index.html +++ b/lnbits/extensions/gerty/templates/gerty/index.html @@ -1,639 +1,781 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} -
-
- - - New Gerty - - - - - -
-
-
Gerty
-
-
- Export to CSV -
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block page %} +
+
+ + + New Gerty + + + + + +
+
+
Gerty
+
+
+ Export to CSV +
+
+ + {% raw %} + + + + {% endraw %} + +
+
- - {% raw %} - - - {% endraw %} - - - -
- -
- - -
- {{ SITE_TITLE }} Gerty extension -
-
- - - {% include "gerty/_api_docs.html" %} - -
-
- - - - - - - - Hit enter to add values - - - - Used for getting onchain/ln stats - - - - The amount of time in seconds between screen updates - - - - Enter a UTC time offset value (e.g. -1) - - -

Use the toggles below to control what your Gerty will display

- - - - - Displays random quotes from Satoshi - - - - - - - - - - - -
- Create Gerty - - Update Gerty - - Cancel - +
+ + +
+ {{ SITE_TITLE }} Gerty extension +
+
+ + + {% include "gerty/_api_docs.html" %} + +
- - - -
+ + + + + + + + Hit enter to add values + + + + Used for getting onchain/ln stats + + + + The amount of time in seconds between screen updates + + + + + Enter a UTC time offset value (e.g. -1) + + +

Use the toggles below to control what your Gerty will display

+ + + + + + Displays random quotes from Satoshi + + + + + + + + + + + + + + + + + + + + + + + Toggle all + +
+ + + + + + + +
+ + + Toggle all + +
+ + + + +
+ + + + Toggle all + +
+ + + +
+ +
+ + +
+ Create Gerty + + Update Gerty + + Cancel + +
+
+
+
+
{% endblock %} {% block scripts %} {{ window_vars(user) }} - + LNbits.utils + .confirmDialog('Are you sure you want to delete this Gerty?') + .onOk(function () { + LNbits.api + .request( + 'DELETE', + '/gerty/api/v1/gerty/' + gertyId, + _.findWhere(self.g.user.wallets, {id: gerty.wallet}).adminkey + ) + .then(function (response) { + self.gertys = _.reject(self.gertys, function (obj) { + return obj.id == gertyId + }) + }) + .catch(function (error) { + LNbits.utils.notifyApiError(error) + }) + }) + }, + exportCSV: function () { + LNbits.utils.exportCSV(this.gertysTable.columns, this.gertys) + } + }, + created: function () { + if (this.g.user.wallets.length) { + this.getGertys() + } + }, + watch: { + toggleStates: { + handler(toggleStatesValue) { + // Switch all the toggles in each section to the relevant state + for (const [toggleKey, toggleValue] of Object.entries(toggleStatesValue)) { + if (this.oldToggleStates[toggleKey] !== toggleValue) { + for (const [dpKey, dpValue] of Object.entries(this.formDialog.data.display_preferences)) { + if (dpKey.indexOf(toggleKey) === 0) { + this.formDialog.data.display_preferences[dpKey] = toggleValue + } + } + } + } + // This is a weird hack we have to use to get VueJS to persist the previous toggle state between + // watches. VueJS passes the old and new values by reference so when comparing objects they + // will have the same values unless we do this + this.oldToggleStates = JSON.parse(JSON.stringify(toggleStatesValue)) + }, + deep: true + } + } + }) + {% endblock %} {% block styles %} - + {% endblock %} diff --git a/lnbits/extensions/gerty/views_api.py b/lnbits/extensions/gerty/views_api.py index 84a9b967..b400f3ec 100644 --- a/lnbits/extensions/gerty/views_api.py +++ b/lnbits/extensions/gerty/views_api.py @@ -173,16 +173,37 @@ async def get_screen_data(screen_num: int, screens_list: dict, gerty): if screen_slug == "dashboard": title = gerty.name areas = await get_dashboard(gerty) + if screen_slug == "lnbits_wallets_balance": + wallets = await get_lnbits_wallet_balances(gerty) + text = [] + for wallet in wallets: + text.append(get_text_item_dict("{0}'s Wallet".format(wallet['name']), 20)) + text.append(get_text_item_dict("{0} sats".format(format_number(wallet['balance'])), 40)) + areas.append(text) elif screen_slug == "fun_satoshi_quotes": areas.append(await get_satoshi_quotes()) elif screen_slug == "fun_exchange_market_rate": areas.append(await get_exchange_rate(gerty)) - elif screen_slug == "onchain_dashboard": + elif screen_slug == "onchain_difficulty_epoch_progress": + areas.append(await get_onchain_stat(screen_slug, gerty)) + elif screen_slug == "onchain_difficulty_retarget_date": + areas.append(await get_onchain_stat(screen_slug, gerty)) + elif screen_slug == "onchain_difficulty_blocks_remaining": + areas.append(await get_onchain_stat(screen_slug, gerty)) + elif screen_slug == "onchain_difficulty_epoch_time_remaining": + areas.append(await get_onchain_stat(screen_slug, gerty)) + elif screen_slug == "dashboard_onchain": title = "Onchain Data" areas = await get_onchain_dashboard(gerty) elif screen_slug == "mempool_recommended_fees": areas.append(await get_mempool_stat(screen_slug, gerty)) - elif screen_slug == "mining_dashboard": + elif screen_slug == "mempool_tx_count": + areas.append(await get_mempool_stat(screen_slug, gerty)) + elif screen_slug == "mining_current_hash_rate": + areas.append(await get_mining_stat(screen_slug, gerty)) + elif screen_slug == "mining_current_difficulty": + areas.append(await get_mining_stat(screen_slug, gerty)) + elif screen_slug == "dashboard_mining": title = "Mining Data" areas = await get_mining_dashboard(gerty) elif screen_slug == "lightning_dashboard": @@ -292,6 +313,34 @@ async def get_exchange_rate(gerty): pass return text +async def get_onchain_stat(stat_slug: str, gerty): + text = [] + if ( + stat_slug == "onchain_difficulty_epoch_progress" or + stat_slug == "onchain_difficulty_retarget_date" or + stat_slug == "onchain_difficulty_blocks_remaining" or + stat_slug == "onchain_difficulty_epoch_time_remaining" + ): + async with httpx.AsyncClient() as client: + r = await client.get(gerty.mempool_endpoint + "/api/v1/difficulty-adjustment") + if stat_slug == "onchain_difficulty_epoch_progress": + stat = round(r.json()['progressPercent']) + text.append(get_text_item_dict("Progress through current difficulty epoch", 15)) + text.append(get_text_item_dict("{0}%".format(stat), 80)) + elif stat_slug == "onchain_difficulty_retarget_date": + stat = r.json()['estimatedRetargetDate'] + dt = datetime.fromtimestamp(stat / 1000).strftime("%e %b %Y at %H:%M") + text.append(get_text_item_dict("Estimated date of next difficulty adjustment", 15)) + text.append(get_text_item_dict(dt, 40)) + elif stat_slug == "onchain_difficulty_blocks_remaining": + stat = r.json()['remainingBlocks'] + text.append(get_text_item_dict("Blocks remaining until next difficulty adjustment", 15)) + text.append(get_text_item_dict("{0}".format(format_number(stat)), 80)) + elif stat_slug == "onchain_difficulty_epoch_time_remaining": + stat = r.json()['remainingTime'] + text.append(get_text_item_dict("Blocks remaining until next difficulty adjustment", 15)) + text.append(get_text_item_dict(get_time_remaining(stat / 1000, 4), 20)) + return text async def get_onchain_dashboard(gerty): areas = [] @@ -440,3 +489,30 @@ async def get_mempool_stat(stat_slug: str, gerty): ) ) return text + + +def get_date_suffix(dayNumber): + if 4 <= dayNumber <= 20 or 24 <= dayNumber <= 30: + return "th" + else: + return ["st", "nd", "rd"][dayNumber % 10 - 1] + +def get_time_remaining(seconds, granularity=2): + intervals = ( + # ('weeks', 604800), # 60 * 60 * 24 * 7 + ('days', 86400), # 60 * 60 * 24 + ('hours', 3600), # 60 * 60 + ('minutes', 60), + ('seconds', 1), + ) + + result = [] + + for name, count in intervals: + value = seconds // count + if value: + seconds -= value * count + if value == 1: + name = name.rstrip('s') + result.append("{} {}".format(round(value), name)) + return ', '.join(result[:granularity])